<commit_before>#include <bench/BenchTimer.h>
#include "main.h"
#include <Eigen/MPRealSupport>
#include <Eigen/LU>
#include <Eigen/Eigenvalues>
#include <sstream>

using namespace mpfr;
using namespace Eigen;

void test_mpreal_support()
{
  // set precision to 256 bits (double has only 53 bits)
  mpreal::set_default_prec(256);
  typedef Matrix<mpreal,Eigen::Dynamic,Eigen::Dynamic> MatrixXmp;

  std::cerr << "epsilon =         " << NumTraits<mpreal>::epsilon() << "\n";
  std::cerr << "dummy_precision = " << NumTraits<mpreal>::dummy_precision() << "\n";
  std::cerr << "highest =         " << NumTraits<mpreal>::highest() << "\n";
  std::cerr << "lowest =          " << NumTraits<mpreal>::lowest() << "\n";

  for(int i = 0; i < g_repeat; i++) {
    int s = Eigen::internal::random<int>(1,100);
    MatrixXmp A = MatrixXmp::Random(s,s);
    MatrixXmp B = MatrixXmp::Random(s,s);
    MatrixXmp S = A.adjoint() * A;
    MatrixXmp X;

    // Basic stuffs
    VERIFY_IS_APPROX(A.real(), A);
    VERIFY(Eigen::internal::isApprox(A.array().abs2().sum(), A.squaredNorm()));
    VERIFY_IS_APPROX(A.array().exp(),         exp(A.array()));
    VERIFY_IS_APPROX(A.array().abs2().sqrt(), A.array().abs());
    VERIFY_IS_APPROX(A.array().sin(),         sin(A.array()));
    VERIFY_IS_APPROX(A.array().cos(),         cos(A.array()));

    // Cholesky
    X = S.selfadjointView<Lower>().llt().solve(B);
    VERIFY_IS_APPROX((S.selfadjointView<Lower>()*X).eval(),B);

    // partial LU
    X = A.lu().solve(B);
    VERIFY_IS_APPROX((A*X).eval(),B);

    // symmetric eigenvalues
    SelfAdjointEigenSolver<MatrixXmp> eig(S);
    VERIFY_IS_EQUAL(eig.info(), Success);
    VERIFY( (S.selfadjointView<Lower>() * eig.eigenvectors()).isApprox(eig.eigenvectors() * eig.eigenvalues().asDiagonal(), NumTraits<mpreal>::dummy_precision()*1e3) );
  }

  {
    MatrixXmp A(8,3); A.setRandom();
    // test output (interesting things happen in this code)
    std::stringstream stream;
    stream << A;
  }
}
<commit_msg>Remove unnecessary <bench/BenchTimer.h>include<commit_after>#include "main.h"
#include <Eigen/MPRealSupport>
#include <Eigen/LU>
#include <Eigen/Eigenvalues>
#include <sstream>

using namespace mpfr;
using namespace Eigen;

void test_mpreal_support()
{
  // set precision to 256 bits (double has only 53 bits)
  mpreal::set_default_prec(256);
  typedef Matrix<mpreal,Eigen::Dynamic,Eigen::Dynamic> MatrixXmp;

  std::cerr << "epsilon =         " << NumTraits<mpreal>::epsilon() << "\n";
  std::cerr << "dummy_precision = " << NumTraits<mpreal>::dummy_precision() << "\n";
  std::cerr << "highest =         " << NumTraits<mpreal>::highest() << "\n";
  std::cerr << "lowest =          " << NumTraits<mpreal>::lowest() << "\n";

  for(int i = 0; i < g_repeat; i++) {
    int s = Eigen::internal::random<int>(1,100);
    MatrixXmp A = MatrixXmp::Random(s,s);
    MatrixXmp B = MatrixXmp::Random(s,s);
    MatrixXmp S = A.adjoint() * A;
    MatrixXmp X;

    // Basic stuffs
    VERIFY_IS_APPROX(A.real(), A);
    VERIFY(Eigen::internal::isApprox(A.array().abs2().sum(), A.squaredNorm()));
    VERIFY_IS_APPROX(A.array().exp(),         exp(A.array()));
    VERIFY_IS_APPROX(A.array().abs2().sqrt(), A.array().abs());
    VERIFY_IS_APPROX(A.array().sin(),         sin(A.array()));
    VERIFY_IS_APPROX(A.array().cos(),         cos(A.array()));

    // Cholesky
    X = S.selfadjointView<Lower>().llt().solve(B);
    VERIFY_IS_APPROX((S.selfadjointView<Lower>()*X).eval(),B);

    // partial LU
    X = A.lu().solve(B);
    VERIFY_IS_APPROX((A*X).eval(),B);

    // symmetric eigenvalues
    SelfAdjointEigenSolver<MatrixXmp> eig(S);
    VERIFY_IS_EQUAL(eig.info(), Success);
    VERIFY( (S.selfadjointView<Lower>() * eig.eigenvectors()).isApprox(eig.eigenvectors() * eig.eigenvalues().asDiagonal(), NumTraits<mpreal>::dummy_precision()*1e3) );
  }

  {
    MatrixXmp A(8,3); A.setRandom();
    // test output (interesting things happen in this code)
    std::stringstream stream;
    stream << A;
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 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/toolbar_star_toggle_gtk.h"

#include "app/gtk_dnd_util.h"
#include "app/resource_bundle.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/gtk/bookmark_bubble_gtk.h"
#include "chrome/browser/gtk/browser_toolbar_gtk.h"
#include "chrome/browser/gtk/gtk_chrome_button.h"
#include "chrome/browser/gtk/gtk_theme_provider.h"
#include "chrome/browser/gtk/gtk_util.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/notification_service.h"
#include "gfx/rect.h"
#include "grit/theme_resources.h"

ToolbarStarToggleGtk::ToolbarStarToggleGtk(BrowserToolbarGtk* host)
    : host_(host),
      widget_(gtk_chrome_button_new()),
      is_starred_(false),
      theme_provider_(GtkThemeProvider::GetFrom(host->profile())),
      unstarred_(theme_provider_, IDR_STAR, IDR_STAR_P, IDR_STAR_H, IDR_STAR_D,
                 IDR_STAR_MASK),
      starred_(theme_provider_, IDR_STARRED, IDR_STARRED_P, IDR_STARRED_H, 0,
               IDR_STAR_MASK) {
  gtk_widget_set_size_request(widget_.get(), unstarred_.Width(),
                              unstarred_.Height());

  gtk_widget_set_app_paintable(widget_.get(), TRUE);
  // We effectively double-buffer by virtue of having only one image...
  gtk_widget_set_double_buffered(widget_.get(), FALSE);

  g_signal_connect(widget(), "expose-event",
                   G_CALLBACK(OnExpose), this);
  GTK_WIDGET_UNSET_FLAGS(widget(), GTK_CAN_FOCUS);

  gtk_drag_source_set(widget(), GDK_BUTTON1_MASK, NULL, 0,
      static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_LINK));
  gtk_dnd_util::SetSourceTargetListFromCodeMask(widget(),
                                              gtk_dnd_util::TEXT_PLAIN |
                                              gtk_dnd_util::TEXT_URI_LIST |
                                              gtk_dnd_util::CHROME_NAMED_URL |
                                              gtk_dnd_util::NETSCAPE_URL);
  g_signal_connect(widget(), "drag-data-get", G_CALLBACK(OnDragDataGet), this);

  theme_provider_->InitThemesFor(this);
  registrar_.Add(this,
                 NotificationType::BROWSER_THEME_CHANGED,
                 NotificationService::AllSources());

  hover_controller_.Init(widget());
}

ToolbarStarToggleGtk::~ToolbarStarToggleGtk() {
  widget_.Destroy();
}

void ToolbarStarToggleGtk::Observe(NotificationType type,
    const NotificationSource& source, const NotificationDetails& details) {
  DCHECK(NotificationType::BROWSER_THEME_CHANGED == type);

  GtkThemeProvider* provider = static_cast<GtkThemeProvider*>(
      Source<GtkThemeProvider>(source).ptr());
  DCHECK(provider == theme_provider_);
  UpdateGTKButton();
}

void ToolbarStarToggleGtk::ShowStarBubble(const GURL& url,
                                          bool newly_bookmarked) {
  GtkWidget* widget = widget_.get();
  BookmarkBubbleGtk::Show(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
                          gtk_util::GetWidgetRectRelativeToToplevel(widget),
                          host_->profile(),
                          url,
                          newly_bookmarked);
}

void ToolbarStarToggleGtk::SetStarred(bool starred) {
  is_starred_ = starred;
  gtk_widget_queue_draw(widget_.get());
  UpdateGTKButton();
}

// static
gboolean ToolbarStarToggleGtk::OnExpose(GtkWidget* widget, GdkEventExpose* e,
                                        ToolbarStarToggleGtk* button) {
  if (button->theme_provider_->UseGtkTheme()) {
    return FALSE;
  } else {
    double hover_state = button->hover_controller_.GetCurrentValue();
    if (button->is_starred_)
      return button->starred_.OnExpose(widget, e, hover_state);
    else
      return button->unstarred_.OnExpose(widget, e, hover_state);
  }
}

// static
void ToolbarStarToggleGtk::OnDragDataGet(GtkWidget* widget,
    GdkDragContext* drag_context, GtkSelectionData* data, guint info,
    guint time, ToolbarStarToggleGtk* star) {
  const TabContents* tab = star->host_->browser()->tabstrip_model()->
      GetSelectedTabContents();
  if (!tab)
    return;
  gtk_dnd_util::WriteURLWithName(data, tab->GetURL(), tab->GetTitle(), info);
}

void ToolbarStarToggleGtk::UpdateGTKButton() {
  bool use_gtk = theme_provider_ && theme_provider_->UseGtkTheme();

  if (use_gtk) {
    GdkPixbuf* pixbuf = NULL;
    if (is_starred_) {
      pixbuf = theme_provider_->GetPixbufNamed(IDR_STARRED_NOBORDER_CENTER);
    } else {
      pixbuf = theme_provider_->GetPixbufNamed(IDR_STAR_NOBORDER_CENTER);
    }

    gtk_button_set_image(
        GTK_BUTTON(widget_.get()),
        gtk_image_new_from_pixbuf(pixbuf));

    gtk_widget_set_size_request(widget_.get(), -1, -1);
    gtk_widget_set_app_paintable(widget_.get(), FALSE);
    gtk_widget_set_double_buffered(widget_.get(), TRUE);
  } else {
    gtk_widget_set_size_request(widget_.get(), unstarred_.Width(),
                                unstarred_.Height());

    gtk_widget_set_app_paintable(widget_.get(), TRUE);
    // We effectively double-buffer by virtue of having only one image...
    gtk_widget_set_double_buffered(widget_.get(), FALSE);
  }

  gtk_chrome_button_set_use_gtk_rendering(
      GTK_CHROME_BUTTON(widget_.get()), use_gtk);
}
<commit_msg>GTK fix merge failure in uber-revert.<commit_after>// Copyright (c) 2010 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/toolbar_star_toggle_gtk.h"

#include "app/gtk_dnd_util.h"
#include "app/resource_bundle.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/gtk/bookmark_bubble_gtk.h"
#include "chrome/browser/gtk/browser_toolbar_gtk.h"
#include "chrome/browser/gtk/gtk_chrome_button.h"
#include "chrome/browser/gtk/gtk_theme_provider.h"
#include "chrome/browser/gtk/gtk_util.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/notification_service.h"
#include "gfx/rect.h"
#include "grit/theme_resources.h"

ToolbarStarToggleGtk::ToolbarStarToggleGtk(BrowserToolbarGtk* host)
    : host_(host),
      widget_(gtk_chrome_button_new()),
      is_starred_(false),
      theme_provider_(GtkThemeProvider::GetFrom(host->profile())),
      unstarred_(theme_provider_, IDR_STAR, IDR_STAR_P, IDR_STAR_H, IDR_STAR_D,
                 IDR_STAR_MASK),
      starred_(theme_provider_, IDR_STARRED, IDR_STARRED_P, IDR_STARRED_H, 0,
               IDR_STAR_MASK) {
  gtk_widget_set_size_request(widget_.get(), unstarred_.Width(),
                              unstarred_.Height());

  gtk_widget_set_app_paintable(widget_.get(), TRUE);
  // We effectively double-buffer by virtue of having only one image...
  gtk_widget_set_double_buffered(widget_.get(), FALSE);

  g_signal_connect(widget(), "expose-event",
                   G_CALLBACK(OnExpose), this);
  GTK_WIDGET_UNSET_FLAGS(widget(), GTK_CAN_FOCUS);

  gtk_drag_source_set(widget(), GDK_BUTTON1_MASK, NULL, 0,
      static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_LINK));
  gtk_dnd_util::SetSourceTargetListFromCodeMask(widget(),
                                              gtk_dnd_util::TEXT_PLAIN |
                                              gtk_dnd_util::TEXT_URI_LIST |
                                              gtk_dnd_util::CHROME_NAMED_URL |
                                              gtk_dnd_util::NETSCAPE_URL);
  g_signal_connect(widget(), "drag-data-get", G_CALLBACK(OnDragDataGet), this);

  theme_provider_->InitThemesFor(this);
  registrar_.Add(this,
                 NotificationType::BROWSER_THEME_CHANGED,
                 NotificationService::AllSources());

  hover_controller_.Init(widget());
}

