<commit_before>// Copyright (c) 2012 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 "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "net/base/net_util.h"
#include "webkit/dom_storage/dom_storage_area.h"

namespace {

// The tests server is started separately for each test function (including PRE_
// functions). We need a test server which always uses the same port, so that
// the pages can be accessed with the same URLs after restoring the browser
// session.
class FixedPortTestServer : public net::LocalTestServer {
 public:
  explicit FixedPortTestServer(uint16 port)
      :  LocalTestServer(
             net::TestServer::TYPE_HTTP,
             net::TestServer::kLocalhost,
             FilePath().AppendASCII("chrome/test/data")) {
    BaseTestServer::SetPort(port);
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(FixedPortTestServer);
};

}  // namespace

class BetterSessionRestoreTest : public InProcessBrowserTest {
 public:
  BetterSessionRestoreTest()
      : test_server_(8001),
        title_pass_(ASCIIToUTF16("PASS")),
        title_storing_(ASCIIToUTF16("STORING")),
        title_error_write_failed_(ASCIIToUTF16("ERROR_WRITE_FAILED")),
        title_error_empty_(ASCIIToUTF16("ERROR_EMPTY")) {
    CHECK(test_server_.Start());
  }
 protected:
  void StoreDataWithPage(const std::string& filename) {
    GURL url = test_server_.GetURL("files/session_restore/" + filename);
    content::WebContents* web_contents =
        chrome::GetActiveWebContents(browser());
    content::TitleWatcher title_watcher(web_contents, title_storing_);
    title_watcher.AlsoWaitForTitle(title_pass_);
    title_watcher.AlsoWaitForTitle(title_error_write_failed_);
    title_watcher.AlsoWaitForTitle(title_error_empty_);
    ui_test_utils::NavigateToURL(browser(), url);
    string16 final_title = title_watcher.WaitAndGetTitle();
    EXPECT_EQ(title_storing_, final_title);
  }

  void CheckReloadedPage() {
    content::WebContents* web_contents = chrome::GetWebContentsAt(browser(), 0);
    content::TitleWatcher title_watcher(web_contents, title_pass_);
    title_watcher.AlsoWaitForTitle(title_storing_);
    title_watcher.AlsoWaitForTitle(title_error_write_failed_);
    title_watcher.AlsoWaitForTitle(title_error_empty_);
    // It's possible that the title was already the right one before
    // title_watcher was created.
    if (web_contents->GetTitle() != title_pass_) {
      string16 final_title = title_watcher.WaitAndGetTitle();
      EXPECT_EQ(title_pass_, final_title);
    }
  }

 private:
  FixedPortTestServer test_server_;
  string16 title_pass_;
  string16 title_storing_;
  string16 title_error_write_failed_;
  string16 title_error_empty_;

  DISALLOW_COPY_AND_ASSIGN(BetterSessionRestoreTest);
};

// crbug.com/156981
IN_PROC_BROWSER_TEST_F(BetterSessionRestoreTest, FLAKY_PRE_SessionCookies) {
  // Set the startup preference to "continue where I left off" and visit a page
  // which stores a session cookie.
  SessionStartupPref::SetStartupPref(
      browser()->profile(), SessionStartupPref(SessionStartupPref::LAST));
  StoreDataWithPage("session_cookies.html");
}

// crbug.com/156981
IN_PROC_BROWSER_TEST_F(BetterSessionRestoreTest, FLAKY_SessionCookies) {
  // The browsing session will be continued; just wait for the page to reload
  // and check the stored data.
  CheckReloadedPage();
}

// crbug.com/156981
IN_PROC_BROWSER_TEST_F(BetterSessionRestoreTest, FLAKY_PRE_SessionStorage) {
  // Write the data on disk less lazily.
  dom_storage::DomStorageArea::DisableCommitDelayForTesting();
  SessionStartupPref::SetStartupPref(
      browser()->profile(), SessionStartupPref(SessionStartupPref::LAST));
  StoreDataWithPage("session_storage.html");
}

// crbug.com/156981
IN_PROC_BROWSER_TEST_F(BetterSessionRestoreTest, FLAKY_SessionStorage) {
  CheckReloadedPage();
}
<commit_msg>Disable BetterSessionRestoreTest.* under AddressSanitizer<commit_after>// Copyright (c) 2012 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 "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "net/base/net_util.h"
#include "webkit/dom_storage/dom_storage_area.h"

namespace {

// The tests server is started separately for each test function (including PRE_
// functions). We need a test server which always uses the same port, so that
// the pages can be accessed with the same URLs after restoring the browser
// session.
class FixedPortTestServer : public net::LocalTestServer {
 public:
  explicit FixedPortTestServer(uint16 port)
      :  LocalTestServer(
             net::TestServer::TYPE_HTTP,
             net::TestServer::kLocalhost,
             FilePath().AppendASCII("chrome/test/data")) {
    BaseTestServer::SetPort(port);
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(FixedPortTestServer);
};

}  // namespace

class BetterSessionRestoreTest : public InProcessBrowserTest {
 public:
  BetterSessionRestoreTest()
      : test_server_(8001),
        title_pass_(ASCIIToUTF16("PASS")),
        title_storing_(ASCIIToUTF16("STORING")),
        title_error_write_failed_(ASCIIToUTF16("ERROR_WRITE_FAILED")),
        title_error_empty_(ASCIIToUTF16("ERROR_EMPTY")) {
    CHECK(test_server_.Start());
  }
 protected:
  void StoreDataWithPage(const std::string& filename) {
    GURL url = test_server_.GetURL("files/session_restore/" + filename);
    content::WebContents* web_contents =
        chrome::GetActiveWebContents(browser());
    content::TitleWatcher title_watcher(web_contents, title_storing_);
    title_watcher.AlsoWaitForTitle(title_pass_);
    title_watcher.AlsoWaitForTitle(title_error_write_failed_);
    title_watcher.AlsoWaitForTitle(title_error_empty_);
    ui_test_utils::NavigateToURL(browser(), url);
    string16 final_title = title_watcher.WaitAndGetTitle();
    EXPECT_EQ(title_storing_, final_title);
  }

  void CheckReloadedPage() {
    content::WebContents* web_contents = chrome::GetWebContentsAt(browser(), 0);
    content::TitleWatcher title_watcher(web_contents, title_pass_);
    title_watcher.AlsoWaitForTitle(title_storing_);
    title_watcher.AlsoWaitForTitle(title_error_write_failed_);
    title_watcher.AlsoWaitForTitle(title_error_empty_);
    // It's possible that the title was already the right one before
    // title_watcher was created.
    if (web_contents->GetTitle() != title_pass_) {
      string16 final_title = title_watcher.WaitAndGetTitle();
      EXPECT_EQ(title_pass_, final_title);
    }
  }

 private:
  FixedPortTestServer test_server_;
  string16 title_pass_;
  string16 title_storing_;
  string16 title_error_write_failed_;
  string16 title_error_empty_;