ToolbarStarToggleGtk::~ToolbarStarToggleGtk() {
  widget_.Destroy();
}

void ToolbarStarToggleGtk::Observe(NotificationType type,
    const NotificationSource& source, const NotificationDetails& details) {
  DCHECK(NotificationType::BROWSER_THEME_CHANGED == type);

  GtkThemeProvider* provider = static_cast<GtkThemeProvider*>(
      Source<GtkThemeProvider>(source).ptr());
  DCHECK(provider == theme_provider_);
  UpdateGTKButton();
}

void ToolbarStarToggleGtk::ShowStarBubble(const GURL& url,
                                          bool newly_bookmarked) {
  GtkWidget* widget = widget_.get();
  BookmarkBubbleGtk::Show(widget,
                          host_->profile(),
                          url,
                          newly_bookmarked);
}

void ToolbarStarToggleGtk::SetStarred(bool starred) {
  is_starred_ = starred;
  gtk_widget_queue_draw(widget_.get());
  UpdateGTKButton();
}

// static
gboolean ToolbarStarToggleGtk::OnExpose(GtkWidget* widget, GdkEventExpose* e,
                                        ToolbarStarToggleGtk* button) {
  if (button->theme_provider_->UseGtkTheme()) {
    return FALSE;
  } else {
    double hover_state = button->hover_controller_.GetCurrentValue();
    if (button->is_starred_)
      return button->starred_.OnExpose(widget, e, hover_state);
    else
      return button->unstarred_.OnExpose(widget, e, hover_state);
  }
}

// static
void ToolbarStarToggleGtk::OnDragDataGet(GtkWidget* widget,
    GdkDragContext* drag_context, GtkSelectionData* data, guint info,
    guint time, ToolbarStarToggleGtk* star) {
  const TabContents* tab = star->host_->browser()->tabstrip_model()->
      GetSelectedTabContents();
  if (!tab)
    return;
  gtk_dnd_util::WriteURLWithName(data, tab->GetURL(), tab->GetTitle(), info);
}

void ToolbarStarToggleGtk::UpdateGTKButton() {
  bool use_gtk = theme_provider_ && theme_provider_->UseGtkTheme();

  if (use_gtk) {
    GdkPixbuf* pixbuf = NULL;
    if (is_starred_) {
      pixbuf = theme_provider_->GetPixbufNamed(IDR_STARRED_NOBORDER_CENTER);
    } else {
      pixbuf = theme_provider_->GetPixbufNamed(IDR_STAR_NOBORDER_CENTER);
    }

    gtk_button_set_image(
        GTK_BUTTON(widget_.get()),
        gtk_image_new_from_pixbuf(pixbuf));

    gtk_widget_set_size_request(widget_.get(), -1, -1);
    gtk_widget_set_app_paintable(widget_.get(), FALSE);
    gtk_widget_set_double_buffered(widget_.get(), TRUE);
  } else {
    gtk_widget_set_size_request(widget_.get(), unstarred_.Width(),
                                unstarred_.Height());

    gtk_widget_set_app_paintable(widget_.get(), TRUE);
    // We effectively double-buffer by virtue of having only one image...
    gtk_widget_set_double_buffered(widget_.get(), FALSE);
  }

  gtk_chrome_button_set_use_gtk_rendering(
      GTK_CHROME_BUTTON(widget_.get()), use_gtk);
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 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 <vector>

#include "base/message_loop.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/history/history.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "googleurl/src/gurl.h"

namespace {

// Note: WaitableEvent is not used for synchronization between the main thread
// and history backend thread because the history subsystem posts tasks back
// to the main thread. Had we tried to Signal an event in such a task
// and Wait for it on the main thread, the task would not run at all because
// the main thread would be blocked on the Wait call, resulting in a deadlock.

// A task to be scheduled on the history backend thread.
// Notifies the main thread after all history backend thread tasks have run.
class WaitForHistoryTask : public HistoryDBTask {
 public:
  WaitForHistoryTask() {
  }

  virtual bool RunOnDBThread(history::HistoryBackend* backend,
                             history::HistoryDatabase* db) {
    return true;
  }

  virtual void DoneRunOnMainThread() {
    MessageLoop::current()->Quit();
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(WaitForHistoryTask);
};

// Enumerates all history contents on the backend thread.
class HistoryEnumerator : public HistoryService::URLEnumerator {
 public:
  explicit HistoryEnumerator(HistoryService* history) {
    EXPECT_TRUE(history);
    if (!history)
      return;

    BrowserThread::PostTask(
        BrowserThread::UI,
        FROM_HERE,
        NewRunnableMethod(history, &HistoryService::IterateURLs, this));
    ui_test_utils::RunMessageLoop();
  }

  virtual void OnURL(const GURL& url) {
    urls_.push_back(url);
  }

  virtual void OnComplete(bool success) {
    BrowserThread::PostTask(
        BrowserThread::UI,
        FROM_HERE,
        new MessageLoop::QuitTask());
  }

  std::vector<GURL>& urls() { return urls_; }

 private:
  std::vector<GURL> urls_;

  DISALLOW_COPY_AND_ASSIGN(HistoryEnumerator);
};

class HistoryBrowserTest : public InProcessBrowserTest {
 protected:
  PrefService* GetPrefs() {
    return GetProfile()->GetPrefs();
  }

  Profile* GetProfile() {
    return browser()->GetProfile();
  }

  HistoryService* GetHistoryService() {
    return GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
  }

  std::vector<GURL> GetHistoryContents() {
    HistoryEnumerator enumerator(GetHistoryService());
    return enumerator.urls();
  }

  GURL GetTestUrl() {
    return ui_test_utils::GetTestUrl(
        FilePath(FilePath::kCurrentDirectory),
        FilePath(FILE_PATH_LITERAL("title2.html")));
  }

  void WaitForHistoryBackendToRun() {
    CancelableRequestConsumerTSimple<int> request_consumer;
    scoped_refptr<HistoryDBTask> task(new WaitForHistoryTask());
    HistoryService* history = GetHistoryService();
    BrowserThread::PostTask(BrowserThread::UI,
                            FROM_HERE,
                            NewRunnableMethod(history,
                                              &HistoryService::ScheduleDBTask,
                                              task,
                                              &request_consumer));
    ui_test_utils::RunMessageLoop();
  }

  void ExpectEmptyHistory() {
    std::vector<GURL> urls(GetHistoryContents());
    EXPECT_EQ(0U, urls.size());
  }
};

// Test that the browser history is saved (default setting).
IN_PROC_BROWSER_TEST_F(HistoryBrowserTest, SavingHistoryEnabled) {
  EXPECT_FALSE(GetPrefs()->GetBoolean(prefs::kSavingBrowserHistoryDisabled));

  EXPECT_TRUE(GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS));
  EXPECT_TRUE(GetProfile()->GetHistoryService(Profile::IMPLICIT_ACCESS));

  ui_test_utils::WaitForHistoryToLoad(browser());
  ExpectEmptyHistory();

  ui_test_utils::NavigateToURL(browser(), GetTestUrl());
  WaitForHistoryBackendToRun();

  {
    std::vector<GURL> urls(GetHistoryContents());
    ASSERT_EQ(1U, urls.size());
    EXPECT_EQ(GetTestUrl().spec(), urls[0].spec());
  }
}

// Times out on Vista only.  http://crbug.com/57994
#if defined(OS_WIN)
#define MAYBE_SavingHistoryDisabled DISABLED_SavingHistoryDisabled
#else
#define MAYBE_SavingHistoryDisabled SavingHistoryDisabled
#endif

// Test that disabling saving browser history really works.
IN_PROC_BROWSER_TEST_F(HistoryBrowserTest, MAYBE_SavingHistoryDisabled) {
  GetPrefs()->SetBoolean(prefs::kSavingBrowserHistoryDisabled, true);

  EXPECT_TRUE(GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS));
  EXPECT_FALSE(GetProfile()->GetHistoryService(Profile::IMPLICIT_ACCESS));

  ui_test_utils::WaitForHistoryToLoad(browser());
  ExpectEmptyHistory();

  ui_test_utils::NavigateToURL(browser(), GetTestUrl());
  WaitForHistoryBackendToRun();
  ExpectEmptyHistory();
}

// Times out on Vista only.  http://crbug.com/57994
#if defined(OS_WIN)
#define MAYBE_SavingHistoryEnabledThenDisabled \
    DISABLED_SavingHistoryEnabledThenDisabled
#else
#define MAYBE_SavingHistoryEnabledThenDisabled SavingHistoryEnabledThenDisabled
#endif

// Test that changing the pref takes effect immediately
// when the browser is running.
IN_PROC_BROWSER_TEST_F(HistoryBrowserTest,
    MAYBE_SavingHistoryEnabledThenDisabled) {
  EXPECT_FALSE(GetPrefs()->GetBoolean(prefs::kSavingBrowserHistoryDisabled));

  ui_test_utils::WaitForHistoryToLoad(browser());

  ui_test_utils::NavigateToURL(browser(), GetTestUrl());
  WaitForHistoryBackendToRun();

  {
    std::vector<GURL> urls(GetHistoryContents());
    ASSERT_EQ(1U, urls.size());
    EXPECT_EQ(GetTestUrl().spec(), urls[0].spec());
  }

  GetPrefs()->SetBoolean(prefs::kSavingBrowserHistoryDisabled, true);

  ui_test_utils::NavigateToURL(browser(), GetTestUrl());
  WaitForHistoryBackendToRun();

  {
    // No additional entries should be present in the history.
    std::vector<GURL> urls(GetHistoryContents());
    ASSERT_EQ(1U, urls.size());
    EXPECT_EQ(GetTestUrl().spec(), urls[0].spec());
  }
}

// Test that changing the pref takes effect immediately
// when the browser is running.
IN_PROC_BROWSER_TEST_F(HistoryBrowserTest, SavingHistoryDisabledThenEnabled) {
  GetPrefs()->SetBoolean(prefs::kSavingBrowserHistoryDisabled, true);

  ui_test_utils::WaitForHistoryToLoad(browser());
  ExpectEmptyHistory();

  ui_test_utils::NavigateToURL(browser(), GetTestUrl());
  WaitForHistoryBackendToRun();
  ExpectEmptyHistory();

  GetPrefs()->SetBoolean(prefs::kSavingBrowserHistoryDisabled, false);

  ui_test_utils::NavigateToURL(browser(), GetTestUrl());
  WaitForHistoryBackendToRun();

  {
    std::vector<GURL> urls(GetHistoryContents());
    ASSERT_EQ(1U, urls.size());
    EXPECT_EQ(GetTestUrl().spec(), urls[0].spec());
  }
}

}  // namespace
<commit_msg>Re-enable HistoryBrowserTest.SavingHistoryDisabled and SavingHistoryDisabledThenEnabled<commit_after>// Copyright (c) 2010 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 <vector>

#include "base/message_loop.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/history/history.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "googleurl/src/gurl.h"

namespace {

// Helper to debug intermittent test hangs/timeouts.
// TODO(phajdan.jr): remove when http://crbug.com/57994 is fixed.
void Checkpoint(const char* message, const base::TimeTicks& start_time) {
  LOG(INFO) << message << " : "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;
}

// Note: WaitableEvent is not used for synchronization between the main thread
// and history backend thread because the history subsystem posts tasks back
// to the main thread. Had we tried to Signal an event in such a task
// and Wait for it on the main thread, the task would not run at all because
// the main thread would be blocked on the Wait call, resulting in a deadlock.

// A task to be scheduled on the history backend thread.
// Notifies the main thread after all history backend thread tasks have run.
class WaitForHistoryTask : public HistoryDBTask {
 public:
  WaitForHistoryTask() {
  }

  virtual bool RunOnDBThread(history::HistoryBackend* backend,
                             history::HistoryDatabase* db) {
    return true;
  }

  virtual void DoneRunOnMainThread() {
    MessageLoop::current()->Quit();
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(WaitForHistoryTask);
};

// Enumerates all history contents on the backend thread.
class HistoryEnumerator : public HistoryService::URLEnumerator {
 public:
  explicit HistoryEnumerator(HistoryService* history) {
    EXPECT_TRUE(history);
    if (!history)
      return;

    BrowserThread::PostTask(
        BrowserThread::UI,
        FROM_HERE,
        NewRunnableMethod(history, &HistoryService::IterateURLs, this));
    ui_test_utils::RunMessageLoop();
  }

  virtual void OnURL(const GURL& url) {
    urls_.push_back(url);
  }

  virtual void OnComplete(bool success) {
    BrowserThread::PostTask(
        BrowserThread::UI,
        FROM_HERE,
        new MessageLoop::QuitTask());
  }

  std::vector<GURL>& urls() { return urls_; }

 private:
  std::vector<GURL> urls_;

  DISALLOW_COPY_AND_ASSIGN(HistoryEnumerator);
};

class HistoryBrowserTest : public InProcessBrowserTest {
 protected:
  PrefService* GetPrefs() {
    return GetProfile()->GetPrefs();
  }

  Profile* GetProfile() {
    return browser()->GetProfile();
  }

  HistoryService* GetHistoryService() {
    return GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
  }

  std::vector<GURL> GetHistoryContents() {
    HistoryEnumerator enumerator(GetHistoryService());
    return enumerator.urls();
  }

  GURL GetTestUrl() {
    return ui_test_utils::GetTestUrl(
        FilePath(FilePath::kCurrentDirectory),
        FilePath(FILE_PATH_LITERAL("title2.html")));
  }

  void WaitForHistoryBackendToRun() {
    CancelableRequestConsumerTSimple<int> request_consumer;
    scoped_refptr<HistoryDBTask> task(new WaitForHistoryTask());
    HistoryService* history = GetHistoryService();
    BrowserThread::PostTask(BrowserThread::UI,
                            FROM_HERE,
                            NewRunnableMethod(history,
                                              &HistoryService::ScheduleDBTask,
                                              task,
                                              &request_consumer));
    ui_test_utils::RunMessageLoop();
  }

  void ExpectEmptyHistory() {
    std::vector<GURL> urls(GetHistoryContents());
    EXPECT_EQ(0U, urls.size());
  }
};

// Test that the browser history is saved (default setting).
IN_PROC_BROWSER_TEST_F(HistoryBrowserTest, SavingHistoryEnabled) {
  EXPECT_FALSE(GetPrefs()->GetBoolean(prefs::kSavingBrowserHistoryDisabled));

  EXPECT_TRUE(GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS));
  EXPECT_TRUE(GetProfile()->GetHistoryService(Profile::IMPLICIT_ACCESS));

  ui_test_utils::WaitForHistoryToLoad(browser());
  ExpectEmptyHistory();

  ui_test_utils::NavigateToURL(browser(), GetTestUrl());
  WaitForHistoryBackendToRun();

  {
    std::vector<GURL> urls(GetHistoryContents());
    ASSERT_EQ(1U, urls.size());
    EXPECT_EQ(GetTestUrl().spec(), urls[0].spec());
  }
}

// Test that disabling saving browser history really works.
// TODO(phajdan.jr): remove debug code when http://crbug.com/57994 is fixed.
IN_PROC_BROWSER_TEST_F(HistoryBrowserTest, SavingHistoryDisabled) {
  base::TimeTicks start_time = base::TimeTicks::Now();

  GetPrefs()->SetBoolean(prefs::kSavingBrowserHistoryDisabled, true);

  EXPECT_TRUE(GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS));
  EXPECT_FALSE(GetProfile()->GetHistoryService(Profile::IMPLICIT_ACCESS));

  Checkpoint("Before waiting for history to load", start_time);
  ui_test_utils::WaitForHistoryToLoad(browser());
  Checkpoint("After waiting for history to load", start_time);
  ExpectEmptyHistory();
  Checkpoint("After checking history", start_time);

  ui_test_utils::NavigateToURL(browser(), GetTestUrl());
  Checkpoint("After NavigateToURL", start_time);
  WaitForHistoryBackendToRun();
  Checkpoint("After waiting for history backend to run", start_time);
  ExpectEmptyHistory();
  Checkpoint("After second check", start_time);
}

// Test that changing the pref takes effect immediately
// when the browser is running.
// TODO(phajdan.jr): remove debug code when http://crbug.com/57994 is fixed.
IN_PROC_BROWSER_TEST_F(HistoryBrowserTest, SavingHistoryEnabledThenDisabled) {
  base::TimeTicks start_time = base::TimeTicks::Now();

  EXPECT_FALSE(GetPrefs()->GetBoolean(prefs::kSavingBrowserHistoryDisabled));

  Checkpoint("Before waiting for history to load", start_time);
  ui_test_utils::WaitForHistoryToLoad(browser());
  Checkpoint("After waiting for history to load", start_time);

  ui_test_utils::NavigateToURL(browser(), GetTestUrl());
  Checkpoint("After first NavigateToURL", start_time);
  WaitForHistoryBackendToRun();
  Checkpoint("After waiting for history backend to run", start_time);

  {
    std::vector<GURL> urls(GetHistoryContents());
    Checkpoint("After first GetHistoryContents", start_time);
    ASSERT_EQ(1U, urls.size());
    EXPECT_EQ(GetTestUrl().spec(), urls[0].spec());
  }

  GetPrefs()->SetBoolean(prefs::kSavingBrowserHistoryDisabled, true);

  ui_test_utils::NavigateToURL(browser(), GetTestUrl());
  Checkpoint("After second NavigateToURL", start_time);
  WaitForHistoryBackendToRun();
  Checkpoint("After waiting for history backend to run (2nd time)", start_time);

  {
    // No additional entries should be present in the history.
    std::vector<GURL> urls(GetHistoryContents());
    Checkpoint("After second GetHistoryContents", start_time);
    ASSERT_EQ(1U, urls.size());
    EXPECT_EQ(GetTestUrl().spec(), urls[0].spec());
  }
}

// Test that changing the pref takes effect immediately
// when the browser is running.
IN_PROC_BROWSER_TEST_F(HistoryBrowserTest, SavingHistoryDisabledThenEnabled) {
  GetPrefs()->SetBoolean(prefs::kSavingBrowserHistoryDisabled, true);

  ui_test_utils::WaitForHistoryToLoad(browser());
  ExpectEmptyHistory();

  ui_test_utils::NavigateToURL(browser(), GetTestUrl());
  WaitForHistoryBackendToRun();
  ExpectEmptyHistory();

  GetPrefs()->SetBoolean(prefs::kSavingBrowserHistoryDisabled, false);

  ui_test_utils::NavigateToURL(browser(), GetTestUrl());
  WaitForHistoryBackendToRun();

  {
    std::vector<GURL> urls(GetHistoryContents());
    ASSERT_EQ(1U, urls.size());
    EXPECT_EQ(GetTestUrl().spec(), urls[0].spec());
  }
}

}  // namespace
<|endoftext|>


<commit_before><commit_msg>Use destination URL to compute search engine type in OmniboxEditModel<commit_after><|endoftext|>


<commit_before>// Copyright 2013 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/signin/signin_header_helper.h"

#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/prefs/incognito_mode_prefs.h"
#include "chrome/browser/profiles/profile_io_data.h"
#include "chrome/browser/tab_contents/tab_util.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/url_constants.h"
#include "components/google/core/browser/google_util.h"
#include "components/signin/core/common/profile_management_switches.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"

#if defined(OS_ANDROID)
#include "chrome/browser/android/signin/account_management_screen_helper.h"
#else
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#endif  // defined(OS_ANDROID)

namespace {

// Dictionary of fields in a mirror response header.
typedef std::map<std::string, std::string> MirrorResponseHeaderDictionary;

const char kChromeConnectedHeader[] = "X-Chrome-Connected";
const char kChromeManageAccountsHeader[] = "X-Chrome-Manage-Accounts";
const char kGaiaIdAttrName[] = "id";
const char kProfileModeAttrName[] = "mode";
const char kEnableAccountConsistencyAttrName[] = "enable_account_consistency";

const char kServiceTypeAttrName[] = "action";
const char kEmailAttrName[] = "email";
const char kIsSamlAttrName[] = "is_saml";
const char kContinueUrlAttrName[] = "continue_url";
const char kIsSameTabAttrName[] = "is_same_tab";

// Determines the service type that has been passed from GAIA in the header.
signin::GAIAServiceType GetGAIAServiceTypeFromHeader(
    const std::string& header_value) {
  if (header_value == "SIGNOUT")
    return signin::GAIA_SERVICE_TYPE_SIGNOUT;
  else if (header_value == "INCOGNITO")
    return signin::GAIA_SERVICE_TYPE_INCOGNITO;
  else if (header_value == "ADDSESSION")
    return signin::GAIA_SERVICE_TYPE_ADDSESSION;
  else if (header_value == "REAUTH")
    return signin::GAIA_SERVICE_TYPE_REAUTH;
  else if (header_value == "SIGNUP")
    return signin::GAIA_SERVICE_TYPE_SIGNUP;
  else if (header_value == "DEFAULT")
    return signin::GAIA_SERVICE_TYPE_DEFAULT;
  else
    return signin::GAIA_SERVICE_TYPE_NONE;
}

// Parses the mirror response header. Its expected format is
// "key1=value1,key2=value2,...".
MirrorResponseHeaderDictionary ParseMirrorResponseHeader(
    const std::string& header_value) {
  std::vector<std::string> fields;
  if (!Tokenize(header_value, std::string(","), &fields))
    return MirrorResponseHeaderDictionary();

  MirrorResponseHeaderDictionary dictionary;
  for (std::vector<std::string>::iterator i = fields.begin();
       i != fields.end(); ++i) {
    std::string field(*i);
    std::vector<std::string> tokens;
    if (Tokenize(field, "=", &tokens) != 2) {
      DLOG(WARNING) << "Unexpected GAIA header field '" << field << "'.";
      continue;
    }
    dictionary[tokens[0]] = tokens[1];
  }
  return dictionary;
}

// Returns the parameters contained in the X-Chrome-Manage-Accounts response
// header.
signin::ManageAccountsParams BuildManageAccountsParams(
    const std::string& header_value) {
  signin::ManageAccountsParams params;
  MirrorResponseHeaderDictionary header_dictionary =
      ParseMirrorResponseHeader(header_value);
  MirrorResponseHeaderDictionary::const_iterator it = header_dictionary.begin();
  for (; it != header_dictionary.end(); ++it) {
    const std::string key_name(it->first);
    if (key_name == kServiceTypeAttrName) {
      params.service_type =
          GetGAIAServiceTypeFromHeader(header_dictionary[kServiceTypeAttrName]);
    } else if (key_name == kEmailAttrName) {
      params.email = header_dictionary[kEmailAttrName];
    } else if (key_name == kIsSamlAttrName) {
      params.is_saml = header_dictionary[kIsSamlAttrName] == "true";
    } else if (key_name == kContinueUrlAttrName) {
      params.continue_url = header_dictionary[kContinueUrlAttrName];
    } else if (key_name == kIsSameTabAttrName) {
      params.is_same_tab = header_dictionary[kIsSameTabAttrName] == "true";
    } else {
      DLOG(WARNING) << "Unexpected GAIA header attribute '" << key_name << "'.";
    }
  }
  return params;
}

#if !defined(OS_IOS)
// Processes the mirror response header on the UI thread. Currently depending
// on the value of |header_value|, it either shows the profile avatar menu, or
// opens an incognito window/tab.
void ProcessMirrorHeaderUIThread(
    int child_id, int route_id,
    signin::ManageAccountsParams manage_accounts_params) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

  signin::GAIAServiceType service_type = manage_accounts_params.service_type;
  DCHECK_NE(signin::GAIA_SERVICE_TYPE_NONE, service_type);