  DISALLOW_COPY_AND_ASSIGN(BetterSessionRestoreTest);
};

// BetterSessionRestoreTest tests fail under AddressSanitizer, see
// http://crbug.com/156444.
#if defined(ADDRESS_SANITIZER)
# define MAYBE_PRE_SessionCookies DISABLED_PRE_SessionCookies
# define MAYBE_SessionCookies DISABLED_SessionCookies
# define MAYBE_PRE_SessionStorage DISABLED_PRE_SessionStorage
# define MAYBE_SessionStorage DISABLED_SessionStorage
#else
# define MAYBE_PRE_SessionCookies FLAKY_PRE_SessionCookies
# define MAYBE_SessionCookies FLAKY_SessionCookies
# define MAYBE_PRE_SessionStorage FLAKY_PRE_SessionStorage
# define MAYBE_SessionStorage FLAKY_SessionStorage
#endif

// crbug.com/156981
IN_PROC_BROWSER_TEST_F(BetterSessionRestoreTest, MAYBE_PRE_SessionCookies) {
  // Set the startup preference to "continue where I left off" and visit a page
  // which stores a session cookie.
  SessionStartupPref::SetStartupPref(
      browser()->profile(), SessionStartupPref(SessionStartupPref::LAST));
  StoreDataWithPage("session_cookies.html");
}

// crbug.com/156981
IN_PROC_BROWSER_TEST_F(BetterSessionRestoreTest, MAYBE_SessionCookies) {
  // The browsing session will be continued; just wait for the page to reload
  // and check the stored data.
  CheckReloadedPage();
}

// crbug.com/156981
IN_PROC_BROWSER_TEST_F(BetterSessionRestoreTest, MAYBE_PRE_SessionStorage) {
  // Write the data on disk less lazily.
  dom_storage::DomStorageArea::DisableCommitDelayForTesting();
  SessionStartupPref::SetStartupPref(
      browser()->profile(), SessionStartupPref(SessionStartupPref::LAST));
  StoreDataWithPage("session_storage.html");
}

// crbug.com/156981
IN_PROC_BROWSER_TEST_F(BetterSessionRestoreTest, MAYBE_SessionStorage) {
  CheckReloadedPage();
}
<|endoftext|>


<commit_before>/**\file
 * \ingroup example-programmes
 * \brief "Hello World" HTTP Server
 *
 * An example HTTP server that serves a simple "Hello World!" on /, and a 404 on
 * all other resources.
 *
 * Call it like this:
 * \code
 * $ ./hello http:localhost:8080
 * \endcode
 *
 * With localhost and 8080 being a host name and port of your choosing. Then,
 * while the programme is running, open a browser and go to
 * http://localhost:8080/ and you should see the familiar greeting.
 *
 * \copyright
 * Copyright (c) 2015, Magnus Achim Deininger <magnus@ef.gy>
 * \copyright
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * \copyright
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * \copyright
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * \see Documentation: https://ef.gy/documentation/imperiald
 * \see Source Code: https://github.com/jyujin/imperiald
 * \see Licence Terms: https://github.com/jyujin/imperiald/COPYING
 */

#define ASIO_DISABLE_THREADS
#include <prometheus/http.h>
#include <imperiald/procfs-linux.h>

using namespace efgy;
using namespace prometheus;
using asio::ip::tcp;
using asio::local::stream_protocol;

static imperiald::linux::stat<> linux_procfs_stats(1);

/**\brief Hello World request handler
 *
 * This function serves the familiar "Hello World!" when called.
 *
 * \param[out] session The HTTP session to answer on.
 *
 * \returns true (always, as we always reply).
 */
template <class transport>
static bool hello(typename net::http::server<transport>::session &session,
                  std::smatch &) {
  session.reply(200, "Hello World!");

  return true;
}

/**\brief Hello World request handler for /quit
 *
 * When this handler is invoked, it stops the ASIO IO handler (after replying,
 * maybe...).
 *
 * \note Having this on your production server in this exact way is PROBABLY a
 *       really bad idea, unless you gate it in an upstream forward proxy. Or
 *       you have some way of automatically respawning your server. Or both.
 *
 * \param[out] session The HTTP session to answer on.
 *
 * \returns true (always, as we always reply).
 */
template <class transport>
static bool quit(typename net::http::server<transport>::session &session,
                 std::smatch &) {
  session.reply(200, "Good-Bye, Cruel World!");

  session.server.io.get().stop();

  return true;
}

template <class sock>
static std::size_t setup(net::endpoint<sock> lookup,
                         io::service &service = io::service::common()) {
  return lookup.with([&service](typename sock::endpoint &endpoint) -> bool {
    net::http::server<sock> *s = new net::http::server<sock>(endpoint, service);

    s->processor.add("^/$", hello<sock>);
    s->processor.add("^/quit$", quit<sock>);
    s->processor.add(http::regex, http::common<sock>);

    return true;
  });
}

/**\brief Main function for the HTTP/IRC demo
 *
 * Main function for the network server hello world programme.
 *
 * Sets up server(s) as per the given command line arguments. Invalid arguments
 * are ignored.
 *
 * \param[in] argc Process argument count.
 * \param[in] argv Process argument vector
 *
 * \returns 0 when nothing bad happened, 1 otherwise.
 */
int main(int argc, char *argv[]) {
  try {
    int targets = 0;

    if (argc < 2) {
      std::cerr << "Usage: server [http:<host>:<port>|http:unix:<path>]...\n";
      return 1;
    }

    for (unsigned int i = 1; i < argc; i++) {
      static const std::regex http("http:(.+):([0-9]+)");
      static const std::regex httpSocket("http:unix:(.+)");
      std::smatch matches;

      if (std::regex_match(std::string(argv[i]), matches, httpSocket)) {
        targets += setup(net::endpoint<stream_protocol>(matches[1]));
      } else if (std::regex_match(std::string(argv[i]), matches, http)) {
        targets += setup(net::endpoint<tcp>(matches[1], matches[2]));
      } else {
        std::cerr << "Argument not recognised: " << argv[i] << "\n";
      }
    }

    if (targets > 0) {
      io::service::common().run();
    }

    return 0;
  } catch (std::exception &e) {
    std::cerr << "Exception: " << e.what() << "\n";
  } catch (std::system_error &e) {
    std::cerr << "System Error: " << e.what() << "\n";
  }

  return 1;
}
<commit_msg>upgrade to the latest command line parsing shiny<commit_after>/**\file
 * \ingroup example-programmes
 * \brief "Hello World" HTTP Server
 *
 * An example HTTP server that serves a simple "Hello World!" on /, and a 404 on
 * all other resources.
 *
 * Call it like this:
 * \code
 * $ ./hello http:localhost:8080
 * \endcode
 *
 * With localhost and 8080 being a host name and port of your choosing. Then,
 * while the programme is running, open a browser and go to
 * http://localhost:8080/ and you should see the familiar greeting.
 *
 * \copyright
 * Copyright (c) 2015, Magnus Achim Deininger <magnus@ef.gy>
 * \copyright
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * \copyright
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * \copyright
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * \see Documentation: https://ef.gy/documentation/imperiald
 * \see Source Code: https://github.com/jyujin/imperiald
 * \see Licence Terms: https://github.com/jyujin/imperiald/COPYING
 */

#define ASIO_DISABLE_THREADS
#include <prometheus/http.h>
#include <imperiald/procfs-linux.h>
#include <ef.gy/cli.h>

using namespace efgy;
using namespace prometheus;
using asio::ip::tcp;
using asio::local::stream_protocol;

static imperiald::linux::stat<> linux_procfs_stats(1);

/**\brief Hello World request handler
 *
 * This function serves the familiar "Hello World!" when called.
 *
 * \param[out] session The HTTP session to answer on.
 *
 * \returns true (always, as we always reply).
 */
template <class transport>
static bool hello(typename net::http::server<transport>::session &session,
                  std::smatch &) {
  session.reply(200, "Hello World!");

  return true;
}

/**\brief Hello World request handler for /quit
 *
 * When this handler is invoked, it stops the ASIO IO handler (after replying,
 * maybe...).
 *
 * \note Having this on your production server in this exact way is PROBABLY a
 *       really bad idea, unless you gate it in an upstream forward proxy. Or
 *       you have some way of automatically respawning your server. Or both.
 *
 * \param[out] session The HTTP session to answer on.
 *
 * \returns true (always, as we always reply).
 */
template <class transport>
static bool quit(typename net::http::server<transport>::session &session,
                 std::smatch &) {
  session.reply(200, "Good-Bye, Cruel World!");

  session.server.io.get().stop();

  return true;
}

template <class sock>
static std::size_t setup(net::endpoint<sock> lookup,
                         io::service &service = io::service::common()) {
  return lookup.with([&service](typename sock::endpoint &endpoint) -> bool {
    net::http::server<sock> *s = new net::http::server<sock>(endpoint, service);

    s->processor.add("^/$", hello<sock>);
    s->processor.add("^/quit$", quit<sock>);
    s->processor.add(http::regex, http::common<sock>);

    return true;
  });
}

static cli::option oHTTPSocket(std::regex("http:unix:(.+)"), [](std::smatch &m)->bool {
  return setup(net::endpoint<stream_protocol>(m[1])) > 0;
});

static cli::option oHTTP(std::regex("http:(.+):([0-9]+)"), [](std::smatch &m)->bool {
  return setup(net::endpoint<tcp>(m[1], m[2])) > 0;
});

/**\brief Main function for the HTTP/IRC demo
 *
 * Main function for the network server hello world programme.
 *
 * Sets up server(s) as per the given command line arguments. Invalid arguments
 * are ignored.
 *
 * \param[in] argc Process argument count.
 * \param[in] argv Process argument vector
 *
 * \returns 0 when nothing bad happened, 1 otherwise.
 */
int main(int argc, char *argv[]) {
  try {
    int targets = 0;

    if (cli::options<cli::option>::common().apply(argc, argv) > 0) {
      io::service::common().run();
    } else {
      std::cerr << "Usage: server [http:<host>:<port>|http:unix:<path>]...\n";
      return 1;
    }

    return 0;
  } catch (std::exception &e) {
    std::cerr << "Exception: " << e.what() << "\n";
  } catch (std::system_error &e) {
    std::cerr << "System Error: " << e.what() << "\n";
  }

  return 1;
}
<|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/sync/notifier/registration_manager.h"

#include <cstddef>
#include <deque>
#include <vector>

#include "base/basictypes.h"
#include "chrome/browser/sync/notifier/invalidation_util.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "google/cacheinvalidation/invalidation-client.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace sync_notifier {
namespace {

// Fake invalidation client that just stores the args of calls to
// Register().
class FakeInvalidationClient : public invalidation::InvalidationClient {
 public:
  FakeInvalidationClient() {}

  virtual ~FakeInvalidationClient() {}

  virtual void Register(const invalidation::ObjectId& oid) {
    registered_oids.push_back(oid);
  }

  virtual void Unregister(const invalidation::ObjectId& oid) {
    ADD_FAILURE();
  }

  virtual invalidation::NetworkEndpoint* network_endpoint() {
    ADD_FAILURE();
    return NULL;
  }

  std::deque<invalidation::ObjectId> registered_oids;

 private:
  DISALLOW_COPY_AND_ASSIGN(FakeInvalidationClient);
};

class RegistrationManagerTest : public testing::Test {
 protected:
  RegistrationManagerTest()
      : registration_manager_(&fake_invalidation_client_) {}

  virtual ~RegistrationManagerTest() {}

  FakeInvalidationClient fake_invalidation_client_;
  RegistrationManager registration_manager_;

 private:
  DISALLOW_COPY_AND_ASSIGN(RegistrationManagerTest);
};

invalidation::ObjectId ModelTypeToObjectId(
    syncable::ModelType model_type) {
  invalidation::ObjectId object_id;
  EXPECT_TRUE(RealModelTypeToObjectId(model_type, &object_id));
  return object_id;
}

syncable::ModelType ObjectIdToModelType(
    const invalidation::ObjectId& object_id) {
  syncable::ModelType model_type = syncable::UNSPECIFIED;
  EXPECT_TRUE(ObjectIdToRealModelType(object_id, &model_type));
  return model_type;
}

invalidation::RegistrationUpdateResult MakeRegistrationUpdateResult(
    syncable::ModelType model_type) {
  invalidation::RegistrationUpdateResult result;
  result.mutable_operation()->
      set_type(invalidation::RegistrationUpdate::REGISTER);
  *result.mutable_operation()->mutable_object_id() =
      ModelTypeToObjectId(model_type);
  result.mutable_status()->set_code(invalidation::Status::SUCCESS);
  return result;
}

TEST_F(RegistrationManagerTest, RegisterType) {
  const syncable::ModelType kModelTypes[] = {
    syncable::BOOKMARKS,
    syncable::PREFERENCES,
    syncable::THEMES,
    syncable::AUTOFILL,
    syncable::EXTENSIONS,
  };
  const size_t kModelTypeCount = arraysize(kModelTypes);

  // Register types.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    // Register twice; it shouldn't matter.
    registration_manager_.RegisterType(kModelTypes[i]);
    registration_manager_.RegisterType(kModelTypes[i]);
  }

  ASSERT_EQ(kModelTypeCount,
            fake_invalidation_client_.registered_oids.size());

  // Everything should be registered.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
  }

  // Check object IDs.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    EXPECT_EQ(kModelTypes[i],
              ObjectIdToModelType(
                  fake_invalidation_client_.registered_oids[i]));
  }
}

TEST_F(RegistrationManagerTest, MarkRegistrationLost) {
  const syncable::ModelType kModelTypes[] = {
    syncable::BOOKMARKS,
    syncable::PREFERENCES,
    syncable::THEMES,
    syncable::AUTOFILL,
    syncable::EXTENSIONS,
  };
  const size_t kModelTypeCount = arraysize(kModelTypes);

  // Register types.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    registration_manager_.RegisterType(kModelTypes[i]);
  }

  ASSERT_EQ(kModelTypeCount,
            fake_invalidation_client_.registered_oids.size());

  // All should be registered.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
  }

  // Mark the registrations of all but the first one lost.
  for (size_t i = 1; i < kModelTypeCount; ++i) {
    registration_manager_.MarkRegistrationLost(kModelTypes[i]);
  }

  ASSERT_EQ(2 * kModelTypeCount - 1,
            fake_invalidation_client_.registered_oids.size());

  // All should still be registered.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
  }
}

TEST_F(RegistrationManagerTest, MarkAllRegistrationsLost) {
  const syncable::ModelType kModelTypes[] = {
    syncable::BOOKMARKS,
    syncable::PREFERENCES,
    syncable::THEMES,
    syncable::AUTOFILL,
    syncable::EXTENSIONS,
  };
  const size_t kModelTypeCount = arraysize(kModelTypes);

  // Register types.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    registration_manager_.RegisterType(kModelTypes[i]);
  }

  ASSERT_EQ(kModelTypeCount,
            fake_invalidation_client_.registered_oids.size());

  // All should be registered.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
  }

  // Mark the registrations of all but the first one lost.  Then mark
  // everything lost.
  for (size_t i = 1; i < kModelTypeCount; ++i) {
    registration_manager_.MarkRegistrationLost(kModelTypes[i]);
  }
  registration_manager_.MarkAllRegistrationsLost();

  ASSERT_EQ(3 * kModelTypeCount - 1,
            fake_invalidation_client_.registered_oids.size());

  // All should still be registered.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
  }
}

}  // namespace
}  // namespace notifier
<commit_msg>[Sync] Removed unused function causing clang build to fail<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/sync/notifier/registration_manager.h"

#include <cstddef>
#include <deque>
#include <vector>

#include "base/basictypes.h"
#include "chrome/browser/sync/notifier/invalidation_util.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "google/cacheinvalidation/invalidation-client.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace sync_notifier {
namespace {

// Fake invalidation client that just stores the args of calls to
// Register().
class FakeInvalidationClient : public invalidation::InvalidationClient {
 public:
  FakeInvalidationClient() {}

  virtual ~FakeInvalidationClient() {}

  virtual void Register(const invalidation::ObjectId& oid) {
    registered_oids.push_back(oid);
  }

  virtual void Unregister(const invalidation::ObjectId& oid) {
    ADD_FAILURE();
  }

  virtual invalidation::NetworkEndpoint* network_endpoint() {
    ADD_FAILURE();
    return NULL;
  }

  std::deque<invalidation::ObjectId> registered_oids;

 private:
  DISALLOW_COPY_AND_ASSIGN(FakeInvalidationClient);
};

class RegistrationManagerTest : public testing::Test {
 protected:
  RegistrationManagerTest()
      : registration_manager_(&fake_invalidation_client_) {}