  content::WebContents* web_contents =
      tab_util::GetWebContentsByID(child_id, route_id);
  if (!web_contents)
    return;

#if !defined(OS_ANDROID)
  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
  if (browser) {
    BrowserWindow::AvatarBubbleMode bubble_mode;
    switch (service_type) {
      case signin::GAIA_SERVICE_TYPE_INCOGNITO:
        chrome::NewIncognitoWindow(browser);
        return;
      case signin::GAIA_SERVICE_TYPE_ADDSESSION:
        bubble_mode = BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN;
        break;
      case signin::GAIA_SERVICE_TYPE_REAUTH:
        bubble_mode = BrowserWindow::AVATAR_BUBBLE_MODE_REAUTH;
        break;
      default:
        bubble_mode = BrowserWindow::AVATAR_BUBBLE_MODE_ACCOUNT_MANAGEMENT;
    }
    browser->window()->ShowAvatarBubbleFromAvatarButton(
        bubble_mode, manage_accounts_params);
  }
#else  // defined(OS_ANDROID)
  if (service_type == signin::GAIA_SERVICE_TYPE_INCOGNITO) {
    web_contents->OpenURL(content::OpenURLParams(
        GURL(chrome::kChromeUINativeNewTabURL), content::Referrer(),
        OFF_THE_RECORD, content::PAGE_TRANSITION_AUTO_TOPLEVEL, false));
  } else {
    AccountManagementScreenHelper::OpenAccountManagementScreen(
        Profile::FromBrowserContext(web_contents->GetBrowserContext()),
        service_type);
  }
#endif // OS_ANDROID
}
#endif // !defined(OS_IOS)

bool IsDriveOrigin(const GURL& url) {
  if (!url.SchemeIsSecure())
    return false;

  const GURL kGoogleDriveURL("https://drive.google.com");
  const GURL kGoogleDocsURL("https://docs.google.com");
  return url == kGoogleDriveURL || url == kGoogleDocsURL;
}

} // empty namespace

namespace signin {

ManageAccountsParams::ManageAccountsParams() :
    service_type(GAIA_SERVICE_TYPE_NONE),
    email(""),
    is_saml(false),
    continue_url(""),
    is_same_tab(false),
    child_id(0),
    route_id(0) {}

bool AppendMirrorRequestHeaderIfPossible(
    net::URLRequest* request,
    const GURL& redirect_url,
    ProfileIOData* io_data) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));

  if (io_data->IsOffTheRecord() ||
      io_data->google_services_username()->GetValue().empty()) {
    return false;
  }

  // Only set the header for Drive always, and other Google properties if
  // new-profile-management is enabled.
  // Vasquette, which is integrated with most Google properties, needs the
  // header to redirect certain user actions to Chrome native UI. Drive needs
  // the header to tell if the current user is connected. The drive path is a
  // temporary workaround until the more generic chrome.principals API is
  // available.
  const GURL& url = redirect_url.is_empty() ? request->url() : redirect_url;
  GURL origin(url.GetOrigin());
  bool is_enable_account_consistency = switches::IsEnableAccountConsistency();
  bool is_google_url =
      !switches::IsEnableWebBasedSignin() &&
      is_enable_account_consistency &&
      (google_util::IsGoogleDomainUrl(
           url,
           google_util::ALLOW_SUBDOMAIN,
           google_util::DISALLOW_NON_STANDARD_PORTS) ||
       google_util::IsYoutubeDomainUrl(
           url,
           google_util::ALLOW_SUBDOMAIN,
           google_util::DISALLOW_NON_STANDARD_PORTS));
  if (!is_google_url && !IsDriveOrigin(origin))
    return false;

  std::string account_id(io_data->google_services_account_id()->GetValue());

  int profile_mode_mask = PROFILE_MODE_DEFAULT;
  if (io_data->incognito_availibility()->GetValue() ==
          IncognitoModePrefs::DISABLED ||
      IncognitoModePrefs::ArePlatformParentalControlsEnabledCached()) {
    profile_mode_mask |= PROFILE_MODE_INCOGNITO_DISABLED;
  }

  // TODO(guohui): needs to make a new flag for enabling account consistency.
  std::string header_value(base::StringPrintf("%s=%s,%s=%s,%s=%s",
      kGaiaIdAttrName, account_id.c_str(),
      kProfileModeAttrName, base::IntToString(profile_mode_mask).c_str(),
      kEnableAccountConsistencyAttrName,
      is_enable_account_consistency ? "true" : "false"));
  request->SetExtraRequestHeaderByName(
      kChromeConnectedHeader, header_value, false);
  return true;
}

void ProcessMirrorResponseHeaderIfExists(
    net::URLRequest* request,
    ProfileIOData* io_data,
    int child_id,
    int route_id) {
#if defined(OS_IOS)
  NOTREACHED();
#else
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
  if (!gaia::IsGaiaSignonRealm(request->url().GetOrigin()))
    return;

  std::string header_value;
  if (!request->response_headers()->GetNormalizedHeader(
          kChromeManageAccountsHeader, &header_value)) {
    return;
  }

  DCHECK(switches::IsEnableAccountConsistency() && !io_data->IsOffTheRecord());
  ManageAccountsParams params(BuildManageAccountsParams(header_value));
  if (params.service_type == GAIA_SERVICE_TYPE_NONE)
    return;

  params.child_id = child_id;
  params.route_id = route_id;
  content::BrowserThread::PostTask(
      content::BrowserThread::UI, FROM_HERE,
      base::Bind(ProcessMirrorHeaderUIThread, child_id, route_id, params));
#endif  // defined(OS_IOS)
}

} // namespace signin
<commit_msg>Ensures Chrome only honors x-chrome-manage-accounts header on user initiated top frame requests.<commit_after>// Copyright 2013 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/signin/signin_header_helper.h"

#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/prefs/incognito_mode_prefs.h"
#include "chrome/browser/profiles/profile_io_data.h"
#include "chrome/browser/tab_contents/tab_util.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/url_constants.h"
#include "components/google/core/browser/google_util.h"
#include "components/signin/core/common/profile_management_switches.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/resource_request_info.h"
#include "content/public/browser/web_contents.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"

#if defined(OS_ANDROID)
#include "chrome/browser/android/signin/account_management_screen_helper.h"
#else
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#endif  // defined(OS_ANDROID)

namespace {

// Dictionary of fields in a mirror response header.
typedef std::map<std::string, std::string> MirrorResponseHeaderDictionary;

const char kChromeConnectedHeader[] = "X-Chrome-Connected";
const char kChromeManageAccountsHeader[] = "X-Chrome-Manage-Accounts";
const char kGaiaIdAttrName[] = "id";
const char kProfileModeAttrName[] = "mode";
const char kEnableAccountConsistencyAttrName[] = "enable_account_consistency";

const char kServiceTypeAttrName[] = "action";
const char kEmailAttrName[] = "email";
const char kIsSamlAttrName[] = "is_saml";
const char kContinueUrlAttrName[] = "continue_url";
const char kIsSameTabAttrName[] = "is_same_tab";

// Determines the service type that has been passed from GAIA in the header.
signin::GAIAServiceType GetGAIAServiceTypeFromHeader(
    const std::string& header_value) {
  if (header_value == "SIGNOUT")
    return signin::GAIA_SERVICE_TYPE_SIGNOUT;
  else if (header_value == "INCOGNITO")
    return signin::GAIA_SERVICE_TYPE_INCOGNITO;
  else if (header_value == "ADDSESSION")
    return signin::GAIA_SERVICE_TYPE_ADDSESSION;
  else if (header_value == "REAUTH")
    return signin::GAIA_SERVICE_TYPE_REAUTH;
  else if (header_value == "SIGNUP")
    return signin::GAIA_SERVICE_TYPE_SIGNUP;
  else if (header_value == "DEFAULT")
    return signin::GAIA_SERVICE_TYPE_DEFAULT;
  else
    return signin::GAIA_SERVICE_TYPE_NONE;
}

// Parses the mirror response header. Its expected format is
// "key1=value1,key2=value2,...".
MirrorResponseHeaderDictionary ParseMirrorResponseHeader(
    const std::string& header_value) {
  std::vector<std::string> fields;
  if (!Tokenize(header_value, std::string(","), &fields))
    return MirrorResponseHeaderDictionary();

  MirrorResponseHeaderDictionary dictionary;
  for (std::vector<std::string>::iterator i = fields.begin();
       i != fields.end(); ++i) {
    std::string field(*i);
    std::vector<std::string> tokens;
    if (Tokenize(field, "=", &tokens) != 2) {
      DLOG(WARNING) << "Unexpected GAIA header field '" << field << "'.";
      continue;
    }
    dictionary[tokens[0]] = tokens[1];
  }
  return dictionary;
}

// Returns the parameters contained in the X-Chrome-Manage-Accounts response
// header.
signin::ManageAccountsParams BuildManageAccountsParams(
    const std::string& header_value) {
  signin::ManageAccountsParams params;
  MirrorResponseHeaderDictionary header_dictionary =
      ParseMirrorResponseHeader(header_value);
  MirrorResponseHeaderDictionary::const_iterator it = header_dictionary.begin();
  for (; it != header_dictionary.end(); ++it) {
    const std::string key_name(it->first);
    if (key_name == kServiceTypeAttrName) {
      params.service_type =
          GetGAIAServiceTypeFromHeader(header_dictionary[kServiceTypeAttrName]);
    } else if (key_name == kEmailAttrName) {
      params.email = header_dictionary[kEmailAttrName];
    } else if (key_name == kIsSamlAttrName) {
      params.is_saml = header_dictionary[kIsSamlAttrName] == "true";
    } else if (key_name == kContinueUrlAttrName) {
      params.continue_url = header_dictionary[kContinueUrlAttrName];
    } else if (key_name == kIsSameTabAttrName) {
      params.is_same_tab = header_dictionary[kIsSameTabAttrName] == "true";
    } else {
      DLOG(WARNING) << "Unexpected GAIA header attribute '" << key_name << "'.";
    }
  }
  return params;
}

#if !defined(OS_IOS)
// Processes the mirror response header on the UI thread. Currently depending
// on the value of |header_value|, it either shows the profile avatar menu, or
// opens an incognito window/tab.
void ProcessMirrorHeaderUIThread(
    int child_id, int route_id,
    signin::ManageAccountsParams manage_accounts_params) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

  signin::GAIAServiceType service_type = manage_accounts_params.service_type;
  DCHECK_NE(signin::GAIA_SERVICE_TYPE_NONE, service_type);

  content::WebContents* web_contents =
      tab_util::GetWebContentsByID(child_id, route_id);
  if (!web_contents)
    return;

#if !defined(OS_ANDROID)
  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
  if (browser) {
    BrowserWindow::AvatarBubbleMode bubble_mode;
    switch (service_type) {
      case signin::GAIA_SERVICE_TYPE_INCOGNITO:
        chrome::NewIncognitoWindow(browser);
        return;
      case signin::GAIA_SERVICE_TYPE_ADDSESSION:
        bubble_mode = BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN;
        break;
      case signin::GAIA_SERVICE_TYPE_REAUTH:
        bubble_mode = BrowserWindow::AVATAR_BUBBLE_MODE_REAUTH;
        break;
      default:
        bubble_mode = BrowserWindow::AVATAR_BUBBLE_MODE_ACCOUNT_MANAGEMENT;
    }
    browser->window()->ShowAvatarBubbleFromAvatarButton(
        bubble_mode, manage_accounts_params);
  }
#else  // defined(OS_ANDROID)
  if (service_type == signin::GAIA_SERVICE_TYPE_INCOGNITO) {
    web_contents->OpenURL(content::OpenURLParams(
        GURL(chrome::kChromeUINativeNewTabURL), content::Referrer(),
        OFF_THE_RECORD, content::PAGE_TRANSITION_AUTO_TOPLEVEL, false));
  } else {
    AccountManagementScreenHelper::OpenAccountManagementScreen(
        Profile::FromBrowserContext(web_contents->GetBrowserContext()),
        service_type);
  }
#endif // OS_ANDROID
}
#endif // !defined(OS_IOS)

bool IsDriveOrigin(const GURL& url) {
  if (!url.SchemeIsSecure())
    return false;

  const GURL kGoogleDriveURL("https://drive.google.com");
  const GURL kGoogleDocsURL("https://docs.google.com");
  return url == kGoogleDriveURL || url == kGoogleDocsURL;
}

} // empty namespace

namespace signin {

ManageAccountsParams::ManageAccountsParams() :
    service_type(GAIA_SERVICE_TYPE_NONE),
    email(""),
    is_saml(false),
    continue_url(""),
    is_same_tab(false),
    child_id(0),
    route_id(0) {}

bool AppendMirrorRequestHeaderIfPossible(
    net::URLRequest* request,
    const GURL& redirect_url,
    ProfileIOData* io_data) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));

  if (io_data->IsOffTheRecord() ||
      io_data->google_services_username()->GetValue().empty()) {
    return false;
  }

  // Only set the header for Drive always, and other Google properties if
  // new-profile-management is enabled.
  // Vasquette, which is integrated with most Google properties, needs the
  // header to redirect certain user actions to Chrome native UI. Drive needs
  // the header to tell if the current user is connected. The drive path is a
  // temporary workaround until the more generic chrome.principals API is
  // available.
  const GURL& url = redirect_url.is_empty() ? request->url() : redirect_url;
  GURL origin(url.GetOrigin());
  bool is_enable_account_consistency = switches::IsEnableAccountConsistency();
  bool is_google_url =
      !switches::IsEnableWebBasedSignin() &&
      is_enable_account_consistency &&
      (google_util::IsGoogleDomainUrl(
           url,
           google_util::ALLOW_SUBDOMAIN,
           google_util::DISALLOW_NON_STANDARD_PORTS) ||
       google_util::IsYoutubeDomainUrl(
           url,
           google_util::ALLOW_SUBDOMAIN,
           google_util::DISALLOW_NON_STANDARD_PORTS));
  if (!is_google_url && !IsDriveOrigin(origin))
    return false;

  std::string account_id(io_data->google_services_account_id()->GetValue());

  int profile_mode_mask = PROFILE_MODE_DEFAULT;
  if (io_data->incognito_availibility()->GetValue() ==
          IncognitoModePrefs::DISABLED ||
      IncognitoModePrefs::ArePlatformParentalControlsEnabledCached()) {
    profile_mode_mask |= PROFILE_MODE_INCOGNITO_DISABLED;
  }

  // TODO(guohui): needs to make a new flag for enabling account consistency.
  std::string header_value(base::StringPrintf("%s=%s,%s=%s,%s=%s",
      kGaiaIdAttrName, account_id.c_str(),
      kProfileModeAttrName, base::IntToString(profile_mode_mask).c_str(),
      kEnableAccountConsistencyAttrName,
      is_enable_account_consistency ? "true" : "false"));
  request->SetExtraRequestHeaderByName(
      kChromeConnectedHeader, header_value, false);
  return true;
}

void ProcessMirrorResponseHeaderIfExists(
    net::URLRequest* request,
    ProfileIOData* io_data,
    int child_id,
    int route_id) {
#if defined(OS_IOS)
  NOTREACHED();
#else
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
  if (!gaia::IsGaiaSignonRealm(request->url().GetOrigin()))
    return;

  const content::ResourceRequestInfo* info =
      content::ResourceRequestInfo::ForRequest(request);
  if (!(info && info->IsMainFrame() && info->HasUserGesture()))
    return;

  std::string header_value;
  if (!request->response_headers()->GetNormalizedHeader(
          kChromeManageAccountsHeader, &header_value)) {
    return;
  }

  DCHECK(switches::IsEnableAccountConsistency() && !io_data->IsOffTheRecord());
  ManageAccountsParams params(BuildManageAccountsParams(header_value));
  if (params.service_type == GAIA_SERVICE_TYPE_NONE)
    return;

  params.child_id = child_id;
  params.route_id = route_id;
  content::BrowserThread::PostTask(
      content::BrowserThread::UI, FROM_HERE,
      base::Bind(ProcessMirrorHeaderUIThread, child_id, route_id, params));
#endif  // defined(OS_IOS)
}

} // namespace signin
<|endoftext|>


<commit_before>// Ylikuutio - A 3D game and simulation engine.
//
// Copyright (C) 2015-2021 Antti Nuortimo.
//
// This program 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 (at your option) any later version.
//
// This program 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 more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include <chrono>   // std::chrono
#include <cstdlib>  // std::rand, std::srand
#include <iostream> // std::cout, std::cin, std::cerr
#include <mutex>    // std::mutex, std::scoped_lock
#include <stdint.h> // uint32_t etc.
#include <thread>   // std::thread
#include <ctime>    // std::time
#include <vector>   // std::vector

class Messenger
{
    public:
        std::mutex my_mutex;
};

class Random
{
    public:
        Random()
        {
            // constructor.
            std::srand(std::time(nullptr)); // Use current time as seed.
        }

        float get_random() const
        {
            float dividend = static_cast<float>(std::rand() % 10000);
            float divisor = static_cast<float>(std::rand() % 10000);
            return dividend / divisor / 10000.0f;;
        }

        std::mutex my_mutex;
};

class Fork
{
    public:
        Fork()
        {
            // constructor.
        }

        std::mutex my_mutex;
};

class Philosopher
{
    public:
        Philosopher(Messenger& messenger, Random& random, const uint32_t id, Fork& left_fork, Fork& right_fork)
            : messenger { messenger },
            random { random },
            left_fork { left_fork },
            right_fork { right_fork },
            id { id }
        {
            // constructor.
            const std::lock_guard<std::mutex> lock(this->messenger.my_mutex);
            std::cout << "Philosopher " << this->id << " says hello!\n";
        }

        void eat() const
        {
            std::scoped_lock fork_lock(this->left_fork.my_mutex, this->right_fork.my_mutex);
            float eat_time = 0.0f;
            {
                const std::lock_guard<std::mutex> random_lock(this->random.my_mutex);
                eat_time = this->random.get_random();
            }
            {
                const std::lock_guard<std::mutex> lock(this->messenger.my_mutex);
                std::cout << "Philosopher " << this->id << " is eating for " << eat_time << " seconds...\n";
            }
            std::this_thread::sleep_for(std::chrono::duration<float>(eat_time));
        }

        void think() const
        {
            float think_time = 0.0f;
            {
                const std::lock_guard<std::mutex> random_lock(this->random.my_mutex);
                think_time = this->random.get_random();
            }
            {
                const std::lock_guard<std::mutex> lock(this->messenger.my_mutex);
                std::cout << "Philosopher " << this->id << " is thinking for " << think_time << " seconds...\n";
            }
            std::this_thread::sleep_for(std::chrono::duration<float>(think_time));
        }

        void philosophize() const
        {
            while (true)
            {
                eat();
                think();
            }
        }

    private:
        Messenger& messenger;
        Random& random;
        Fork& left_fork;
        Fork& right_fork;
        const uint32_t id;
};

class Table
{
    public:
        Table(const uint32_t n_philosophers, Messenger& messenger, Random& random)
            : n_philosophers { n_philosophers },
            messenger { messenger },
            random { random },
            forks(n_philosophers)
        {
            // constructor.

            this->philosophers.reserve(this->n_philosophers);

            if (this->n_philosophers > 0)
            {
                this->philosophers.emplace_back(this->messenger, this->random, 0, this->forks.back(), this->forks.front());
            }

            for (uint32_t id = 1; id < this->n_philosophers; id++)
            {
                this->philosophers.emplace_back(this->messenger, this->random, id, this->forks.at(id - 1), this->forks.at(id));
            }

            this->threads.resize(this->n_philosophers);
        }

        void dine()
        {
            for (uint32_t i = 0; i < this->n_philosophers; i++)
            {
                this->threads.at(i) = std::thread { &Philosopher::philosophize, this->philosophers.at(i) };
            }
        }

        void cleanup()
        {
            for (std::thread& my_thread : this->threads)
            {
                my_thread.join();
            }
        }

        std::vector<Fork> forks;
        std::vector<Philosopher> philosophers;
        std::vector<std::thread> threads;
        const uint32_t n_philosophers;
        Messenger& messenger;
        Random& random;
};

int main()
{
    Messenger messenger;
    Random random;
    Table table(5, messenger, random);
    table.dine();
    table.cleanup();
}
<commit_msg>Rename variables: `lock` -> `messenger_lock`.<commit_after>// Ylikuutio - A 3D game and simulation engine.
//
// Copyright (C) 2015-2021 Antti Nuortimo.
//
// This program 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 (at your option) any later version.
//
// This program 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 more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include <chrono>   // std::chrono
#include <cstdlib>  // std::rand, std::srand
#include <iostream> // std::cout, std::cin, std::cerr
#include <mutex>    // std::mutex, std::scoped_lock
#include <stdint.h> // uint32_t etc.
#include <thread>   // std::thread
#include <ctime>    // std::time
#include <vector>   // std::vector

class Messenger
{
    public:
        std::mutex my_mutex;
};

class Random
{
    public:
        Random()
        {
            // constructor.
            std::srand(std::time(nullptr)); // Use current time as seed.
        }

        float get_random() const
        {
            float dividend = static_cast<float>(std::rand() % 10000);
            float divisor = static_cast<float>(std::rand() % 10000);
            return dividend / divisor / 10000.0f;;
        }

        std::mutex my_mutex;
};

class Fork
{
    public:
        Fork()
        {
            // constructor.
        }

        std::mutex my_mutex;
};

class Philosopher
{
    public:
        Philosopher(Messenger& messenger, Random& random, const uint32_t id, Fork& left_fork, Fork& right_fork)
            : messenger { messenger },
            random { random },
            left_fork { left_fork },
            right_fork { right_fork },
            id { id }
        {
            // constructor.
            const std::lock_guard<std::mutex> messenger_lock(this->messenger.my_mutex);
            std::cout << "Philosopher " << this->id << " says hello!\n";
        }

        void eat() const
        {
            std::scoped_lock fork_lock(this->left_fork.my_mutex, this->right_fork.my_mutex);
            float eat_time = 0.0f;
            {
                const std::lock_guard<std::mutex> random_lock(this->random.my_mutex);
                eat_time = this->random.get_random();
            }
            {
                const std::lock_guard<std::mutex> messenger_lock(this->messenger.my_mutex);
                std::cout << "Philosopher " << this->id << " is eating for " << eat_time << " seconds...\n";
            }
            std::this_thread::sleep_for(std::chrono::duration<float>(eat_time));
        }

        void think() const
        {
            float think_time = 0.0f;
            {
                const std::lock_guard<std::mutex> random_lock(this->random.my_mutex);
                think_time = this->random.get_random();
            }
            {
                const std::lock_guard<std::mutex> messenger_lock(this->messenger.my_mutex);
                std::cout << "Philosopher " << this->id << " is thinking for " << think_time << " seconds...\n";
            }
            std::this_thread::sleep_for(std::chrono::duration<float>(think_time));
        }

        void philosophize() const
        {
            while (true)
            {
                eat();
                think();
            }
        }

    private:
        Messenger& messenger;
        Random& random;
        Fork& left_fork;
        Fork& right_fork;
        const uint32_t id;
};

class Table
{
    public:
        Table(const uint32_t n_philosophers, Messenger& messenger, Random& random)
            : n_philosophers { n_philosophers },
            messenger { messenger },
            random { random },
            forks(n_philosophers)
        {
            // constructor.

            this->philosophers.reserve(this->n_philosophers);

            if (this->n_philosophers > 0)
            {
                this->philosophers.emplace_back(this->messenger, this->random, 0, this->forks.back(), this->forks.front());
            }

            for (uint32_t id = 1; id < this->n_philosophers; id++)
            {
                this->philosophers.emplace_back(this->messenger, this->random, id, this->forks.at(id - 1), this->forks.at(id));
            }

            this->threads.resize(this->n_philosophers);
        }

        void dine()
        {
            for (uint32_t i = 0; i < this->n_philosophers; i++)
            {
                this->threads.at(i) = std::thread { &Philosopher::philosophize, this->philosophers.at(i) };
            }
        }

        void cleanup()
        {
            for (std::thread& my_thread : this->threads)
            {
                my_thread.join();
            }
        }

        std::vector<Fork> forks;
        std::vector<Philosopher> philosophers;
        std::vector<std::thread> threads;
        const uint32_t n_philosophers;
        Messenger& messenger;
        Random& random;
};

int main()
{
    Messenger messenger;
    Random random;
    Table table(5, messenger, random);
    table.dine();
    table.cleanup();
}
<|endoftext|>


<commit_before>// This file is a part of the OpenSurgSim project.
// Copyright 2013, SimQuest Solutions Inc.
//
// Licensed 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 <memory>

#include <gtest/gtest.h>
#include "SurgSim/Physics/ContactConstraintData.h"
#include "SurgSim/Blocks/MassSpring1DRepresentation.h"
#include "SurgSim/Physics/MassSpringRepresentationContact.h"
#include "SurgSim/Physics/MassSpringRepresentationLocalization.h"
#include "SurgSim/Physics/MlcpPhysicsProblem.h"

#include "SurgSim/Math/RigidTransform.h"
#include "SurgSim/Math/Vector.h"

namespace
{
	const double epsilon = 1e-10;
	const double dt = 1e-3;
};