  virtual ~RegistrationManagerTest() {}

  FakeInvalidationClient fake_invalidation_client_;
  RegistrationManager registration_manager_;

 private:
  DISALLOW_COPY_AND_ASSIGN(RegistrationManagerTest);
};

invalidation::ObjectId ModelTypeToObjectId(
    syncable::ModelType model_type) {
  invalidation::ObjectId object_id;
  EXPECT_TRUE(RealModelTypeToObjectId(model_type, &object_id));
  return object_id;
}

syncable::ModelType ObjectIdToModelType(
    const invalidation::ObjectId& object_id) {
  syncable::ModelType model_type = syncable::UNSPECIFIED;
  EXPECT_TRUE(ObjectIdToRealModelType(object_id, &model_type));
  return model_type;
}

TEST_F(RegistrationManagerTest, RegisterType) {
  const syncable::ModelType kModelTypes[] = {
    syncable::BOOKMARKS,
    syncable::PREFERENCES,
    syncable::THEMES,
    syncable::AUTOFILL,
    syncable::EXTENSIONS,
  };
  const size_t kModelTypeCount = arraysize(kModelTypes);

  // Register types.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    // Register twice; it shouldn't matter.
    registration_manager_.RegisterType(kModelTypes[i]);
    registration_manager_.RegisterType(kModelTypes[i]);
  }

  ASSERT_EQ(kModelTypeCount,
            fake_invalidation_client_.registered_oids.size());

  // Everything should be registered.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
  }

  // Check object IDs.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    EXPECT_EQ(kModelTypes[i],
              ObjectIdToModelType(
                  fake_invalidation_client_.registered_oids[i]));
  }
}

TEST_F(RegistrationManagerTest, MarkRegistrationLost) {
  const syncable::ModelType kModelTypes[] = {
    syncable::BOOKMARKS,
    syncable::PREFERENCES,
    syncable::THEMES,
    syncable::AUTOFILL,
    syncable::EXTENSIONS,
  };
  const size_t kModelTypeCount = arraysize(kModelTypes);

  // Register types.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    registration_manager_.RegisterType(kModelTypes[i]);
  }

  ASSERT_EQ(kModelTypeCount,
            fake_invalidation_client_.registered_oids.size());

  // All should be registered.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
  }

  // Mark the registrations of all but the first one lost.
  for (size_t i = 1; i < kModelTypeCount; ++i) {
    registration_manager_.MarkRegistrationLost(kModelTypes[i]);
  }

  ASSERT_EQ(2 * kModelTypeCount - 1,
            fake_invalidation_client_.registered_oids.size());

  // All should still be registered.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
  }
}

TEST_F(RegistrationManagerTest, MarkAllRegistrationsLost) {
  const syncable::ModelType kModelTypes[] = {
    syncable::BOOKMARKS,
    syncable::PREFERENCES,
    syncable::THEMES,
    syncable::AUTOFILL,
    syncable::EXTENSIONS,
  };
  const size_t kModelTypeCount = arraysize(kModelTypes);

  // Register types.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    registration_manager_.RegisterType(kModelTypes[i]);
  }

  ASSERT_EQ(kModelTypeCount,
            fake_invalidation_client_.registered_oids.size());

  // All should be registered.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
  }

  // Mark the registrations of all but the first one lost.  Then mark
  // everything lost.
  for (size_t i = 1; i < kModelTypeCount; ++i) {
    registration_manager_.MarkRegistrationLost(kModelTypes[i]);
  }
  registration_manager_.MarkAllRegistrationsLost();

  ASSERT_EQ(3 * kModelTypeCount - 1,
            fake_invalidation_client_.registered_oids.size());

  // All should still be registered.
  for (size_t i = 0; i < kModelTypeCount; ++i) {
    EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
  }
}

}  // namespace
}  // namespace notifier
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2013-2018 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <set>
#include <thread>

#include "cpp_utils/assert.hpp"

#include "server.hpp"
#include "expenses.hpp"
#include "earnings.hpp"
#include "accounts.hpp"
#include "incomes.hpp"
#include "assets.hpp"
#include "config.hpp"
#include "objectives.hpp"
#include "wishes.hpp"
#include "fortune.hpp"
#include "recurring.hpp"
#include "debts.hpp"
#include "currency.hpp"
#include "share.hpp"
#include "http.hpp"

#include "api/server_api.hpp"
#include "pages/server_pages.hpp"

using namespace budget;

namespace {

bool server_running = false;

httplib::Server * server_ptr = nullptr;
volatile bool cron = true;

void server_signal_handler(int signum) {
    std::cout << "INFO: Received signal (" << signum << ")" << std::endl;

    cron = false;

    if (server_ptr) {
        server_ptr->stop();
    }
}

void install_signal_handler() {
    struct sigaction action;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    action.sa_handler = server_signal_handler;
    sigaction(SIGTERM, &action, NULL);
    sigaction(SIGINT, &action, NULL);

    std::cout << "INFO: Installed the signal handler" << std::endl;
}

void start_server(){
    std::cout << "INFO: Started the server thread" << std::endl;

    httplib::Server server;

    load_pages(server);
    load_api(server);

    install_signal_handler();

    auto port = get_server_port();
    auto listen = get_server_listen();
    server_ptr = &server;

    // Listen
    std::cout << "INFO: Server is starting to listen on " << listen << ':' << port << std::endl;
    server.listen(listen.c_str(), port);
    std::cout << "INFO: Server has exited" << std::endl;
}

void start_cron_loop(){
    std::cout << "INFO: Started the cron thread" << std::endl;
    size_t hours = 0;

    while(cron){
        using namespace std::chrono_literals;

        std::this_thread::sleep_for(1h);
        ++hours;

        check_for_recurrings();

        // We save the cache once per day
        if (hours % 24 == 0) {
            save_currency_cache();
            save_share_price_cache();
        }

        // Every four hours, we refresh the currency cache
        // Only current day rates are refreshed
        if (hours % 4 == 0) {
            std::cout << "Refresh the currency cache" << std::endl;
            budget::refresh_currency_cache();
        }

        // Every hour, we try to prefetch value for new days
        std::cout << "Prefetch the share cache" << std::endl;
        budget::prefetch_share_price_cache();
    }

    std::cout << "INFO: Cron Thread has exited" << std::endl;
}

} //end of anonymous namespace

void budget::set_server_running(){
    // Indicates to the system that it's running in server mode
    server_running = true;
}

void budget::server_module::load(){
    load_accounts();
    load_incomes();
    load_expenses();
    load_earnings();
    load_assets();
    load_objectives();
    load_wishes();
    load_fortunes();
    load_recurrings();
    load_debts();
    load_wishes();
}

void budget::server_module::handle(const std::vector<std::string>& args){
    cpp_unused(args);

    std::cout << "Starting the threads" << std::endl;

    std::thread server_thread([](){ start_server(); });
    std::thread cron_thread([](){ start_cron_loop(); });

    server_thread.join();
    cron_thread.join();
}

bool budget::is_server_running(){
    return server_running;
}
<commit_msg>Cleanup server starting<commit_after>//=======================================================================
// Copyright (c) 2013-2018 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <set>
#include <thread>

#include "cpp_utils/assert.hpp"

#include "server.hpp"
#include "expenses.hpp"
#include "earnings.hpp"
#include "accounts.hpp"
#include "incomes.hpp"
#include "assets.hpp"
#include "config.hpp"
#include "objectives.hpp"
#include "wishes.hpp"
#include "fortune.hpp"
#include "recurring.hpp"
#include "debts.hpp"
#include "currency.hpp"
#include "share.hpp"
#include "http.hpp"

#include "api/server_api.hpp"
#include "pages/server_pages.hpp"

using namespace budget;

namespace {

bool server_running = false;

httplib::Server * server_ptr = nullptr;
volatile bool cron = true;

void server_signal_handler(int signum) {
    std::cout << "INFO: Received signal (" << signum << ")" << std::endl;

    cron = false;

    if (server_ptr) {
        server_ptr->stop();
    }
}

void install_signal_handler() {
    struct sigaction action;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    action.sa_handler = server_signal_handler;
    sigaction(SIGTERM, &action, NULL);
    sigaction(SIGINT, &action, NULL);

    std::cout << "INFO: Installed the signal handler" << std::endl;
}

bool start_server(){
    std::cout << "INFO: Started the server thread" << std::endl;

    httplib::Server server;

    load_pages(server);
    load_api(server);

    install_signal_handler();

    auto port = get_server_port();
    auto listen = get_server_listen();
    server_ptr = &server;

    // Listen
    std::cout << "INFO: Server is starting to listen on " << listen << ':' << port << std::endl;
    if (!server.listen(listen.c_str(), port)) {
        std::cerr << "INFO: Server failed to start" << std::endl;
        return false;
    }

    std::cout << "INFO: Server has exited normally" << std::endl;
    return true;
}

void start_cron_loop(){
    std::cout << "INFO: Started the cron thread" << std::endl;
    size_t hours = 0;

    while(cron){
        using namespace std::chrono_literals;

        std::this_thread::sleep_for(1h);
        ++hours;

        check_for_recurrings();

        // We save the cache once per day
        if (hours % 24 == 0) {
            save_currency_cache();
            save_share_price_cache();
        }

        // Every four hours, we refresh the currency cache
        // Only current day rates are refreshed
        if (hours % 4 == 0) {
            std::cout << "Refresh the currency cache" << std::endl;
            budget::refresh_currency_cache();
        }

        // Every hour, we try to prefetch value for new days
        std::cout << "Prefetch the share cache" << std::endl;
        budget::prefetch_share_price_cache();
    }

    std::cout << "INFO: Cron Thread has exited" << std::endl;
}

} //end of anonymous namespace

void budget::set_server_running(){
    // Indicates to the system that it's running in server mode
    server_running = true;
}

void budget::server_module::load(){
    load_accounts();
    load_incomes();
    load_expenses();
    load_earnings();
    load_assets();
    load_objectives();
    load_wishes();
    load_fortunes();
    load_recurrings();
    load_debts();
    load_wishes();
}

void budget::server_module::handle(const std::vector<std::string>& args){
    cpp_unused(args);

    std::cout << "Starting the threads" << std::endl;

    volatile bool success = false;
    std::thread server_thread([&success](){ success = start_server(); });
    std::thread cron_thread([](){ start_cron_loop(); });

    server_thread.join();

    if (!success) {
        cron = false;
    }

    cron_thread.join();
}