namespace SurgSim
{
namespace Physics
{
using SurgSim::Math::Vector3d;

class MassSpringRepresentationContactTest : public ::testing::Test
{
public:
	void SetUp() {
		// Define plane with normal 'n' pointing against gravity.
		m_n = Vector3d(0.8539, 0.6289, -0.9978);
		m_n.normalize();

		// Place spring at random location.
		m_extremities[0] = Vector3d(0.8799, -0.0871, 0.7468);
		m_extremities[1] = Vector3d(0.9040, -0.7074, 0.6783);

		// Define physics representation of mass-spring using 1d helper function.
		m_massSpring = std::make_shared<SurgSim::Blocks::MassSpring1DRepresentation>("MassSpring");
		unsigned int numNodesPerDim[1] = {2};
		m_massPerNode = 0.137;
		std::vector<unsigned int> boundaryConditions;
		m_massSpring->init1D(
			m_extremities,
			numNodesPerDim,
			boundaryConditions,
			numNodesPerDim[0] * m_massPerNode, // total mass (in Kg)
			100.0, // Stiffness stretching
			0.0, // Damping stretching
			10.0, // Stiffness bending
			0.0); // Damping bending

		// Update position in only 1 timestep
		// Forward euler for velocity, backward euler for position
		m_massSpring->setIntegrationScheme(SurgSim::Math::IntegrationScheme::INTEGRATIONSCHEME_MODIFIED_EXPLICIT_EULER);

		m_massSpring->setIsActive(true);

		// Update model by one timestep
		m_massSpring->beforeUpdate(dt);
		m_massSpring->update(dt);

		// Create localization helper class
		m_localization = std::make_shared<MassSpringRepresentationLocalization>(m_massSpring);
	}

	void setContactAtNode(size_t nodeId)
	{
		m_nodeId = nodeId;
		m_localization->setLocalNode(nodeId);

		// Place plane at nodeId
		double distance = -m_extremities[nodeId].dot(m_n);
		m_constraintData.setPlaneEquation(m_n, distance);
	}

	Vector3d m_n;
	ContactConstraintData m_constraintData;
	size_t m_nodeId;

	std::shared_ptr<SurgSim::Blocks::MassSpring1DRepresentation> m_massSpring;
	double m_massPerNode;
	std::array<Vector3d, 2> m_extremities;

	std::shared_ptr<MassSpringRepresentationLocalization> m_localization;
};

TEST_F(MassSpringRepresentationContactTest, ConstructorTest)
{
	ASSERT_NO_THROW({ MassSpringRepresentationContact massSpring; });

	ASSERT_NE(nullptr, std::make_shared<MassSpringRepresentationContact>());
}

TEST_F(MassSpringRepresentationContactTest, ConstraintConstantsTest)
{
	auto implementation = std::make_shared<MassSpringRepresentationContact>();

	EXPECT_EQ(SurgSim::Math::MLCP_UNILATERAL_3D_FRICTIONLESS_CONSTRAINT, implementation->getMlcpConstraintType());
	EXPECT_EQ(SurgSim::Physics::REPRESENTATION_TYPE_MASSSPRING, implementation->getRepresentationType());
	EXPECT_EQ(1u, implementation->getNumDof());
}

void initializeMlcp(MlcpPhysicsProblem *mlcpPhysicsProblem, size_t numDof, size_t numConstraints)
{
	// Resize and zero all Eigen types
	mlcpPhysicsProblem->A.resize(numConstraints, numConstraints);
	mlcpPhysicsProblem->A.setZero();
	mlcpPhysicsProblem->b.resize(numConstraints);
	mlcpPhysicsProblem->b.setZero();
	mlcpPhysicsProblem->mu.resize(numConstraints);
	mlcpPhysicsProblem->mu.setZero();
	mlcpPhysicsProblem->CHt.resize(numDof, numConstraints);
	mlcpPhysicsProblem->CHt.setZero();
	mlcpPhysicsProblem->H.resize(numConstraints, numDof);
	mlcpPhysicsProblem->H.setZero();
	// Empty all std::vector types
	mlcpPhysicsProblem->constraintTypes.clear();
}

TEST_F(MassSpringRepresentationContactTest, BuildMlcpTest)
{
	// Define constraint (frictionless non-penetration)
	auto implementation = std::make_shared<MassSpringRepresentationContact>();

	// Initialize MLCP
	MlcpPhysicsProblem mlcpPhysicsProblem;
	initializeMlcp(&mlcpPhysicsProblem, m_massSpring->getNumDof(), 1);

	// Build MLCP for 0th node
	setContactAtNode(0);

	implementation->build(dt, m_constraintData, m_localization,
		&mlcpPhysicsProblem, 0, 0, SurgSim::Physics::CONSTRAINT_POSITIVE_SIDE);

	// Expected results.
	// At the initial time-step, the only force is gravity.
	//
	// F(0) = m * a(0)
	// => a(0) = 1/m * F(0)
	//         = (0, -g, 0)
	//
	// Modified Explicit Euler:
	// v(1) = v(0) + dv/dt|(t=0)*dt
	//      = v(0) + a(0)*dt
	//      = (0, -g*dt, 0)
	//
	// p(1) = p(0) + dp/dt|(t=1)*dt
	//      = p(0) + v(1)*dt
	//      = p(0) + (0, -g*dt^2, 0)
	//
	// The constraint violation is the position from the plane projected onto the constraint.  Note, we have defined the
	// plane to intersect with p(0).  The constraint is
	//      U(t) = n^t.(p(t) - p(0)) >= 0
	//
	// U(1) = n^t.(p(1) - p(0))
	//      = (nx, ny, nz)^t.(0, -g*dt^2, 0)
	//      = -g*dt^2*ny
	EXPECT_NEAR(-9.81 * dt * dt * m_n[1], mlcpPhysicsProblem.b[0], epsilon);

	// By definition, H = dU/dp.
	//
	// dU/dt = (dU/dp).(dp/dt)
	//       = H.(dp/dt)
	//
	// dp = p(1) - p(0)
	// => dp/dt = (p(1) - p(0)) / dt
	//
	// dU/dt = (U(1) - U(0)) / dt      [Note U(0) = 0]
	//       = n^t.(p(1) - p(0)) / dt
	//       = n^t.(dp/dt)
	// => H = n^t
	EXPECT_NEAR(m_n[0], mlcpPhysicsProblem.H(0, 0), epsilon);
	EXPECT_NEAR(m_n[1], mlcpPhysicsProblem.H(0, 1), epsilon);
	EXPECT_NEAR(m_n[2], mlcpPhysicsProblem.H(0, 2), epsilon);

	// We define C as the matrix which transforms F -> v (which differs from treatments which define it as F -> p)
	// v(1) = v(0) + a(0)*dt
	//      = v(0) + 1/m*F(0)*dt
	// => (v(1) - v(0)) = [dt/m] * F(0)
	//
	// Therefore C = dt/m
	//
	// We can directly calculate
	// CHt = C * H^t
	//     = (dt/m) * (nx, ny, nz)
	EXPECT_NEAR(dt / m_massPerNode * m_n[0], mlcpPhysicsProblem.CHt(0, 0), epsilon);
	EXPECT_NEAR(dt / m_massPerNode * m_n[1], mlcpPhysicsProblem.CHt(1, 0), epsilon);
	EXPECT_NEAR(dt / m_massPerNode * m_n[2], mlcpPhysicsProblem.CHt(2, 0), epsilon);

	// And finally,
	// HCHt = [nx ny nz] * (dt/m) * (nx, ny, nz)
	//      = (dt/m) * (nx*nx + ny*ny + nz*nz)
	double calculatedA = mlcpPhysicsProblem.A.block<1, 1>(0, 0)[0]; // VS intellisense error workaround
	EXPECT_NEAR(dt / m_massPerNode * (m_n[0] * m_n[0] + m_n[1] * m_n[1] + m_n[2] * m_n[2]), calculatedA, epsilon);

	// ConstraintTypes should contain 0 entry as it is setup by the constraint and not the ConstraintImplementation
	// This way, the constraint can verify that both ConstraintImplementation are the same type
	EXPECT_EQ(0u, mlcpPhysicsProblem.constraintTypes.size());
}

TEST_F(MassSpringRepresentationContactTest, BuildMlcpIndiciesTest)
{
	auto implementation = std::make_shared<MassSpringRepresentationContact>();

	MlcpPhysicsProblem mlcpPhysicsProblem;
	initializeMlcp(&mlcpPhysicsProblem, 11, 2);

	// Suppose 5 dof and 1 constraint are defined elsewhere.  Then H, CHt, HCHt, and b are prebuilt.
	Eigen::Matrix<double, 1, 5> localH;
	localH <<
		0.9478,  -0.3807,  0.5536, -0.6944,  0.1815;
	mlcpPhysicsProblem.H.block<1, 5>(0, 0) = localH;

	Eigen::Matrix<double, 5, 5> localC;
	localC <<
		-0.2294,  0.5160,  0.2520,  0.5941, -0.4854,
		 0.1233, -0.4433,  0.3679,  0.9307,  0.2600,
		 0.1988,  0.6637, -0.7591,  0.1475,  0.8517,
		-0.5495, -0.4305,  0.3162, -0.7862,  0.7627,
		-0.5754,  0.4108,  0.8445, -0.5565,  0.7150;
	localC = localC * localC.transpose(); // force to be symmetric

	Eigen::Matrix<double, 5, 1> localCHt = localC * localH.transpose();
	mlcpPhysicsProblem.CHt.block<5, 1>(0, 0) = localCHt;

	mlcpPhysicsProblem.A.block<1, 1>(0, 0) = localH * localCHt;

	mlcpPhysicsProblem.b.block<1, 1>(0, 0)[0] = 0.6991;

	// Place mass-spring at 5th dof and 1th constraint.
	unsigned int indexOfRepresentation = 5;
	unsigned int indexOfConstraint = 1;

	setContactAtNode(1);

	implementation->build(dt, m_constraintData, m_localization,
		&mlcpPhysicsProblem, indexOfRepresentation, indexOfConstraint, SurgSim::Physics::CONSTRAINT_POSITIVE_SIDE);

	// b -> E -> [#constraints, 1]
	EXPECT_NEAR(-9.81 * dt * dt * m_n[1], mlcpPhysicsProblem.b[indexOfConstraint], epsilon);

	// H -> [#constraints, #dof]
	EXPECT_NEAR(m_n[0], mlcpPhysicsProblem.H(indexOfConstraint, indexOfRepresentation + 3 * m_nodeId + 0), epsilon);
	EXPECT_NEAR(m_n[1], mlcpPhysicsProblem.H(indexOfConstraint, indexOfRepresentation + 3 * m_nodeId + 1), epsilon);
	EXPECT_NEAR(m_n[2], mlcpPhysicsProblem.H(indexOfConstraint, indexOfRepresentation + 3 * m_nodeId + 2), epsilon);

	// C -> [#dof, #dof]
	// CHt -> [#dof, #constraints]
	EXPECT_NEAR(dt / m_massPerNode * m_n[0],
		mlcpPhysicsProblem.CHt(indexOfRepresentation + 3 * m_nodeId + 0, indexOfConstraint), epsilon);
	EXPECT_NEAR(dt / m_massPerNode * m_n[1],
		mlcpPhysicsProblem.CHt(indexOfRepresentation + 3 * m_nodeId + 1, indexOfConstraint), epsilon);
	EXPECT_NEAR(dt / m_massPerNode * m_n[2],
		mlcpPhysicsProblem.CHt(indexOfRepresentation + 3 * m_nodeId + 2, indexOfConstraint), epsilon);

	// A -> HCHt -> [#constraints, #constraints]
	double calculatedA = mlcpPhysicsProblem.A.block<1, 1>(indexOfConstraint, indexOfConstraint)[0];
	EXPECT_NEAR(dt / m_massPerNode * (m_n[0] * m_n[0] + m_n[1] * m_n[1] + m_n[2] * m_n[2]), calculatedA, epsilon);
}

};  //  namespace Physics
};  //  namespace SurgSim
<commit_msg>Fix cpplint warnings<commit_after>// This file is a part of the OpenSurgSim project.
// Copyright 2013, SimQuest Solutions Inc.
//
// Licensed 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 <memory>

#include <gtest/gtest.h>
#include "SurgSim/Physics/ContactConstraintData.h"
#include "SurgSim/Blocks/MassSpring1DRepresentation.h"
#include "SurgSim/Physics/MassSpringRepresentationContact.h"
#include "SurgSim/Physics/MassSpringRepresentationLocalization.h"
#include "SurgSim/Physics/MlcpPhysicsProblem.h"

#include "SurgSim/Math/RigidTransform.h"
#include "SurgSim/Math/Vector.h"