bool budget::is_server_running(){
    return server_running;
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 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/ui/views/crypto_module_password_dialog_view.h"

#include "base/basictypes.h"
#include "base/bind.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/ui/views/window.h"
#include "googleurl/src/gurl.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/views/controls/button/text_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/layout/layout_constants.h"
#include "ui/views/widget/widget.h"

int kInputPasswordMinWidth = 8;

namespace browser {

// CryptoModulePasswordDialogView
////////////////////////////////////////////////////////////////////////////////
CryptoModulePasswordDialogView::CryptoModulePasswordDialogView(
    const std::string& slot_name,
    browser::CryptoModulePasswordReason reason,
    const std::string& server,
    const base::Callback<void(const char*)>& callback)
    : callback_(callback) {
  Init(server, slot_name, reason);
}

CryptoModulePasswordDialogView::~CryptoModulePasswordDialogView() {
}

void CryptoModulePasswordDialogView::Init(
    const std::string& server,
    const std::string& slot_name,
    browser::CryptoModulePasswordReason reason) {
  // Select an appropriate text for the reason.
  std::string text;
  const string16& server16 = UTF8ToUTF16(server);
  const string16& slot16 = UTF8ToUTF16(slot_name);
  switch (reason) {
    case browser::kCryptoModulePasswordKeygen:
      text = l10n_util::GetStringFUTF8(
          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_KEYGEN, slot16, server16);
      break;
    case browser::kCryptoModulePasswordCertEnrollment:
      text = l10n_util::GetStringFUTF8(
          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_CERT_ENROLLMENT, slot16, server16);
      break;
    case browser::kCryptoModulePasswordClientAuth:
      text = l10n_util::GetStringFUTF8(
          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_CLIENT_AUTH, slot16, server16);
      break;
    case browser::kCryptoModulePasswordListCerts:
      text = l10n_util::GetStringFUTF8(
          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_LIST_CERTS, slot16);
      break;
    case browser::kCryptoModulePasswordCertImport:
      text = l10n_util::GetStringFUTF8(
          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_CERT_IMPORT, slot16);
      break;
    case browser::kCryptoModulePasswordCertExport:
      text = l10n_util::GetStringFUTF8(
          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_CERT_EXPORT, slot16);
      break;
    default:
      NOTREACHED();
  }
  reason_label_ = new views::Label(UTF8ToUTF16(text));
  reason_label_->SetMultiLine(true);

  password_label_ = new views::Label(l10n_util::GetStringUTF16(
      IDS_CRYPTO_MODULE_AUTH_DIALOG_PASSWORD_FIELD));

  password_entry_ = new views::Textfield(views::Textfield::STYLE_OBSCURED);
  password_entry_->SetController(this);

  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
  SetLayoutManager(layout);

  views::ColumnSet* reason_column_set = layout->AddColumnSet(0);
  reason_column_set->AddColumn(
      views::GridLayout::LEADING, views::GridLayout::LEADING, 1,
      views::GridLayout::USE_PREF, 0, 0);

  views::ColumnSet* column_set = layout->AddColumnSet(1);
  column_set->AddColumn(views::GridLayout::LEADING,
                        views::GridLayout::LEADING, 0,
                        views::GridLayout::USE_PREF, 0, 0);
  column_set->AddPaddingColumn(
      0, views::kUnrelatedControlLargeHorizontalSpacing);
  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0, 0);

  layout->StartRow(0, 0);
  layout->AddView(reason_label_);
  layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);

  layout->StartRow(0, 1);
  layout->AddView(password_label_);
  layout->AddView(password_entry_);
}

views::View* CryptoModulePasswordDialogView::GetInitiallyFocusedView() {
  return password_entry_;
}
ui::ModalType CryptoModulePasswordDialogView::GetModalType() const {
  return ui::MODAL_TYPE_WINDOW;
}
views::View* CryptoModulePasswordDialogView::GetContentsView() {
  return this;
}

string16 CryptoModulePasswordDialogView::GetDialogButtonLabel(
    ui::DialogButton button) const {
  return l10n_util::GetStringUTF16(button == ui::DIALOG_BUTTON_OK ?
      IDS_CRYPTO_MODULE_AUTH_DIALOG_OK_BUTTON_LABEL : IDS_CANCEL);
}

bool CryptoModulePasswordDialogView::Accept() {
  callback_.Run(UTF16ToUTF8(password_entry_->text()).c_str());
  const string16 empty;
  password_entry_->SetText(empty);
  return true;
}

bool CryptoModulePasswordDialogView::Cancel() {
  callback_.Run(static_cast<const char*>(NULL));
  const string16 empty;
  password_entry_->SetText(empty);
  return true;
}

bool CryptoModulePasswordDialogView::HandleKeyEvent(
    views::Textfield* sender,
    const views::KeyEvent& keystroke) {
  return false;
}

void CryptoModulePasswordDialogView::ContentsChanged(
    views::Textfield* sender,
    const string16& new_contents) {
}

string16 CryptoModulePasswordDialogView::GetWindowTitle() const {
  return l10n_util::GetStringUTF16(IDS_CRYPTO_MODULE_AUTH_DIALOG_TITLE);
}

void ShowCryptoModulePasswordDialog(
    const std::string& slot_name,
    bool retry,
    CryptoModulePasswordReason reason,
    const std::string& server,
    const CryptoModulePasswordCallback& callback) {
  CryptoModulePasswordDialogView* dialog =
      new CryptoModulePasswordDialogView(slot_name, reason, server, callback);
  views::Widget::CreateWindow(dialog)->Show();
}

}  // namespace browser
<commit_msg>views: Remove unused const from crypto module password dialog.<commit_after>// Copyright (c) 2012 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/ui/views/crypto_module_password_dialog_view.h"

#include "base/basictypes.h"
#include "base/bind.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/ui/views/window.h"
#include "googleurl/src/gurl.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/views/controls/button/text_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/layout/layout_constants.h"
#include "ui/views/widget/widget.h"

namespace browser {

// CryptoModulePasswordDialogView
////////////////////////////////////////////////////////////////////////////////
CryptoModulePasswordDialogView::CryptoModulePasswordDialogView(
    const std::string& slot_name,
    browser::CryptoModulePasswordReason reason,
    const std::string& server,
    const base::Callback<void(const char*)>& callback)
    : callback_(callback) {
  Init(server, slot_name, reason);
}

CryptoModulePasswordDialogView::~CryptoModulePasswordDialogView() {
}

void CryptoModulePasswordDialogView::Init(
    const std::string& server,
    const std::string& slot_name,
    browser::CryptoModulePasswordReason reason) {
  // Select an appropriate text for the reason.
  std::string text;
  const string16& server16 = UTF8ToUTF16(server);
  const string16& slot16 = UTF8ToUTF16(slot_name);
  switch (reason) {
    case browser::kCryptoModulePasswordKeygen:
      text = l10n_util::GetStringFUTF8(
          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_KEYGEN, slot16, server16);
      break;
    case browser::kCryptoModulePasswordCertEnrollment:
      text = l10n_util::GetStringFUTF8(
          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_CERT_ENROLLMENT, slot16, server16);
      break;
    case browser::kCryptoModulePasswordClientAuth:
      text = l10n_util::GetStringFUTF8(
          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_CLIENT_AUTH, slot16, server16);
      break;
    case browser::kCryptoModulePasswordListCerts:
      text = l10n_util::GetStringFUTF8(
          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_LIST_CERTS, slot16);
      break;
    case browser::kCryptoModulePasswordCertImport:
      text = l10n_util::GetStringFUTF8(
          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_CERT_IMPORT, slot16);
      break;
    case browser::kCryptoModulePasswordCertExport:
      text = l10n_util::GetStringFUTF8(
          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_CERT_EXPORT, slot16);
      break;
    default:
      NOTREACHED();
  }
  reason_label_ = new views::Label(UTF8ToUTF16(text));
  reason_label_->SetMultiLine(true);

  password_label_ = new views::Label(l10n_util::GetStringUTF16(
      IDS_CRYPTO_MODULE_AUTH_DIALOG_PASSWORD_FIELD));

  password_entry_ = new views::Textfield(views::Textfield::STYLE_OBSCURED);
  password_entry_->SetController(this);

  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
  SetLayoutManager(layout);

  views::ColumnSet* reason_column_set = layout->AddColumnSet(0);
  reason_column_set->AddColumn(
      views::GridLayout::LEADING, views::GridLayout::LEADING, 1,
      views::GridLayout::USE_PREF, 0, 0);

  views::ColumnSet* column_set = layout->AddColumnSet(1);
  column_set->AddColumn(views::GridLayout::LEADING,
                        views::GridLayout::LEADING, 0,
                        views::GridLayout::USE_PREF, 0, 0);
  column_set->AddPaddingColumn(
      0, views::kUnrelatedControlLargeHorizontalSpacing);
  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0, 0);

  layout->StartRow(0, 0);
  layout->AddView(reason_label_);
  layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);

  layout->StartRow(0, 1);
  layout->AddView(password_label_);
  layout->AddView(password_entry_);
}

views::View* CryptoModulePasswordDialogView::GetInitiallyFocusedView() {
  return password_entry_;
}

ui::ModalType CryptoModulePasswordDialogView::GetModalType() const {
  return ui::MODAL_TYPE_WINDOW;
}

views::View* CryptoModulePasswordDialogView::GetContentsView() {
  return this;
}

string16 CryptoModulePasswordDialogView::GetDialogButtonLabel(
    ui::DialogButton button) const {
  return l10n_util::GetStringUTF16(button == ui::DIALOG_BUTTON_OK ?
      IDS_CRYPTO_MODULE_AUTH_DIALOG_OK_BUTTON_LABEL : IDS_CANCEL);
}

bool CryptoModulePasswordDialogView::Accept() {
  callback_.Run(UTF16ToUTF8(password_entry_->text()).c_str());
  const string16 empty;
  password_entry_->SetText(empty);
  return true;
}

bool CryptoModulePasswordDialogView::Cancel() {
  callback_.Run(static_cast<const char*>(NULL));
  const string16 empty;
  password_entry_->SetText(empty);
  return true;
}

bool CryptoModulePasswordDialogView::HandleKeyEvent(
    views::Textfield* sender,
    const views::KeyEvent& keystroke) {
  return false;
}

void CryptoModulePasswordDialogView::ContentsChanged(
    views::Textfield* sender,
    const string16& new_contents) {
}

string16 CryptoModulePasswordDialogView::GetWindowTitle() const {
  return l10n_util::GetStringUTF16(IDS_CRYPTO_MODULE_AUTH_DIALOG_TITLE);
}

void ShowCryptoModulePasswordDialog(
    const std::string& slot_name,
    bool retry,
    CryptoModulePasswordReason reason,
    const std::string& server,
    const CryptoModulePasswordCallback& callback) {
  CryptoModulePasswordDialogView* dialog =
      new CryptoModulePasswordDialogView(slot_name, reason, server, callback);
  views::Widget::CreateWindow(dialog)->Show();
}

}  // namespace browser
<|endoftext|>


<commit_before>#include <Game/ECS/GameWorld.h>
#include <Game/ECS/Components/ActionQueueComponent.h>
#include <Game/ECS/Components/SceneComponent.h>
#include <AI/Navigation/NavAgentComponent.h>
#include <AI/Movement/SteerAction.h>
#include <Game/Objects/SmartObjectComponent.h>
#include <Game/Objects/SmartObject.h>
#include <DetourCommon.h>

namespace DEM::Game
{

static inline float SqDistanceToInteractionZone(const vector3& Pos, const CInteractionZone& Zone, UPTR& OutSegment, float& OutT)
{
	const auto VertexCount = Zone.Vertices.size();
	if (VertexCount == 0) return std::numeric_limits<float>().max();
	else if (VertexCount == 1) return vector3::SqDistance(Pos, Zone.Vertices[0]);
	else if (VertexCount == 2) return dtDistancePtSegSqr2D(Pos.v, Zone.Vertices[0].v, Zone.Vertices[1].v, OutT);
	else
	{
		float MinSqDistance = std::numeric_limits<float>().max();
		if (Zone.ClosedPolygon)
		{
			// NB: only convex polys are supported for now!
			if (dtPointInPolygon(Pos.v, Zone.Vertices[0].v, VertexCount))
			{
				OutSegment = VertexCount;
				return 0.f;
			}

			// Process implicit closing edge
			MinSqDistance = dtDistancePtSegSqr2D(Pos.v, Zone.Vertices[VertexCount - 1].v, Zone.Vertices[0].v, OutT);
			OutSegment = VertexCount - 1;
		}

		for (UPTR i = 0; i < VertexCount - 1; ++i)
		{
			float t;
			const float SqDistance = dtDistancePtSegSqr2D(Pos.v, Zone.Vertices[i].v, Zone.Vertices[i + 1].v, t);
			if (SqDistance < MinSqDistance)
			{
				MinSqDistance = SqDistance;
				OutSegment = i;
				OutT = t;
			}
		}

		return MinSqDistance;
	}
}
//---------------------------------------------------------------------

static inline vector3 PointInInteractionZone(const vector3& Pos, const CInteractionZone& Zone, UPTR Segment, float t)
{
	const auto VertexCount = Zone.Vertices.size();
	if (VertexCount == 0) return vector3::Zero;
	else if (VertexCount == 1) return Zone.Vertices[0];
	else if (VertexCount == 2) return vector3::lerp(Zone.Vertices[0], Zone.Vertices[1], t);
	else if (Segment == VertexCount) return Pos;
	else return vector3::lerp(Zone.Vertices[Segment], Zone.Vertices[(Segment + 1) % VertexCount], t);
}
//---------------------------------------------------------------------

// NB: non-zero _AllowedZones implied
static vector3 FindLocalInteractionPoint(SwitchSmartObjectState& Action, const CSmartObject& SO, const vector3& ActorLocalPos, U8 ZoneIndex = CSmartObject::MAX_ZONES)
{
	float MinSqDistance = std::numeric_limits<float>().max();
	UPTR SegmentIdx = 0;
	float t = 0.f;

	if (ZoneIndex >= CSmartObject::MAX_ZONES)
	{
		// Find closest suitable zone
		const auto ZoneCount = SO.GetInteractionZoneCount();
		for (U8 i = 0; i < ZoneCount; ++i)
		{
			if (!((1 << i) & Action._AllowedZones)) continue;

			const float SqDistance = SqDistanceToInteractionZone(ActorLocalPos, SO.GetInteractionZone(i), SegmentIdx, t);
			if (SqDistance < MinSqDistance)
			{
				MinSqDistance = SqDistance;
				Action._ZoneIndex = i;
			}
		}
	}
	else
	{
		MinSqDistance = SqDistanceToInteractionZone(ActorLocalPos, SO.GetInteractionZone(ZoneIndex), SegmentIdx, t);
		Action._ZoneIndex = ZoneIndex;
	}

	// For static smart objects try each zone only once
	if (SO.IsStatic()) Action._AllowedZones &= ~(1 << Action._ZoneIndex);

	return PointInInteractionZone(ActorLocalPos, SO.GetInteractionZone(Action._ZoneIndex), SegmentIdx, t);
}
//---------------------------------------------------------------------

void InteractWithSmartObjects(CGameWorld& World)
{
	World.ForEachEntityWith<CActionQueueComponent, const CSceneComponent, const AI::CNavAgentComponent*>(
		[&World](auto EntityID, auto& Entity,
			CActionQueueComponent& Queue,
			const CSceneComponent& ActorSceneComponent,
			const AI::CNavAgentComponent* pNavAgent)
	{
		if (!ActorSceneComponent.RootNode) return;

		auto Action = Queue.FindCurrent<SwitchSmartObjectState>();
		if (!Action) return;

		const auto ActionStatus = Queue.GetStatus(Action);
		if (ActionStatus != EActionStatus::Active) return;

		const auto ChildAction = Queue.GetChild(Action);
		const auto ChildActionStatus = Queue.GetStatus(ChildAction);
		if (ChildActionStatus == EActionStatus::Cancelled)
		{
			Queue.SetStatus(Action, EActionStatus::Cancelled);
			return;
		}

		auto pAction = Action.As<SwitchSmartObjectState>();

		auto pSOComponent = World.FindComponent<CSmartObjectComponent>(pAction->_Object);
		if (!pSOComponent || !pSOComponent->Asset)
		{
			Queue.SetStatus(Action, EActionStatus::Failed);
			return;
		}

		const CSmartObject* pSOAsset = pSOComponent->Asset->GetObject<CSmartObject>();
		if (!pSOAsset)
		{
			Queue.SetStatus(Action, EActionStatus::Failed);
			return;
		}

		auto pSOSceneComponent = World.FindComponent<CSceneComponent>(pAction->_Object);
		if (!pSOSceneComponent || !pSOSceneComponent->RootNode)
		{
			Queue.SetStatus(Action, EActionStatus::Failed);
			return;
		}

		// Cache allowed zones once
		if (!pAction->_AllowedZones)
		{
			const auto ZoneCount = pSOAsset->GetInteractionZoneCount();
			for (U8 i = 0; i < ZoneCount; ++i)
			{
				const auto& Zone = pSOAsset->GetInteractionZone(i);
				for (const auto& Interaction : Zone.Interactions)
				{
					if (Interaction.ID == pAction->_Interaction)
					{
						pAction->_AllowedZones |= (1 << i);
						break;
					}
				}
			}

			if (!pAction->_AllowedZones)
			{
				Queue.SetStatus(Action, EActionStatus::Failed);
				return;
			}
		}

		const auto& ActorPos = ActorSceneComponent.RootNode->GetWorldPosition();
		const auto& ObjectPos = pSOSceneComponent->RootNode->GetWorldPosition();
		const auto& ObjectWorldTfm = pSOSceneComponent->RootNode->GetWorldMatrix();

		// Move to the interaction point

		if (auto pSteerAction = ChildAction.As<AI::Steer>())
		{
			if (ChildActionStatus == EActionStatus::Active)
			{
				//if (pSOAsset->IsStatic()) return;
				// if Steer optimization will be used for dynamic objects, must update target here
				// and generate Steer or Navigate. But most probably Steer will be used for Static only.
				// NB: Steer can also be used for dynamic if actor can't navigate. So can check this
				// and if nav agent is null then update Steer target without trying to generate Navigate.
				return;
			}
			else if (ChildActionStatus == EActionStatus::Failed)
			{
				Queue.SetStatus(Action, EActionStatus::Failed);
				return;
			}
		}
		else if (auto pNavAction = ChildAction.As<AI::Navigate>())
		{
			if (ChildActionStatus == EActionStatus::Active)
			{
				if (pSOAsset->IsStatic())
				{
					// If the path intersects with another interaction zone, can optimize by navigating to it
					//!!!TODO: instead of _PathScanned could increment path version on replan and recheck, to handle partial path and corridor optimizations!
					if (!pAction->_PathScanned && pAction->_AllowedZones && pNavAgent && pNavAgent->State == AI::ENavigationState::Following)
					{
						pAction->_PathScanned = true;

						const auto ZoneCount = pSOAsset->GetInteractionZoneCount();

						for (int PolyIdx = 0; PolyIdx < pNavAgent->Corridor.getPathCount(); ++PolyIdx)
						{
							const auto PolyRef = pNavAgent->Corridor.getPath()[PolyIdx];
							for (U8 ZoneIdx = 0; ZoneIdx < ZoneCount; ++ZoneIdx)
							{
								if (!((1 << ZoneIdx) & pAction->_AllowedZones)) continue;

								const auto& Zone = pSOAsset->GetInteractionZone(ZoneIdx);

								//!!!calc distance from PolyRef to Zone!
								const float SqDistance = 0.f;

								const float Radius = Zone.Radius; //???apply actor radius too?
								const float SqRadius = std::max(Radius * Radius, AI::Steer::SqLinearTolerance);
								if (SqDistance <= SqRadius)
								{
									pAction->_ZoneIndex = ZoneIdx;
									pAction->_AllowedZones &= ~(1 << ZoneIdx);

									//!!!set new point as a target, no replanning will happen!
								}
							}
						}
					}
				}
				else
				{
					const auto& Zone = pSOAsset->GetInteractionZone(pAction->_ZoneIndex);
					//const float SqDistance = SqDistanceToInteractionZone(SOSpaceActorPos, Zone, SegmentIdx, t);
					// get target position from SO asset (for the curr region) and update it in the action
				}
				return;
			}
			else if (ChildActionStatus == EActionStatus::Failed)
			{
				// try to navigate to the point in the next closest region (static) or any region (dynamic)
				// if all are tried and failed, Queue.SetStatus(Action, EActionStatus::Failed);
				//???when dynamic fails?
				return;
			}
		}
		else if (!pSOAsset->IsStatic() || !ChildAction)
		{
			if (!pAction->_AllowedZones)
			{
				Queue.SetStatus(Action, EActionStatus::Failed);
				return;
			}

			matrix44 WorldToSmartObject;
			ObjectWorldTfm.invert_simple(WorldToSmartObject);
			const vector3 SOSpaceActorPos = WorldToSmartObject.transform_coord(ActorPos);

			vector3 ActionPos = FindLocalInteractionPoint(*pAction, *pSOAsset, SOSpaceActorPos);

			const auto& Zone = pSOAsset->GetInteractionZone(pAction->_ZoneIndex);
			const float Radius = Zone.Radius; //???apply actor radius too?
			const float SqRadius = std::max(Radius * Radius, AI::Steer::SqLinearTolerance);

			// FIXME: find closest navigable point, use radius as arrival distance, apply to the last path segment
			//if (SqRadius < MinSqDistance)
			//	ActionPos = vector3::lerp(ActionPos, SOSpaceActorPos, Radius / n_sqrt(MinSqDistance));

			ActionPos = ObjectWorldTfm.transform_coord(ActionPos);

			if (vector3::SqDistance2D(ActionPos, ActorPos) > SqRadius)
			{
				if (pNavAgent)
				{
					float NearestPos[3];
					const float Extents[3] = { Radius, pNavAgent->Height, Radius };
					dtPolyRef ObjPolyRef = 0;
					pNavAgent->pNavQuery->findNearestPoly(ActionPos.v, Extents, pNavAgent->Settings->GetQueryFilter(), &ObjPolyRef, NearestPos);
					const float SqDiff = dtVdist2DSqr(ActionPos.v, NearestPos);
					if (!ObjPolyRef || SqDiff > SqRadius)
					{
						// No navigable point found
						// FIXME: try the next zone right now!
						// TODO: could explore all the interaction zone for intersecion with valid navigation polys, but it is slow
						//???what if dynamic? discard this zone or fail immediately?
						return;
					}
					else if (SqDiff > 0.f) ActionPos = NearestPos;

					// FIXME: use pNavAgent->Corridor.getFirstPoly() instead! Offmesh can break this now!
					const float AgentExtents[3] = { pNavAgent->Radius, pNavAgent->Height, pNavAgent->Radius };
					dtPolyRef AgentPolyRef = 0;
					pNavAgent->pNavQuery->findNearestPoly(ActorPos.v, AgentExtents, pNavAgent->Settings->GetQueryFilter(), &AgentPolyRef, NearestPos);
					//n_assert_dbg(pNavAgent->Corridor.getFirstPoly() == AgentPolyRef);

					if (ObjPolyRef != AgentPolyRef)
					{
						// To avoid possible parent path invalidation, could try to optimize with:
						//Agent.pNavQuery->findLocalNeighbourhood
						//Agent.pNavQuery->moveAlongSurface
						//Agent.pNavQuery->raycast
						// but it would require additional logic and complicate navigation.
						Queue.PushOrUpdateChild<AI::Navigate>(Action, ActionPos, 0.f);
						return;
					}
				}

				Queue.PushOrUpdateChild<AI::Steer>(Action, ActionPos, ObjectPos, 0.f);
				return;
			}
		}

		// Face interaction direction

		if (auto pTurnAction = ChildAction.As<AI::Turn>())
		{
			if (ChildActionStatus == EActionStatus::Active)
			{
				if (pSOAsset->IsStatic()) return;

				// get facing from SO asset for the current region
				// if facing is still required by SO, update it in Turn action and return
			}
			else if (ChildActionStatus == EActionStatus::Failed)
			{
				Queue.SetStatus(Action, EActionStatus::Failed);
				return;
			}
		}
		else if (!pSOAsset->IsStatic() || !ChildAction)
		{
			// get facing from SO asset for the current region
			// if facing is required by SO and not already looking at that dir, generate Turn action and return
		}

		// Interact with object

		// ... anim, state transition etc ...

		// FIXME: fill! Get facing (only if necessary) from interaction params
		vector3 TargetDir = ObjectPos - ActorPos;
		TargetDir.norm();

		//???update only if there is no Turn already active?
		vector3 LookatDir = -ActorSceneComponent.RootNode->GetWorldMatrix().AxisZ();
		LookatDir.norm();
		const float Angle = vector3::Angle2DNorm(LookatDir, TargetDir);
		if (std::fabsf(Angle) >= DEM::AI::Turn::AngularTolerance)
		{
			Queue.PushOrUpdateChild<AI::Turn>(Action, TargetDir);
			return;
		}

		pSOComponent->RequestedState = pAction->_State;
		pSOComponent->Force = pAction->_Force;

		// TODO: start actor animation and/or state switching, wait for its end before setting Succeeded status!
		Queue.SetStatus(Action, EActionStatus::Succeeded);
	});
}
//---------------------------------------------------------------------

}
<commit_msg>SO iact movement progress 2<commit_after>#include <Game/ECS/GameWorld.h>
#include <Game/ECS/Components/ActionQueueComponent.h>
#include <Game/ECS/Components/SceneComponent.h>
#include <AI/Navigation/NavAgentComponent.h>
#include <AI/Movement/SteerAction.h>
#include <Game/Objects/SmartObjectComponent.h>
#include <Game/Objects/SmartObject.h>
#include <DetourCommon.h>