namespace
{
	const double epsilon = 1e-10;
	const double dt = 1e-3;
};

namespace SurgSim
{
namespace Physics
{
using SurgSim::Math::Vector3d;

class MassSpringRepresentationContactTest : public ::testing::Test
{
public:
	void SetUp() {
		// Define plane with normal 'n' pointing against gravity.
		m_n = Vector3d(0.8539, 0.6289, -0.9978);
		m_n.normalize();

		// Place spring at random location.
		m_extremities[0] = Vector3d(0.8799, -0.0871, 0.7468);
		m_extremities[1] = Vector3d(0.9040, -0.7074, 0.6783);

		// Define physics representation of mass-spring using 1d helper function.
		m_massSpring = std::make_shared<SurgSim::Blocks::MassSpring1DRepresentation>("MassSpring");
		unsigned int numNodesPerDim[1] = {2};
		m_massPerNode = 0.137;
		std::vector<unsigned int> boundaryConditions;
		m_massSpring->init1D(
			m_extremities,
			numNodesPerDim,
			boundaryConditions,
			numNodesPerDim[0] * m_massPerNode, // total mass (in Kg)
			100.0, // Stiffness stretching
			0.0, // Damping stretching
			10.0, // Stiffness bending
			0.0); // Damping bending

		// Update position in only 1 timestep
		// Forward euler for velocity, backward euler for position
		m_massSpring->setIntegrationScheme(SurgSim::Math::IntegrationScheme::INTEGRATIONSCHEME_MODIFIED_EXPLICIT_EULER);

		m_massSpring->setIsActive(true);

		// Update model by one timestep
		m_massSpring->beforeUpdate(dt);
		m_massSpring->update(dt);

		// Create localization helper class
		m_localization = std::make_shared<MassSpringRepresentationLocalization>(m_massSpring);
	}

	void setContactAtNode(size_t nodeId)
	{
		m_nodeId = nodeId;
		m_localization->setLocalNode(nodeId);

		// Place plane at nodeId
		double distance = -m_extremities[nodeId].dot(m_n);
		m_constraintData.setPlaneEquation(m_n, distance);
	}

	Vector3d m_n;
	ContactConstraintData m_constraintData;
	size_t m_nodeId;

	std::shared_ptr<SurgSim::Blocks::MassSpring1DRepresentation> m_massSpring;
	double m_massPerNode;
	std::array<Vector3d, 2> m_extremities;

	std::shared_ptr<MassSpringRepresentationLocalization> m_localization;
};

TEST_F(MassSpringRepresentationContactTest, ConstructorTest)
{
	ASSERT_NO_THROW({ MassSpringRepresentationContact massSpring; });

	ASSERT_NE(nullptr, std::make_shared<MassSpringRepresentationContact>());
}

TEST_F(MassSpringRepresentationContactTest, ConstraintConstantsTest)
{
	auto implementation = std::make_shared<MassSpringRepresentationContact>();

	EXPECT_EQ(SurgSim::Math::MLCP_UNILATERAL_3D_FRICTIONLESS_CONSTRAINT, implementation->getMlcpConstraintType());
	EXPECT_EQ(SurgSim::Physics::REPRESENTATION_TYPE_MASSSPRING, implementation->getRepresentationType());
	EXPECT_EQ(1u, implementation->getNumDof());
}

void initializeMlcp(MlcpPhysicsProblem *mlcpPhysicsProblem, size_t numDof, size_t numConstraints)
{
	// Resize and zero all Eigen types
	mlcpPhysicsProblem->A.resize(numConstraints, numConstraints);
	mlcpPhysicsProblem->A.setZero();
	mlcpPhysicsProblem->b.resize(numConstraints);
	mlcpPhysicsProblem->b.setZero();
	mlcpPhysicsProblem->mu.resize(numConstraints);
	mlcpPhysicsProblem->mu.setZero();
	mlcpPhysicsProblem->CHt.resize(numDof, numConstraints);
	mlcpPhysicsProblem->CHt.setZero();
	mlcpPhysicsProblem->H.resize(numConstraints, numDof);
	mlcpPhysicsProblem->H.setZero();
	// Empty all std::vector types
	mlcpPhysicsProblem->constraintTypes.clear();
}

TEST_F(MassSpringRepresentationContactTest, BuildMlcpTest)
{
	// Define constraint (frictionless non-penetration)
	auto implementation = std::make_shared<MassSpringRepresentationContact>();

	// Initialize MLCP
	MlcpPhysicsProblem mlcpPhysicsProblem;
	initializeMlcp(&mlcpPhysicsProblem, m_massSpring->getNumDof(), 1);

	// Build MLCP for 0th node
	setContactAtNode(0);

	implementation->build(dt, m_constraintData, m_localization,
		&mlcpPhysicsProblem, 0, 0, SurgSim::Physics::CONSTRAINT_POSITIVE_SIDE);

	// Expected results.
	// At the initial time-step, the only force is gravity.
	//
	// F(0) = m * a(0)
	// => a(0) = 1/m * F(0)
	//         = (0, -g, 0)
	//
	// Modified Explicit Euler:
	// v(1) = v(0) + dv/dt|(t=0)*dt
	//      = v(0) + a(0)*dt
	//      = (0, -g*dt, 0)
	//
	// p(1) = p(0) + dp/dt|(t=1)*dt
	//      = p(0) + v(1)*dt
	//      = p(0) + (0, -g*dt^2, 0)
	//
	// The constraint violation is the position from the plane projected onto the constraint.  Note, we have defined the
	// plane to intersect with p(0).  The constraint is
	//      U(t) = n^t.(p(t) - p(0)) >= 0
	//
	// U(1) = n^t.(p(1) - p(0))
	//      = (nx, ny, nz)^t.(0, -g*dt^2, 0)
	//      = -g*dt^2*ny
	EXPECT_NEAR(-9.81 * dt * dt * m_n[1], mlcpPhysicsProblem.b[0], epsilon);

	// By definition, H = dU/dp.
	//
	// dU/dt = (dU/dp).(dp/dt)
	//       = H.(dp/dt)
	//
	// dp = p(1) - p(0)
	// => dp/dt = (p(1) - p(0)) / dt
	//
	// dU/dt = (U(1) - U(0)) / dt      [Note U(0) = 0]
	//       = n^t.(p(1) - p(0)) / dt
	//       = n^t.(dp/dt)
	// => H = n^t
	EXPECT_NEAR(m_n[0], mlcpPhysicsProblem.H(0, 0), epsilon);
	EXPECT_NEAR(m_n[1], mlcpPhysicsProblem.H(0, 1), epsilon);
	EXPECT_NEAR(m_n[2], mlcpPhysicsProblem.H(0, 2), epsilon);

	// We define C as the matrix which transforms F -> v (which differs from treatments which define it as F -> p)
	// v(1) = v(0) + a(0)*dt
	//      = v(0) + 1/m*F(0)*dt
	// => (v(1) - v(0)) = [dt/m] * F(0)
	//
	// Therefore C = dt/m
	//
	// We can directly calculate
	// CHt = C * H^t
	//     = (dt/m) * (nx, ny, nz)
	EXPECT_NEAR(dt / m_massPerNode * m_n[0], mlcpPhysicsProblem.CHt(0, 0), epsilon);
	EXPECT_NEAR(dt / m_massPerNode * m_n[1], mlcpPhysicsProblem.CHt(1, 0), epsilon);
	EXPECT_NEAR(dt / m_massPerNode * m_n[2], mlcpPhysicsProblem.CHt(2, 0), epsilon);

	// And finally,
	// HCHt = [nx ny nz] * (dt/m) * (nx, ny, nz)
	//      = (dt/m) * (nx*nx + ny*ny + nz*nz)
	double calculatedA = mlcpPhysicsProblem.A.block<1, 1>(0, 0)[0]; // VS intellisense error workaround
	EXPECT_NEAR(dt / m_massPerNode * (m_n[0] * m_n[0] + m_n[1] * m_n[1] + m_n[2] * m_n[2]), calculatedA, epsilon);

	// ConstraintTypes should contain 0 entry as it is setup by the constraint and not the ConstraintImplementation
	// This way, the constraint can verify that both ConstraintImplementation are the same type
	EXPECT_EQ(0u, mlcpPhysicsProblem.constraintTypes.size());
}

TEST_F(MassSpringRepresentationContactTest, BuildMlcpIndiciesTest)
{
	auto implementation = std::make_shared<MassSpringRepresentationContact>();

	MlcpPhysicsProblem mlcpPhysicsProblem;
	initializeMlcp(&mlcpPhysicsProblem, 11, 2);

	// Suppose 5 dof and 1 constraint are defined elsewhere.  Then H, CHt, HCHt, and b are prebuilt.
	Eigen::Matrix<double, 1, 5> localH;
	localH <<
		0.9478,  -0.3807,  0.5536, -0.6944,  0.1815;
	mlcpPhysicsProblem.H.block<1, 5>(0, 0) = localH;

	Eigen::Matrix<double, 5, 5> localC;
	localC <<
		-0.2294,  0.5160,  0.2520,  0.5941, -0.4854,
		 0.1233, -0.4433,  0.3679,  0.9307,  0.2600,
		 0.1988,  0.6637, -0.7591,  0.1475,  0.8517,
		-0.5495, -0.4305,  0.3162, -0.7862,  0.7627,
		-0.5754,  0.4108,  0.8445, -0.5565,  0.7150;
	localC = localC * localC.transpose(); // force to be symmetric

	Eigen::Matrix<double, 5, 1> localCHt = localC * localH.transpose();
	mlcpPhysicsProblem.CHt.block<5, 1>(0, 0) = localCHt;

	mlcpPhysicsProblem.A.block<1, 1>(0, 0) = localH * localCHt;

	mlcpPhysicsProblem.b.block<1, 1>(0, 0)[0] = 0.6991;

	// Place mass-spring at 5th dof and 1th constraint.
	unsigned int indexOfRepresentation = 5;
	unsigned int indexOfConstraint = 1;

	setContactAtNode(1);

	implementation->build(dt, m_constraintData, m_localization,
		&mlcpPhysicsProblem, indexOfRepresentation, indexOfConstraint, SurgSim::Physics::CONSTRAINT_POSITIVE_SIDE);

	// b -> E -> [#constraints, 1]
	EXPECT_NEAR(-9.81 * dt * dt * m_n[1], mlcpPhysicsProblem.b[indexOfConstraint], epsilon);

	// H -> [#constraints, #dof]
	EXPECT_NEAR(m_n[0], mlcpPhysicsProblem.H(indexOfConstraint, indexOfRepresentation + 3 * m_nodeId + 0), epsilon);
	EXPECT_NEAR(m_n[1], mlcpPhysicsProblem.H(indexOfConstraint, indexOfRepresentation + 3 * m_nodeId + 1), epsilon);
	EXPECT_NEAR(m_n[2], mlcpPhysicsProblem.H(indexOfConstraint, indexOfRepresentation + 3 * m_nodeId + 2), epsilon);

	// C -> [#dof, #dof]
	// CHt -> [#dof, #constraints]
	EXPECT_NEAR(dt / m_massPerNode * m_n[0],
		mlcpPhysicsProblem.CHt(indexOfRepresentation + 3 * m_nodeId + 0, indexOfConstraint), epsilon);
	EXPECT_NEAR(dt / m_massPerNode * m_n[1],
		mlcpPhysicsProblem.CHt(indexOfRepresentation + 3 * m_nodeId + 1, indexOfConstraint), epsilon);
	EXPECT_NEAR(dt / m_massPerNode * m_n[2],
		mlcpPhysicsProblem.CHt(indexOfRepresentation + 3 * m_nodeId + 2, indexOfConstraint), epsilon);

	// A -> HCHt -> [#constraints, #constraints]
	double calculatedA = mlcpPhysicsProblem.A.block<1, 1>(indexOfConstraint, indexOfConstraint)[0];
	EXPECT_NEAR(dt / m_massPerNode * (m_n[0] * m_n[0] + m_n[1] * m_n[1] + m_n[2] * m_n[2]), calculatedA, epsilon);
}

};  //  namespace Physics
};  //  namespace SurgSim
<|endoftext|>


<commit_before>#include "ground_level.hpp"
#include "species.hpp"

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp> // glm
#endif

// Include standard headers
#include <cmath>  // NAN, std::isnan, std::pow
#include <math.h> // fmax
#include <vector> // std::vector