namespace DEM::Game
{

static inline float SqDistanceToInteractionZone(const vector3& Pos, const CInteractionZone& Zone, UPTR& OutSegment, float& OutT)
{
	const auto VertexCount = Zone.Vertices.size();
	if (VertexCount == 0) return std::numeric_limits<float>().max();
	else if (VertexCount == 1) return vector3::SqDistance(Pos, Zone.Vertices[0]);
	else if (VertexCount == 2) return dtDistancePtSegSqr2D(Pos.v, Zone.Vertices[0].v, Zone.Vertices[1].v, OutT);
	else
	{
		float MinSqDistance = std::numeric_limits<float>().max();
		if (Zone.ClosedPolygon)
		{
			// NB: only convex polys are supported for now!
			if (dtPointInPolygon(Pos.v, Zone.Vertices[0].v, VertexCount))
			{
				OutSegment = VertexCount;
				return 0.f;
			}

			// Process implicit closing edge
			MinSqDistance = dtDistancePtSegSqr2D(Pos.v, Zone.Vertices[VertexCount - 1].v, Zone.Vertices[0].v, OutT);
			OutSegment = VertexCount - 1;
		}

		for (UPTR i = 0; i < VertexCount - 1; ++i)
		{
			float t;
			const float SqDistance = dtDistancePtSegSqr2D(Pos.v, Zone.Vertices[i].v, Zone.Vertices[i + 1].v, t);
			if (SqDistance < MinSqDistance)
			{
				MinSqDistance = SqDistance;
				OutSegment = i;
				OutT = t;
			}
		}

		return MinSqDistance;
	}
}
//---------------------------------------------------------------------

static inline vector3 PointInInteractionZone(const vector3& Pos, const CInteractionZone& Zone, UPTR Segment, float t)
{
	const auto VertexCount = Zone.Vertices.size();
	if (VertexCount == 0) return vector3::Zero;
	else if (VertexCount == 1) return Zone.Vertices[0];
	else if (VertexCount == 2) return vector3::lerp(Zone.Vertices[0], Zone.Vertices[1], t);
	else if (Segment == VertexCount) return Pos;
	else return vector3::lerp(Zone.Vertices[Segment], Zone.Vertices[(Segment + 1) % VertexCount], t);
}
//---------------------------------------------------------------------

// NB: non-zero _AllowedZones implied
static vector3 FindLocalInteractionPoint(SwitchSmartObjectState& Action, const CSmartObject& SO, const vector3& ActorLocalPos, bool OnlyCurrZone)
{
	float MinSqDistance = std::numeric_limits<float>().max();
	UPTR SegmentIdx = 0;
	float t = 0.f;

	if (OnlyCurrZone)
	{
		MinSqDistance = SqDistanceToInteractionZone(ActorLocalPos, SO.GetInteractionZone(Action._ZoneIndex), SegmentIdx, t);
	}
	else
	{
		// Find closest suitable zone
		const auto ZoneCount = SO.GetInteractionZoneCount();
		for (U8 i = 0; i < ZoneCount; ++i)
		{
			if (!((1 << i) & Action._AllowedZones)) continue;

			const float SqDistance = SqDistanceToInteractionZone(ActorLocalPos, SO.GetInteractionZone(i), SegmentIdx, t);
			if (SqDistance < MinSqDistance)
			{
				MinSqDistance = SqDistance;
				Action._ZoneIndex = i;
			}
		}
	}

	// For static smart objects try each zone only once
	if (SO.IsStatic()) Action._AllowedZones &= ~(1 << Action._ZoneIndex);

	return PointInInteractionZone(ActorLocalPos, SO.GetInteractionZone(Action._ZoneIndex), SegmentIdx, t);
}
//---------------------------------------------------------------------

//???when dynamic fails? zones aren't marked as failed!
static bool UpdateMovementSubAction(CActionQueueComponent& Queue, HAction Action, const CSmartObject& SO,
	const AI::CNavAgentComponent* pNavAgent, const matrix44& ObjectWorldTfm, const vector3& ActorPos, bool OnlyCurrZone)
{
	auto pAction = Action.As<SwitchSmartObjectState>();
	if (!pAction->_AllowedZones)
	{
		Queue.SetStatus(Action, EActionStatus::Failed);
		return true;
	}

	matrix44 WorldToSmartObject;
	ObjectWorldTfm.invert_simple(WorldToSmartObject);
	const vector3 SOSpaceActorPos = WorldToSmartObject.transform_coord(ActorPos);

	vector3 ActionPos = FindLocalInteractionPoint(*pAction, SO, SOSpaceActorPos, OnlyCurrZone);

	const auto& Zone = SO.GetInteractionZone(pAction->_ZoneIndex);
	const float Radius = Zone.Radius; //???apply actor radius too?
	const float SqRadius = std::max(Radius * Radius, AI::Steer::SqLinearTolerance);

	// FIXME: find closest navigable point, use radius as arrival distance, apply to the last path segment
	//if (SqRadius < MinSqDistance)
	//	ActionPos = vector3::lerp(ActionPos, SOSpaceActorPos, Radius / n_sqrt(MinSqDistance));

	ActionPos = ObjectWorldTfm.transform_coord(ActionPos);

	if (vector3::SqDistance2D(ActionPos, ActorPos) <= SqRadius) return false;

	if (pNavAgent)
	{
		float NearestPos[3];
		const float Extents[3] = { Radius, pNavAgent->Height, Radius };
		dtPolyRef ObjPolyRef = 0;
		pNavAgent->pNavQuery->findNearestPoly(ActionPos.v, Extents, pNavAgent->Settings->GetQueryFilter(), &ObjPolyRef, NearestPos);
		const float SqDiff = dtVdist2DSqr(ActionPos.v, NearestPos);
		if (!ObjPolyRef || SqDiff > SqRadius)
		{
			// No navigable point found
			// FIXME: try the next zone right now!
			// TODO: could explore all the interaction zone for intersecion with valid navigation polys, but it is slow
			//???what if dynamic? discard this zone or fail immediately?
			return true;
		}
		else if (SqDiff > 0.f) ActionPos = NearestPos;

		// FIXME: use pNavAgent->Corridor.getFirstPoly() instead! Offmesh can break this now!
		const float AgentExtents[3] = { pNavAgent->Radius, pNavAgent->Height, pNavAgent->Radius };
		dtPolyRef AgentPolyRef = 0;
		pNavAgent->pNavQuery->findNearestPoly(ActorPos.v, AgentExtents, pNavAgent->Settings->GetQueryFilter(), &AgentPolyRef, NearestPos);
		//n_assert_dbg(pNavAgent->Corridor.getFirstPoly() == AgentPolyRef);

		if (ObjPolyRef != AgentPolyRef)
		{
			// To avoid possible parent path invalidation, could try to optimize with:
			//Agent.pNavQuery->findLocalNeighbourhood
			//Agent.pNavQuery->moveAlongSurface
			//Agent.pNavQuery->raycast
			// but it would require additional logic and complicate navigation.
			Queue.PushOrUpdateChild<AI::Navigate>(Action, ActionPos, 0.f);
			return true;
		}
	}

	Queue.PushOrUpdateChild<AI::Steer>(Action, ActionPos, ObjectWorldTfm.Translation(), 0.f);
	return true;
}
//---------------------------------------------------------------------

// NB: OutPos is not changed if function returns false
static bool IsNavPolyInInteractionZone(const CInteractionZone& Zone, dtPolyRef PolyRef, dtNavMeshQuery& Query, vector3& OutPos)
{
	////!!!calc distance from PolyRef to Zone skeleton!
	//const float SqDistance = 0.f;

	//const float Radius = Zone.Radius; //???apply actor radius too?
	//const float SqRadius = std::max(Radius * Radius, AI::Steer::SqLinearTolerance);
	//return SqDistance <= SqRadius;

	//!!!return closest pos!

	//NOT_IMPLEMENTED;
	return false;
}
//---------------------------------------------------------------------

// If new path intersects with another interaction zone, can optimize by navigating to it instead of the original target
static void OptimizeStaticPath(SwitchSmartObjectState& Action, AI::Navigate& NavAction, const CSmartObject& SO, const AI::CNavAgentComponent* pNavAgent)
{
	//!!!TODO: instead of _PathScanned could increment path version on replan and recheck,
	//to handle partial path and corridor optimizations!
	if (Action._PathScanned || !Action._AllowedZones || !pNavAgent || pNavAgent->State != AI::ENavigationState::Following) return;

	Action._PathScanned = true;

	const auto ZoneCount = SO.GetInteractionZoneCount();

	// Don't test the last poly, we already navigate to it
	const int PolysToTest = pNavAgent->Corridor.getPathCount() - 1;
	for (int PolyIdx = 0; PolyIdx < PolysToTest; ++PolyIdx)
	{
		const auto PolyRef = pNavAgent->Corridor.getPath()[PolyIdx];
		for (U8 ZoneIdx = 0; ZoneIdx < ZoneCount; ++ZoneIdx)
		{
			if (!((1 << ZoneIdx) & Action._AllowedZones)) continue;
			if (IsNavPolyInInteractionZone(SO.GetInteractionZone(ZoneIdx), PolyRef, *pNavAgent->pNavQuery, NavAction._Destination))
			{
				Action._ZoneIndex = ZoneIdx;
				Action._AllowedZones &= ~(1 << ZoneIdx);
				return;
			}
		}
	}
}
//---------------------------------------------------------------------

void InteractWithSmartObjects(CGameWorld& World)
{
	World.ForEachEntityWith<CActionQueueComponent, const CSceneComponent, const AI::CNavAgentComponent*>(
		[&World](auto EntityID, auto& Entity,
			CActionQueueComponent& Queue,
			const CSceneComponent& ActorSceneComponent,
			const AI::CNavAgentComponent* pNavAgent)
	{
		if (!ActorSceneComponent.RootNode) return;

		auto Action = Queue.FindCurrent<SwitchSmartObjectState>();
		if (!Action) return;

		const auto ActionStatus = Queue.GetStatus(Action);
		if (ActionStatus != EActionStatus::Active) return;

		const auto ChildAction = Queue.GetChild(Action);
		const auto ChildActionStatus = Queue.GetStatus(ChildAction);
		if (ChildActionStatus == EActionStatus::Cancelled)
		{
			Queue.SetStatus(Action, EActionStatus::Cancelled);
			return;
		}

		auto pAction = Action.As<SwitchSmartObjectState>();

		auto pSOComponent = World.FindComponent<CSmartObjectComponent>(pAction->_Object);
		if (!pSOComponent || !pSOComponent->Asset)
		{
			Queue.SetStatus(Action, EActionStatus::Failed);
			return;
		}

		const CSmartObject* pSOAsset = pSOComponent->Asset->GetObject<CSmartObject>();
		if (!pSOAsset)
		{
			Queue.SetStatus(Action, EActionStatus::Failed);
			return;
		}

		auto pSOSceneComponent = World.FindComponent<CSceneComponent>(pAction->_Object);
		if (!pSOSceneComponent || !pSOSceneComponent->RootNode)
		{
			Queue.SetStatus(Action, EActionStatus::Failed);
			return;
		}

		// Cache allowed zones once
		if (!pAction->_AllowedZones)
		{
			const auto ZoneCount = pSOAsset->GetInteractionZoneCount();
			for (U8 i = 0; i < ZoneCount; ++i)
			{
				const auto& Zone = pSOAsset->GetInteractionZone(i);
				for (const auto& Interaction : Zone.Interactions)
				{
					if (Interaction.ID == pAction->_Interaction)
					{
						pAction->_AllowedZones |= (1 << i);
						break;
					}
				}
			}

			if (!pAction->_AllowedZones)
			{
				Queue.SetStatus(Action, EActionStatus::Failed);
				return;
			}
		}

		const auto& ActorPos = ActorSceneComponent.RootNode->GetWorldPosition();
		const auto& ObjectWorldTfm = pSOSceneComponent->RootNode->GetWorldMatrix();

		// Move to the interaction point

		if (auto pSteerAction = ChildAction.As<AI::Steer>())
		{
			if (ChildActionStatus == EActionStatus::Active)
			{
				if (pSOAsset->IsStatic()) return;
				else if (UpdateMovementSubAction(Queue, Action, *pSOAsset, pNavAgent, ObjectWorldTfm, ActorPos, true)) return;
			}
			else if (ChildActionStatus == EActionStatus::Failed)
			{
				Queue.SetStatus(Action, EActionStatus::Failed);
				return;
			}
		}
		else if (auto pNavAction = ChildAction.As<AI::Navigate>())
		{
			if (ChildActionStatus == EActionStatus::Active)
			{
				if (pSOAsset->IsStatic())
				{
					OptimizeStaticPath(*pAction, *pNavAction, *pSOAsset, pNavAgent);
					return;
				}
				else
				{
					// Update movement target from the current zone
					if (UpdateMovementSubAction(Queue, Action, *pSOAsset, pNavAgent, ObjectWorldTfm, ActorPos, true)) return;
				}
			}
			else if (ChildActionStatus == EActionStatus::Failed)
			{
				// Try another remaining zones with navigable points one by one, fail if none left
				if (UpdateMovementSubAction(Queue, Action, *pSOAsset, pNavAgent, ObjectWorldTfm, ActorPos, false)) return;
			}
		}
		else if (!pSOAsset->IsStatic() || !ChildAction)
		{
			// Move to the closest zone with navigable point inside, fail if none left
			if (UpdateMovementSubAction(Queue, Action, *pSOAsset, pNavAgent, ObjectWorldTfm, ActorPos, false)) return;
		}

		// Face interaction direction

		if (auto pTurnAction = ChildAction.As<AI::Turn>())
		{
			if (ChildActionStatus == EActionStatus::Active)
			{
				if (pSOAsset->IsStatic()) return;

				// get facing from SO asset for the current region
				// if facing is still required by SO, update it in Turn action and return
			}
			else if (ChildActionStatus == EActionStatus::Failed)
			{
				Queue.SetStatus(Action, EActionStatus::Failed);
				return;
			}
		}
		else
		{
			// get facing from SO asset for the current region
			// if facing is required by SO and not already looking at that dir, generate Turn action and return

			//!!!DBG TMP!
			vector3 TargetDir = ObjectWorldTfm.Translation() - ActorPos;
			TargetDir.norm();

			//???update only if there is no Turn already active?
			vector3 LookatDir = -ActorSceneComponent.RootNode->GetWorldMatrix().AxisZ();
			LookatDir.norm();
			const float Angle = vector3::Angle2DNorm(LookatDir, TargetDir);
			if (std::fabsf(Angle) >= DEM::AI::Turn::AngularTolerance)
			{
				Queue.PushOrUpdateChild<AI::Turn>(Action, TargetDir);
				return;
			}
		}

		// Interact with object

		pSOComponent->RequestedState = pAction->_State;
		pSOComponent->Force = pAction->_Force;

		// TODO: start actor animation and/or state switching, wait for its end before setting Succeeded status!
		Queue.SetStatus(Action, EActionStatus::Succeeded);
	});
}
//---------------------------------------------------------------------

}
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2012 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_OFFSET_CONVERTER_HPP
#define MAPNIK_OFFSET_CONVERTER_HPP

#ifdef MAPNIK_LOG
#include <mapnik/debug.hpp>
#endif
#include <mapnik/config.hpp>
#include <mapnik/box2d.hpp>
#include <mapnik/vertex.hpp>
#include <mapnik/proj_transform.hpp>

// boost
#include <boost/math/constants/constants.hpp>

// stl
#include <cmath>

namespace mapnik
{

const double pi = boost::math::constants::pi<double>();

template <typename Geometry>
struct MAPNIK_DECL offset_converter
{
    using size_type = std::size_t;

    offset_converter(Geometry & geom)
        : geom_(geom)
        , offset_(0.0)
        , threshold_(8.0)
        , half_turn_segments_(16)
        , status_(initial)
        , pre_first_(vertex2d::no_init)
        , pre_(vertex2d::no_init)
        , cur_(vertex2d::no_init)
        {}

    enum status
    {
        initial,
        process
    };

    unsigned type() const
    {
        return static_cast<unsigned>(geom_.type());
    }

    double get_offset() const
    {
        return offset_;
    }

    double get_threshold() const
    {
        return threshold_;
    }

    void set_offset(double value)
    {
        if (offset_ != value)
        {
            offset_ = value;
            reset();
        }
    }

    void set_threshold(double value)
    {
        threshold_ = value;
        // no need to reset(), since threshold doesn't affect
        // offset vertices' computation, it only controls how
        // far will we be looking for self-intersections
    }

    unsigned vertex(double * x, double * y)
    {
        if (offset_ == 0.0)
        {
            return geom_.vertex(x, y);
        }

        if (status_ == initial)
        {
            init_vertices();
        }

        if (pos_ >= vertices_.size())
        {
            return SEG_END;
        }

        pre_ = (pos_ ? cur_ : pre_first_);
        cur_ = vertices_.at(pos_++);

        if (pos_ == vertices_.size())
        {
            return output_vertex(x, y);
        }

        double const check_dist = offset_ * threshold_;
        double const check_dist2 = check_dist * check_dist;
        double t = 1.0;
        double vt, ut;

        for (size_t i = pos_; i+1 < vertices_.size(); ++i)
        {
            //break; // uncomment this to see all the curls

            vertex2d const& u0 = vertices_[i];
            vertex2d const& u1 = vertices_[i+1];
            double const dx = u0.x - cur_.x;
            double const dy = u0.y - cur_.y;

            if (dx*dx + dy*dy > check_dist2)
            {
                break;
            }

            if (!intersection(pre_, cur_, &vt, u0, u1, &ut))
            {
                continue;
            }

            if (vt < 0.0 || vt > t || ut < 0.0 || ut > 1.0)
            {
                continue;
            }

            t = vt;
            pos_ = i+1;
        }

        cur_.x = pre_.x + t * (cur_.x - pre_.x);
        cur_.y = pre_.y + t * (cur_.y - pre_.y);
        return output_vertex(x, y);
    }

    void reset()
    {
        geom_.rewind(0);
        vertices_.clear();
        status_ = initial;
        pos_ = 0;
    }

    void rewind(unsigned)
    {
        pos_ = 0;
    }

private:

    static double explement_reflex_angle(double angle)
    {
        if (angle > pi)
        {
            return angle - 2 * pi;
        }
        else if (angle < -pi)
        {
            return angle + 2 * pi;
        }
        else
        {
            return angle;
        }
    }

    static bool intersection(vertex2d const& u1, vertex2d const& u2, double* ut,
                             vertex2d const& v1, vertex2d const& v2, double* vt)
    {
        double const dx = v1.x - u1.x;
        double const dy = v1.y - u1.y;
        double const ux = u2.x - u1.x;
        double const uy = u2.y - u1.y;
        double const vx = v2.x - v1.x;
        double const vy = v2.y - v1.y;

        // the first line is not vertical
        if (ux < -1e-6 || ux > 1e-6)
        {
            double const up = ux * dy - dx * uy;
            double const dn = vx * uy - ux * vy;

            if (dn > -1e-6 && dn < 1e-6)
            {
                return false; // they are parallel
            }

            *vt = up / dn;
            *ut = (*vt * vx + dx) / ux;
            return true;
        }

        // the first line is not horizontal
        if (uy < -1e-6 || uy > 1e-6)
        {
            double const up = uy * dx - dy * ux;
            double const dn = vy * ux - uy * vx;

            if (dn > -1e-6 && dn < 1e-6)
            {
                return false; // they are parallel
            }

            *vt = up / dn;
            *ut = (*vt * vy + dy) / uy;
            return true;
        }

        // the first line is too short
        return false;
    }

    /**
     *  @brief  Translate (vx, vy) by rotated (dx, dy).
     */
    static void displace(vertex2d & v, double dx, double dy, double a)
    {
        v.x += dx * std::cos(a) - dy * std::sin(a);
        v.y += dx * std::sin(a) + dy * std::cos(a);
    }

    /**
     *  @brief  Translate (vx, vy) by rotated (0, -offset).
     */
    void displace(vertex2d & v, double a) const
    {
        v.x += offset_ * std::sin(a);
        v.y -= offset_ * std::cos(a);
    }

    /**
     *  @brief  (vx, vy) := (ux, uy) + rotated (0, -offset)
     */
    void displace(vertex2d & v, vertex2d const& u, double a) const
    {
        v.x = u.x + offset_ * std::sin(a);
        v.y = u.y - offset_ * std::cos(a);
        v.cmd = u.cmd;
    }

    void displace2(vertex2d & v, double a, double b) const
    {
        double sa = offset_ * std::sin(a);
        double ca = offset_ * std::cos(a);
        double h = std::tan(0.5 * (b - a));
        v.x = v.x + sa + h * ca;
        v.y = v.y - ca + h * sa;
    }