namespace yli
{
    namespace ontology
    {
        float get_ground_level(
                const yli::ontology::Species* const terrain_species,
                const glm::vec3& position)
        {
            if (!terrain_species->is_terrain)
            {
                // if the current species is not a terrain `Species`, no collision detection to the ground will be performed.
                return NAN;
            }

            if ((position.x < 0.0f) || (position.x >= terrain_species->get_image_width()))
            {
                // if the x coordinate is out of range, no collision detection to the ground will be performed.
                return NAN;
            }

            if ((position.z < 0.0f) || (position.z >= terrain_species->get_image_height()))
            {
                // if the z coordinate is out of range, no collision detection to the ground will be performed.
                return NAN;
            }

            const float x = position.x / terrain_species->get_x_step();
            const float z = position.z / terrain_species->get_z_step();

            const std::size_t image_width = terrain_species->get_image_width();

            // compute the indices of closest vertices.
            std::size_t southwest_i = static_cast<std::size_t>(floor(z) * image_width + floor(x));
            std::size_t southeast_i = static_cast<std::size_t>(floor(z) * image_width + ceil(x));
            std::size_t northwest_i = static_cast<std::size_t>(ceil(z) * image_width + floor(x));
            std::size_t northeast_i = static_cast<std::size_t>(ceil(z) * image_width + ceil(x));

            const std::vector<glm::vec3>& vertices = terrain_species->get_vertices();

            // read closest the heights of closest integer coordinates to be used in bilinear interpolation.
            float southwest_height = vertices[southwest_i].y;
            float southeast_height = vertices[southeast_i].y;
            float northwest_height = vertices[northwest_i].y;
            float northeast_height = vertices[northeast_i].y;

            // these are not actually means but interpolations.
            // the result of the interpolation is mean if and only if (ceil(x) - x == 0.5) & (x - floor(x) == 0.5) , likewise for the z coordinate.
            float south_mean;
            float north_mean;
            float mean;

            if ((x - floor(x) < 0.01f) || (ceil(x) - x < 0.01f))
            {
                // if the x coordinate is too close to integer, the height of the closest vertex is used instead.
                south_mean = southwest_height;
                north_mean = northwest_height;
            }
            else
            {
                // the height is computed using bilinear interpolation.
                south_mean = ((ceil(x) - x) * southwest_height) + ((x - floor(x)) * southeast_height);
                north_mean = ((ceil(x) - x) * northwest_height) + ((x - floor(x)) * northeast_height);
            }

            if ((z - floor(z) < 0.01f) || (ceil(z) - z < 0.01f))
            {
                // if the z coordinate is too close to integer, the mean is used instead of bilinear interpolation.
                mean = (south_mean + north_mean) / 2.0f;
            }
            else
            {
                // the height is computed using bilinear interpolation.
                mean = ((ceil(z) - z) * south_mean) + ((z - floor(z)) * north_mean);
            }

            return mean;
        }

        float get_floor_level(
                const yli::ontology::Species* const terrain_species,
                const glm::vec3& position)
        {
            if (!terrain_species->is_terrain)
            {
                // if the current species is not a world species, no collision detection to the ground will be performed.
                return NAN;
            }

#define CHARACTER_RADIUS 1.0f
            float current_ground_level = get_ground_level(terrain_species, position);
            float x = position.x;
            float y = position.y;
            float z = position.z;

            // Get ground level at current location and +/- 1.0f.
            glm::vec3 south_position;
            south_position.x = x;
            south_position.y = y;
            south_position.z = z;
            south_position.z -= CHARACTER_RADIUS;
            float south_ground_level = get_ground_level(terrain_species, south_position);

            glm::vec3 west_position;
            west_position.x = x;
            west_position.y = y;
            west_position.z = z;
            west_position.x -= CHARACTER_RADIUS;
            float west_ground_level = get_ground_level(terrain_species, west_position);

            glm::vec3 north_position;
            north_position.x = x;
            north_position.y = y;
            north_position.z = z;
            north_position.z += CHARACTER_RADIUS;
            float north_ground_level = get_ground_level(terrain_species, north_position);

            glm::vec3 east_position;
            east_position.x = x;
            east_position.y = y;
            east_position.z = z;
            east_position.x += CHARACTER_RADIUS;
            float east_ground_level = get_ground_level(terrain_species, east_position);

            glm::vec3 southwest_position;
            southwest_position.x = south_position.x;
            southwest_position.y = south_position.y;
            southwest_position.z = south_position.z;
            southwest_position.x -= CHARACTER_RADIUS;
            float southwest_ground_level = get_ground_level(terrain_species, southwest_position);

            glm::vec3 southeast_position;
            southeast_position.x = south_position.x;
            southeast_position.y = south_position.y;
            southeast_position.z = south_position.z;
            southeast_position.x += CHARACTER_RADIUS;
            float southeast_ground_level = get_ground_level(terrain_species, southeast_position);

            glm::vec3 northwest_position = north_position;
            northwest_position.x = north_position.x;
            northwest_position.y = north_position.y;
            northwest_position.z = north_position.z;
            northwest_position.x -= CHARACTER_RADIUS;
            float northwest_ground_level = get_ground_level(terrain_species, northwest_position);

            glm::vec3 northeast_position = north_position;
            northeast_position.x = north_position.x;
            northeast_position.y = north_position.y;
            northeast_position.z = north_position.z;
            northeast_position.x += CHARACTER_RADIUS;
            float northeast_ground_level = get_ground_level(terrain_species, northeast_position);

            float temp_max_between_southwest_and_southeast = std::fmax(southwest_ground_level, southeast_ground_level);
            float temp_max_south = std::fmax(temp_max_between_southwest_and_southeast, south_ground_level);
            float temp_max_between_northwest_and_northeast = std::fmax(northwest_ground_level, northeast_ground_level);
            float temp_max_north = std::fmax(temp_max_between_northwest_and_northeast, north_ground_level);
            float temp_max_between_south_and_north = std::fmax(temp_max_south, temp_max_north);
            float temp_max_between_west_and_east = std::fmax(west_ground_level, east_ground_level);
            float temp_max_around_character = std::fmax(temp_max_between_south_and_north, temp_max_between_west_and_east);
            return std::fmax(temp_max_around_character, current_ground_level);
        }
    }
}
<commit_msg>`yli::ontology::get_ground_level`: added `const` modifiers to variables.<commit_after>#include "ground_level.hpp"
#include "species.hpp"

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp> // glm
#endif

// Include standard headers
#include <cmath>  // NAN, std::isnan, std::pow
#include <math.h> // fmax
#include <vector> // std::vector

namespace yli
{
    namespace ontology
    {
        float get_ground_level(
                const yli::ontology::Species* const terrain_species,
                const glm::vec3& position)
        {
            if (!terrain_species->is_terrain)
            {
                // if the current species is not a terrain `Species`, no collision detection to the ground will be performed.
                return NAN;
            }

            if ((position.x < 0.0f) || (position.x >= terrain_species->get_image_width()))
            {
                // if the x coordinate is out of range, no collision detection to the ground will be performed.
                return NAN;
            }

            if ((position.z < 0.0f) || (position.z >= terrain_species->get_image_height()))
            {
                // if the z coordinate is out of range, no collision detection to the ground will be performed.
                return NAN;
            }

            const float x = position.x / terrain_species->get_x_step();
            const float z = position.z / terrain_species->get_z_step();

            const std::size_t image_width = terrain_species->get_image_width();

            // compute the indices of closest vertices.
            const std::size_t southwest_i = static_cast<std::size_t>(floor(z) * image_width + floor(x));
            const std::size_t southeast_i = static_cast<std::size_t>(floor(z) * image_width + ceil(x));
            const std::size_t northwest_i = static_cast<std::size_t>(ceil(z) * image_width + floor(x));
            const std::size_t northeast_i = static_cast<std::size_t>(ceil(z) * image_width + ceil(x));

            const std::vector<glm::vec3>& vertices = terrain_species->get_vertices();

            // read closest the heights of closest integer coordinates to be used in bilinear interpolation.
            const float southwest_height = vertices[southwest_i].y;
            const float southeast_height = vertices[southeast_i].y;
            const float northwest_height = vertices[northwest_i].y;
            const float northeast_height = vertices[northeast_i].y;

            // these are not actually means but interpolations.
            // the result of the interpolation is mean if and only if (ceil(x) - x == 0.5) & (x - floor(x) == 0.5) , likewise for the z coordinate.
            float south_mean;
            float north_mean;
            float mean;

            if ((x - floor(x) < 0.01f) || (ceil(x) - x < 0.01f))
            {
                // if the x coordinate is too close to integer, the height of the closest vertex is used instead.
                south_mean = southwest_height;
                north_mean = northwest_height;
            }
            else
            {
                // the height is computed using bilinear interpolation.
                south_mean = ((ceil(x) - x) * southwest_height) + ((x - floor(x)) * southeast_height);
                north_mean = ((ceil(x) - x) * northwest_height) + ((x - floor(x)) * northeast_height);
            }

            if ((z - floor(z) < 0.01f) || (ceil(z) - z < 0.01f))
            {
                // if the z coordinate is too close to integer, the mean is used instead of bilinear interpolation.
                mean = (south_mean + north_mean) / 2.0f;
            }
            else
            {
                // the height is computed using bilinear interpolation.
                mean = ((ceil(z) - z) * south_mean) + ((z - floor(z)) * north_mean);
            }

            return mean;
        }

        float get_floor_level(
                const yli::ontology::Species* const terrain_species,
                const glm::vec3& position)
        {
            if (!terrain_species->is_terrain)
            {
                // if the current species is not a world species, no collision detection to the ground will be performed.
                return NAN;
            }

#define CHARACTER_RADIUS 1.0f
            float current_ground_level = get_ground_level(terrain_species, position);
            float x = position.x;
            float y = position.y;
            float z = position.z;

            // Get ground level at current location and +/- 1.0f.
            glm::vec3 south_position;
            south_position.x = x;
            south_position.y = y;
            south_position.z = z;
            south_position.z -= CHARACTER_RADIUS;
            float south_ground_level = get_ground_level(terrain_species, south_position);

            glm::vec3 west_position;
            west_position.x = x;
            west_position.y = y;
            west_position.z = z;
            west_position.x -= CHARACTER_RADIUS;
            float west_ground_level = get_ground_level(terrain_species, west_position);

            glm::vec3 north_position;
            north_position.x = x;
            north_position.y = y;
            north_position.z = z;
            north_position.z += CHARACTER_RADIUS;
            float north_ground_level = get_ground_level(terrain_species, north_position);

            glm::vec3 east_position;
            east_position.x = x;
            east_position.y = y;
            east_position.z = z;
            east_position.x += CHARACTER_RADIUS;
            float east_ground_level = get_ground_level(terrain_species, east_position);

            glm::vec3 southwest_position;
            southwest_position.x = south_position.x;
            southwest_position.y = south_position.y;
            southwest_position.z = south_position.z;
            southwest_position.x -= CHARACTER_RADIUS;
            float southwest_ground_level = get_ground_level(terrain_species, southwest_position);

            glm::vec3 southeast_position;
            southeast_position.x = south_position.x;
            southeast_position.y = south_position.y;
            southeast_position.z = south_position.z;
            southeast_position.x += CHARACTER_RADIUS;
            float southeast_ground_level = get_ground_level(terrain_species, southeast_position);

            glm::vec3 northwest_position = north_position;
            northwest_position.x = north_position.x;
            northwest_position.y = north_position.y;
            northwest_position.z = north_position.z;
            northwest_position.x -= CHARACTER_RADIUS;
            float northwest_ground_level = get_ground_level(terrain_species, northwest_position);

            glm::vec3 northeast_position = north_position;
            northeast_position.x = north_position.x;
            northeast_position.y = north_position.y;
            northeast_position.z = north_position.z;
            northeast_position.x += CHARACTER_RADIUS;
            float northeast_ground_level = get_ground_level(terrain_species, northeast_position);

            float temp_max_between_southwest_and_southeast = std::fmax(southwest_ground_level, southeast_ground_level);
            float temp_max_south = std::fmax(temp_max_between_southwest_and_southeast, south_ground_level);
            float temp_max_between_northwest_and_northeast = std::fmax(northwest_ground_level, northeast_ground_level);
            float temp_max_north = std::fmax(temp_max_between_northwest_and_northeast, north_ground_level);
            float temp_max_between_south_and_north = std::fmax(temp_max_south, temp_max_north);
            float temp_max_between_west_and_east = std::fmax(west_ground_level, east_ground_level);
            float temp_max_around_character = std::fmax(temp_max_between_south_and_north, temp_max_between_west_and_east);
            return std::fmax(temp_max_around_character, current_ground_level);
        }
    }
}
<|endoftext|>