    status init_vertices()
    {
        if (status_ != initial) // already initialized
        {
            return status_;
        }

        vertex2d v1(vertex2d::no_init);
        vertex2d v2(vertex2d::no_init);
        vertex2d w(vertex2d::no_init);

        v1.cmd = geom_.vertex(&v1.x, &v1.y);
        v2.cmd = geom_.vertex(&v2.x, &v2.y);

        if (v2.cmd == SEG_END) // not enough vertices in source
        {
            return status_ = process;
        }

        double angle_a = 0;
        double angle_b = std::atan2((v2.y - v1.y), (v2.x - v1.x));
        double joint_angle;

        // first vertex
        displace(v1, angle_b);
        push_vertex(v1);

        // Sometimes when the first segment is too short, it causes ugly
        // curls at the beginning of the line. To avoid this, we make up
        // a fake vertex two offset-lengths before the first, and expect
        // intersection detection smoothes it out.
        pre_first_ = v1;
        displace(pre_first_, -2 * std::fabs(offset_), 0, angle_b);

        while ((v1 = v2, v2.cmd = geom_.vertex(&v2.x, &v2.y)) != SEG_END)
        {
            angle_a = angle_b;
            angle_b = std::atan2((v2.y - v1.y), (v2.x - v1.x));
            joint_angle = explement_reflex_angle(angle_b - angle_a);

            double half_turns = half_turn_segments_ * std::fabs(joint_angle);
            int bulge_steps = 0;

            if (offset_ < 0.0)
            {
                if (joint_angle > 0.0)
                {
                    joint_angle = joint_angle - 2 * pi;
                }
                else
                {
                    bulge_steps = 1 + static_cast<int>(std::floor(half_turns / pi));
                }
            }
            else
            {
                if (joint_angle < 0.0)
                {
                    joint_angle = joint_angle + 2 * pi;
                }
                else
                {
                    bulge_steps = 1 + static_cast<int>(std::floor(half_turns / pi));
                }
            }

            #ifdef MAPNIK_LOG
            if (bulge_steps == 0)
            {
                // inside turn (sharp/obtuse angle)
                MAPNIK_LOG_DEBUG(ctrans) << "offset_converter:"
                    << " Sharp joint [<< inside turn " << int(joint_angle*180/pi)
                    << " degrees >>]";
            }
            else
            {
                // outside turn (reflex angle)
                MAPNIK_LOG_DEBUG(ctrans) << "offset_converter:"
                    << " Bulge joint >)) outside turn " << int(joint_angle*180/pi)
                    << " degrees ((< with " << bulge_steps << " segments";
            }
            #endif

            displace(w, v1, angle_a);
            push_vertex(w);

            for (int s = 0; ++s < bulge_steps;)
            {
                displace(w, v1, angle_a + (joint_angle * s) / bulge_steps);
                push_vertex(w);
            }

            displace(v1, angle_b);
            push_vertex(v1);
        }

        // last vertex
        displace(v1, angle_b);
        push_vertex(v1);

        // initialization finished
        return status_ = process;
    }

    unsigned output_vertex(double* px, double* py)
    {
        *px = cur_.x;
        *py = cur_.y;
        return cur_.cmd;
    }

    unsigned output_vertex(double* px, double* py, status st)
    {
        status_ = st;
        return output_vertex(px, py);
    }

    void push_vertex(vertex2d const& v)
    {
        vertices_.push_back(v);
    }

    Geometry &              geom_;
    double                  offset_;
    double                  threshold_;
    unsigned                half_turn_segments_;
    status                  status_;
    size_t                  pos_;
    std::vector<vertex2d>   vertices_;
    vertex2d                pre_first_;
    vertex2d                pre_;
    vertex2d                cur_;
};

}

#endif // MAPNIK_OFFSET_CONVERTER_HPP
<commit_msg>speed up compile / avoid boost/math - refs #2439<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2012 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_OFFSET_CONVERTER_HPP
#define MAPNIK_OFFSET_CONVERTER_HPP

#ifdef MAPNIK_LOG
#include <mapnik/debug.hpp>
#endif
#include <mapnik/global.hpp>
#include <mapnik/config.hpp>
#include <mapnik/vertex.hpp>

// stl
#include <cmath>
#include <vector>
#include <cstddef>

namespace mapnik
{

template <typename Geometry>
struct MAPNIK_DECL offset_converter
{
    using size_type = std::size_t;

    offset_converter(Geometry & geom)
        : geom_(geom)
        , offset_(0.0)
        , threshold_(8.0)
        , half_turn_segments_(16)
        , status_(initial)
        , pre_first_(vertex2d::no_init)
        , pre_(vertex2d::no_init)
        , cur_(vertex2d::no_init)
        {}

    enum status
    {
        initial,
        process
    };

    unsigned type() const
    {
        return static_cast<unsigned>(geom_.type());
    }

    double get_offset() const
    {
        return offset_;
    }

    double get_threshold() const
    {
        return threshold_;
    }

    void set_offset(double value)
    {
        if (offset_ != value)
        {
            offset_ = value;
            reset();
        }
    }

    void set_threshold(double value)
    {
        threshold_ = value;
        // no need to reset(), since threshold doesn't affect
        // offset vertices' computation, it only controls how
        // far will we be looking for self-intersections
    }

    unsigned vertex(double * x, double * y)
    {
        if (offset_ == 0.0)
        {
            return geom_.vertex(x, y);
        }

        if (status_ == initial)
        {
            init_vertices();
        }

        if (pos_ >= vertices_.size())
        {
            return SEG_END;
        }

        pre_ = (pos_ ? cur_ : pre_first_);
        cur_ = vertices_.at(pos_++);

        if (pos_ == vertices_.size())
        {
            return output_vertex(x, y);
        }

        double const check_dist = offset_ * threshold_;
        double const check_dist2 = check_dist * check_dist;
        double t = 1.0;
        double vt, ut;

        for (size_t i = pos_; i+1 < vertices_.size(); ++i)
        {
            //break; // uncomment this to see all the curls

            vertex2d const& u0 = vertices_[i];
            vertex2d const& u1 = vertices_[i+1];
            double const dx = u0.x - cur_.x;
            double const dy = u0.y - cur_.y;

            if (dx*dx + dy*dy > check_dist2)
            {
                break;
            }

            if (!intersection(pre_, cur_, &vt, u0, u1, &ut))
            {
                continue;
            }

            if (vt < 0.0 || vt > t || ut < 0.0 || ut > 1.0)
            {
                continue;
            }

            t = vt;
            pos_ = i+1;
        }

        cur_.x = pre_.x + t * (cur_.x - pre_.x);
        cur_.y = pre_.y + t * (cur_.y - pre_.y);
        return output_vertex(x, y);
    }

    void reset()
    {
        geom_.rewind(0);
        vertices_.clear();
        status_ = initial;
        pos_ = 0;
    }

    void rewind(unsigned)
    {
        pos_ = 0;
    }

private:

    static double explement_reflex_angle(double angle)
    {
        if (angle > M_PI)
        {
            return angle - 2 * M_PI;
        }
        else if (angle < -M_PI)
        {
            return angle + 2 * M_PI;
        }
        else
        {
            return angle;
        }
    }

    static bool intersection(vertex2d const& u1, vertex2d const& u2, double* ut,
                             vertex2d const& v1, vertex2d const& v2, double* vt)
    {
        double const dx = v1.x - u1.x;
        double const dy = v1.y - u1.y;
        double const ux = u2.x - u1.x;
        double const uy = u2.y - u1.y;
        double const vx = v2.x - v1.x;
        double const vy = v2.y - v1.y;

        // the first line is not vertical
        if (ux < -1e-6 || ux > 1e-6)
        {
            double const up = ux * dy - dx * uy;
            double const dn = vx * uy - ux * vy;

            if (dn > -1e-6 && dn < 1e-6)
            {
                return false; // they are parallel
            }

            *vt = up / dn;
            *ut = (*vt * vx + dx) / ux;
            return true;
        }

        // the first line is not horizontal
        if (uy < -1e-6 || uy > 1e-6)
        {
            double const up = uy * dx - dy * ux;
            double const dn = vy * ux - uy * vx;

            if (dn > -1e-6 && dn < 1e-6)
            {
                return false; // they are parallel
            }

            *vt = up / dn;
            *ut = (*vt * vy + dy) / uy;
            return true;
        }

        // the first line is too short
        return false;
    }

    /**
     *  @brief  Translate (vx, vy) by rotated (dx, dy).
     */
    static void displace(vertex2d & v, double dx, double dy, double a)
    {
        v.x += dx * std::cos(a) - dy * std::sin(a);
        v.y += dx * std::sin(a) + dy * std::cos(a);
    }

    /**
     *  @brief  Translate (vx, vy) by rotated (0, -offset).
     */
    void displace(vertex2d & v, double a) const
    {
        v.x += offset_ * std::sin(a);
        v.y -= offset_ * std::cos(a);
    }

    /**
     *  @brief  (vx, vy) := (ux, uy) + rotated (0, -offset)
     */
    void displace(vertex2d & v, vertex2d const& u, double a) const
    {
        v.x = u.x + offset_ * std::sin(a);
        v.y = u.y - offset_ * std::cos(a);
        v.cmd = u.cmd;
    }

    void displace2(vertex2d & v, double a, double b) const
    {
        double sa = offset_ * std::sin(a);
        double ca = offset_ * std::cos(a);
        double h = std::tan(0.5 * (b - a));
        v.x = v.x + sa + h * ca;
        v.y = v.y - ca + h * sa;
    }

    status init_vertices()
    {
        if (status_ != initial) // already initialized
        {
            return status_;
        }

        vertex2d v1(vertex2d::no_init);
        vertex2d v2(vertex2d::no_init);
        vertex2d w(vertex2d::no_init);

        v1.cmd = geom_.vertex(&v1.x, &v1.y);
        v2.cmd = geom_.vertex(&v2.x, &v2.y);

        if (v2.cmd == SEG_END) // not enough vertices in source
        {
            return status_ = process;
        }

        double angle_a = 0;
        double angle_b = std::atan2((v2.y - v1.y), (v2.x - v1.x));
        double joint_angle;

        // first vertex
        displace(v1, angle_b);
        push_vertex(v1);

        // Sometimes when the first segment is too short, it causes ugly
        // curls at the beginning of the line. To avoid this, we make up
        // a fake vertex two offset-lengths before the first, and expect
        // intersection detection smoothes it out.
        pre_first_ = v1;
        displace(pre_first_, -2 * std::fabs(offset_), 0, angle_b);

        while ((v1 = v2, v2.cmd = geom_.vertex(&v2.x, &v2.y)) != SEG_END)
        {
            angle_a = angle_b;
            angle_b = std::atan2((v2.y - v1.y), (v2.x - v1.x));
            joint_angle = explement_reflex_angle(angle_b - angle_a);

            double half_turns = half_turn_segments_ * std::fabs(joint_angle);
            int bulge_steps = 0;

            if (offset_ < 0.0)
            {
                if (joint_angle > 0.0)
                {
                    joint_angle = joint_angle - 2 * M_PI;
                }
                else
                {
                    bulge_steps = 1 + static_cast<int>(std::floor(half_turns / M_PI));
                }
            }
            else
            {
                if (joint_angle < 0.0)
                {
                    joint_angle = joint_angle + 2 * M_PI;
                }
                else
                {
                    bulge_steps = 1 + static_cast<int>(std::floor(half_turns / M_PI));
                }
            }

            #ifdef MAPNIK_LOG
            if (bulge_steps == 0)
            {
                // inside turn (sharp/obtuse angle)
                MAPNIK_LOG_DEBUG(ctrans) << "offset_converter:"
                    << " Sharp joint [<< inside turn " << int(joint_angle*180/M_PI)
                    << " degrees >>]";
            }
            else
            {
                // outside turn (reflex angle)
                MAPNIK_LOG_DEBUG(ctrans) << "offset_converter:"
                    << " Bulge joint >)) outside turn " << int(joint_angle*180/M_PI)
                    << " degrees ((< with " << bulge_steps << " segments";
            }
            #endif

            displace(w, v1, angle_a);
            push_vertex(w);

            for (int s = 0; ++s < bulge_steps;)
            {
                displace(w, v1, angle_a + (joint_angle * s) / bulge_steps);
                push_vertex(w);
            }

            displace(v1, angle_b);
            push_vertex(v1);
        }

        // last vertex
        displace(v1, angle_b);
        push_vertex(v1);

        // initialization finished
        return status_ = process;
    }

    unsigned output_vertex(double* px, double* py)
    {
        *px = cur_.x;
        *py = cur_.y;
        return cur_.cmd;
    }

    unsigned output_vertex(double* px, double* py, status st)
    {
        status_ = st;
        return output_vertex(px, py);
    }

    void push_vertex(vertex2d const& v)
    {
        vertices_.push_back(v);
    }

    Geometry &              geom_;
    double                  offset_;
    double                  threshold_;
    unsigned                half_turn_segments_;
    status                  status_;
    size_t                  pos_;
    std::vector<vertex2d>   vertices_;
    vertex2d                pre_first_;
    vertex2d                pre_;
    vertex2d                cur_;
};

}

#endif // MAPNIK_OFFSET_CONVERTER_HPP
<|endoftext|>