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

#include "libcef/browser/resource_request_job.h"

#include <map>
#include <vector>

#include "include/cef_callback.h"
#include "libcef/browser/cookie_manager_impl.h"
#include "libcef/browser/thread_util.h"
#include "libcef/common/request_impl.h"
#include "libcef/common/response_impl.h"

#include "base/logging.h"
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/http_user_agent_settings.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"

using net::URLRequestStatus;

namespace {

bool SetHeaderIfMissing(CefRequest::HeaderMap& headerMap,
                        const std::string& name,
                        const std::string& value) {
  if (value.empty())
    return false;

  CefRequest::HeaderMap::const_iterator it = headerMap.find(name);
  if (it == headerMap.end()) {
    headerMap.insert(std::make_pair(name, value));
    return true;
  }

  return false;
}

}  // namespace

// Client callback for asynchronous response continuation.
class CefResourceRequestJobCallback : public CefCallback {
 public:
  enum Type {
    HEADERS_AVAILABLE,
    BYTES_AVAILABLE,
  };

  explicit CefResourceRequestJobCallback(CefResourceRequestJob* job, Type type)
      : job_(job),
        type_(type),
        dest_(NULL),
        dest_size_(0) {}

  virtual void Continue() OVERRIDE {
    if (CEF_CURRENTLY_ON_IOT()) {
      // Currently on IO thread.
      // Return early if the callback has already been detached.
      if (!job_)
        return;

      if (type_ == HEADERS_AVAILABLE) {
        // Callback for headers available.
        if (!job_->has_response_started()) {
          // Send header information.
          job_->SendHeaders();
        }

        // This type of callback only ever needs to be called once.
        Detach();
      } else if (type_ == BYTES_AVAILABLE) {
        // Callback for bytes available.
        if (job_->has_response_started() &&
            job_->GetStatus().is_io_pending()) {
          // Read the bytes. They should be available but, if not, wait again.
          int bytes_read = 0;
          if (job_->ReadRawData(dest_, dest_size_, &bytes_read)) {
            if (bytes_read > 0) {
              // Clear the IO_PENDING status.
              job_->SetStatus(URLRequestStatus());

              // Notify about the available bytes.
              job_->NotifyReadComplete(bytes_read);

              dest_ = NULL;
              dest_size_ = 0;
            } else {
              // All done.
              job_->NotifyDone(URLRequestStatus());
              Detach();
            }
          } else if (!job_->GetStatus().is_io_pending()) {
            // Failed due to an error.
            NOTREACHED() <<
                "ReadRawData returned false without setting IO as pending";
            job_->NotifyDone(URLRequestStatus());
            Detach();
          }
        }
      }
    } else {
      // Execute this method on the IO thread.
      CEF_POST_TASK(CEF_IOT,
          base::Bind(&CefResourceRequestJobCallback::Continue, this));
    }
  }

  virtual void Cancel() OVERRIDE {
    if (CEF_CURRENTLY_ON_IOT()) {
      // Currently on IO thread.
      if (job_)
        job_->Kill();
    } else {
      // Execute this method on the IO thread.
      CEF_POST_TASK(CEF_IOT,
          base::Bind(&CefResourceRequestJobCallback::Cancel, this));
    }
  }

  void Detach() {
    CEF_REQUIRE_IOT();
    job_ = NULL;
  }

  void SetDestination(net::IOBuffer* dest, int dest_size) {
    CEF_REQUIRE_IOT();
    dest_ = dest;
    dest_size_ = dest_size;
  }

 private:
  CefResourceRequestJob* job_;
  Type type_;

  net::IOBuffer* dest_;
  int dest_size_;

  IMPLEMENT_REFCOUNTING(Callback);
};

CefResourceRequestJob::CefResourceRequestJob(
    net::URLRequest* request,
    net::NetworkDelegate* network_delegate,
    CefRefPtr<CefResourceHandler> handler)
    : net::URLRequestJob(request, network_delegate),
      handler_(handler),
      remaining_bytes_(0),
      response_cookies_save_index_(0),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
}

CefResourceRequestJob::~CefResourceRequestJob() {
}

void CefResourceRequestJob::Start() {
  CEF_REQUIRE_IOT();

  cef_request_ = CefRequest::Create();

  // Populate the request data.
  static_cast<CefRequestImpl*>(cef_request_.get())->Set(request_);

  // Add default headers if not already specified.
  const net::URLRequestContext* context = request_->context();
  if (context) {
    CefRequest::HeaderMap headerMap;
    cef_request_->GetHeaderMap(headerMap);
    bool changed = false;

    const net::HttpUserAgentSettings* ua_settings =
        context->http_user_agent_settings();
    if (ua_settings) {
      if (SetHeaderIfMissing(headerMap,
                             net::HttpRequestHeaders::kAcceptLanguage,
                             ua_settings->GetAcceptLanguage())) {
        changed = true;
      }

      if (SetHeaderIfMissing(headerMap,
                             net::HttpRequestHeaders::kUserAgent,
                             ua_settings->GetUserAgent(request_->url()))) {
        changed = true;
      }
    }

    if (changed)
      cef_request_->SetHeaderMap(headerMap);
  }

  AddCookieHeaderAndStart();
}

void CefResourceRequestJob::Kill() {
  CEF_REQUIRE_IOT();

  // Notify the handler that the request has been canceled.
  handler_->Cancel();

  if (callback_) {
    callback_->Detach();
    callback_ = NULL;
  }

  net::URLRequestJob::Kill();
}

bool CefResourceRequestJob::ReadRawData(net::IOBuffer* dest, int dest_size,
                                        int* bytes_read) {
  CEF_REQUIRE_IOT();

  DCHECK_NE(dest_size, 0);
  DCHECK(bytes_read);

  if (remaining_bytes_ == 0) {
    // No more data to read.
    *bytes_read = 0;
    return true;
  } else if (remaining_bytes_ > 0 && remaining_bytes_ < dest_size) {
    // The handler knows the content size beforehand.
    dest_size = static_cast<int>(remaining_bytes_);
  }

  if (!callback_.get()) {
    // Create the bytes available callback that will be used until the request
    // is completed.
    callback_ = new CefResourceRequestJobCallback(this,
        CefResourceRequestJobCallback::BYTES_AVAILABLE);
  }

  // Read response data from the handler.
  bool rv = handler_->ReadResponse(dest->data(), dest_size, *bytes_read,
                                   callback_.get());
  if (!rv) {
    // The handler has indicated completion of the request.
    *bytes_read = 0;
    return true;
  } else if (*bytes_read == 0) {
    if (!GetStatus().is_io_pending()) {
      // Report our status as IO pending.
      SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
      callback_->SetDestination(dest, dest_size);
    }
    return false;
  } else if (*bytes_read > dest_size) {
    // Normalize the return value.
    *bytes_read = dest_size;
  }

  if (remaining_bytes_ > 0)
    remaining_bytes_ -= *bytes_read;

  // Continue calling this method.
  return true;
}

void CefResourceRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
  CEF_REQUIRE_IOT();

  info->headers = GetResponseHeaders();
}

bool CefResourceRequestJob::GetResponseCookies(
    std::vector<std::string>* cookies) {
  CEF_REQUIRE_IOT();

  cookies->clear();
  FetchResponseCookies(cookies);
  return true;
}

bool CefResourceRequestJob::IsRedirectResponse(GURL* location,
                                               int* http_status_code) {
  CEF_REQUIRE_IOT();

  if (redirect_url_.is_valid()) {
    // Redirect to the new URL.
    *http_status_code = 303;
    location->Swap(&redirect_url_);
    return true;
  }

  if (response_.get()) {
    // Check for HTTP 302 or HTTP 303 redirect.
    int status = response_->GetStatus();
    if (status == 302 || status == 303) {
      CefResponse::HeaderMap headerMap;
      response_->GetHeaderMap(headerMap);
      CefRequest::HeaderMap::iterator iter = headerMap.find("Location");
      if (iter != headerMap.end()) {
          GURL new_url = GURL(std::string(iter->second));
          *http_status_code = status;
          location->Swap(&new_url);
          return true;
      }
    }
  }

  return false;
}

bool CefResourceRequestJob::GetMimeType(std::string* mime_type) const {
  CEF_REQUIRE_IOT();

  if (response_.get())
    *mime_type = response_->GetMimeType();
  return true;
}

void CefResourceRequestJob::SendHeaders() {
  CEF_REQUIRE_IOT();

  // Clear the headers available callback.
  callback_ = NULL;

  // We may have been orphaned...
  if (!request())
    return;

  response_ = new CefResponseImpl();
  remaining_bytes_ = 0;

  CefString redirectUrl;

  // Get header information from the handler.
  handler_->GetResponseHeaders(response_, remaining_bytes_, redirectUrl);
  if (!redirectUrl.empty()) {
    std::string redirectUrlStr = redirectUrl;
    redirect_url_ = GURL(redirectUrlStr);
  }

  if (remaining_bytes_ > 0)
    set_expected_content_size(remaining_bytes_);

  // Continue processing the request.
  SaveCookiesAndNotifyHeadersComplete();
}

void CefResourceRequestJob::AddCookieHeaderAndStart() {
  // No matter what, we want to report our status as IO pending since we will
  // be notifying our consumer asynchronously via OnStartCompleted.
  SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));

  // If the request was destroyed, then there is no more work to do.
  if (!request_)
    return;

  net::CookieStore* cookie_store =
      request_->context()->cookie_store();
  if (cookie_store &&
      !(request_->load_flags() & net::LOAD_DO_NOT_SEND_COOKIES)) {
    net::CookieMonster* cookie_monster = cookie_store->GetCookieMonster();
    if (cookie_monster) {
      cookie_monster->GetAllCookiesForURLAsync(
          request_->url(),
          base::Bind(&CefResourceRequestJob::CheckCookiePolicyAndLoad,
                      weak_factory_.GetWeakPtr()));
    } else {
      DoLoadCookies();
    }
  } else {
    DoStartTransaction();
  }
}

void CefResourceRequestJob::DoLoadCookies() {
  net::CookieOptions options;
  options.set_include_httponly();
  request_->context()->cookie_store()->GetCookiesWithOptionsAsync(
      request_->url(), options,
      base::Bind(&CefResourceRequestJob::OnCookiesLoaded,
                 weak_factory_.GetWeakPtr()));
}

void CefResourceRequestJob::CheckCookiePolicyAndLoad(
    const net::CookieList& cookie_list) {
  bool can_get_cookies = CanGetCookies(cookie_list);
  if (can_get_cookies) {
    net::CookieList::const_iterator it = cookie_list.begin();
    for (; it != cookie_list.end(); ++it) {
      CefCookie cookie;
      if (!CefCookieManagerImpl::GetCefCookie(*it, cookie) ||
          !handler_->CanGetCookie(cookie)) {
        can_get_cookies = false;
        break;
      }
    }
  }

  if (can_get_cookies)
    DoLoadCookies();
  else
    DoStartTransaction();
}

void CefResourceRequestJob::OnCookiesLoaded(const std::string& cookie_line) {
  if (!cookie_line.empty()) {
    CefRequest::HeaderMap headerMap;
    cef_request_->GetHeaderMap(headerMap);
    headerMap.insert(
        std::make_pair(net::HttpRequestHeaders::kCookie, cookie_line));
    cef_request_->SetHeaderMap(headerMap);
  }
  DoStartTransaction();
}

void CefResourceRequestJob::DoStartTransaction() {
  // We may have been canceled while retrieving cookies.
  if (GetStatus().is_success()) {
    StartTransaction();
  } else {
    NotifyCanceled();
  }
}

void CefResourceRequestJob::StartTransaction() {
  // Create the callback that will be used to notify when header information is
  // available.
  callback_ = new CefResourceRequestJobCallback(this,
      CefResourceRequestJobCallback::HEADERS_AVAILABLE);

  // Protect against deletion of this object.
  base::WeakPtr<CefResourceRequestJob> weak_ptr(weak_factory_.GetWeakPtr());

  // Handler can decide whether to process the request.
  bool rv = handler_->ProcessRequest(cef_request_, callback_.get());
  if (weak_ptr.get() && !rv) {
    // Cancel the request.
    NotifyCanceled();
  }
}

net::HttpResponseHeaders* CefResourceRequestJob::GetResponseHeaders() {
  DCHECK(response_);
  if (!response_headers_.get()) {
    CefResponseImpl* responseImpl =
        static_cast<CefResponseImpl*>(response_.get());
    response_headers_ = responseImpl->GetResponseHeaders();
  }
  return response_headers_;
}

void CefResourceRequestJob::SaveCookiesAndNotifyHeadersComplete() {
  if (request_->load_flags() & net::LOAD_DO_NOT_SAVE_COOKIES) {
    SetStatus(URLRequestStatus());  // Clear the IO_PENDING status
    NotifyHeadersComplete();
    return;
  }

  response_cookies_.clear();
  response_cookies_save_index_ = 0;

  FetchResponseCookies(&response_cookies_);

  // Now, loop over the response cookies, and attempt to persist each.
  SaveNextCookie();
}

void CefResourceRequestJob::SaveNextCookie() {
  if (response_cookies_save_index_ == response_cookies_.size()) {
    response_cookies_.clear();
    response_cookies_save_index_ = 0;
    SetStatus(URLRequestStatus());  // Clear the IO_PENDING status
    NotifyHeadersComplete();
    return;
  }

  // No matter what, we want to report our status as IO pending since we will
  // be notifying our consumer asynchronously via OnStartCompleted.
  SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));

  net::CookieOptions options;
  options.set_include_httponly();
  bool can_set_cookie = CanSetCookie(
      response_cookies_[response_cookies_save_index_], &options);
  if (can_set_cookie) {
    CefCookie cookie;
    if (CefCookieManagerImpl::GetCefCookie(request_->url(),
            response_cookies_[response_cookies_save_index_], cookie)) {
      can_set_cookie = handler_->CanSetCookie(cookie);
    } else {
      can_set_cookie = false;
    }
  }

  if (can_set_cookie) {
    request_->context()->cookie_store()->SetCookieWithOptionsAsync(
        request_->url(), response_cookies_[response_cookies_save_index_],
        options, base::Bind(&CefResourceRequestJob::OnCookieSaved,
                            weak_factory_.GetWeakPtr()));
    return;
  }

  CookieHandled();
}

void CefResourceRequestJob::OnCookieSaved(bool cookie_status) {
  CookieHandled();
}

void CefResourceRequestJob::CookieHandled() {
  response_cookies_save_index_++;
  // We may have been canceled within OnSetCookie.
  if (GetStatus().is_success()) {
    SaveNextCookie();
  } else {
    NotifyCanceled();
  }
}

void CefResourceRequestJob::FetchResponseCookies(
    std::vector<std::string>* cookies) {
  const std::string name = "Set-Cookie";
  std::string value;

  void* iter = NULL;
  net::HttpResponseHeaders* headers = GetResponseHeaders();
  while (headers->EnumerateHeader(&iter, name, &value)) {
    if (!value.empty())
      cookies->push_back(value);
  }
}
<commit_msg>Continue resource loading asynchronously to avoid issues during ResourceScheduler stack unwinding.<commit_after>// Copyright (c) 2012 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "libcef/browser/resource_request_job.h"

#include <map>
#include <vector>

#include "include/cef_callback.h"
#include "libcef/browser/cookie_manager_impl.h"
#include "libcef/browser/thread_util.h"
#include "libcef/common/request_impl.h"
#include "libcef/common/response_impl.h"

#include "base/logging.h"
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/http_user_agent_settings.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"

using net::URLRequestStatus;

namespace {

bool SetHeaderIfMissing(CefRequest::HeaderMap& headerMap,
                        const std::string& name,
                        const std::string& value) {
  if (value.empty())
    return false;

  CefRequest::HeaderMap::const_iterator it = headerMap.find(name);
  if (it == headerMap.end()) {
    headerMap.insert(std::make_pair(name, value));
    return true;
  }

  return false;
}

}  // namespace

// Client callback for asynchronous response continuation.
class CefResourceRequestJobCallback : public CefCallback {
 public:
  enum Type {
    HEADERS_AVAILABLE,
    BYTES_AVAILABLE,
  };

  explicit CefResourceRequestJobCallback(CefResourceRequestJob* job, Type type)
      : job_(job),
        type_(type),
        dest_(NULL),
        dest_size_(0) {}

  virtual void Continue() OVERRIDE {
    // Continue asynchronously.
    CEF_POST_TASK(CEF_IOT,
        base::Bind(&CefResourceRequestJobCallback::ContinueOnIOThread, this));
  }

  virtual void Cancel() OVERRIDE {
    // Cancel asynchronously.
    CEF_POST_TASK(CEF_IOT,
        base::Bind(&CefResourceRequestJobCallback::CancelOnIOThread, this));
  }

  void Detach() {
    CEF_REQUIRE_IOT();
    job_ = NULL;
  }

  void SetDestination(net::IOBuffer* dest, int dest_size) {
    CEF_REQUIRE_IOT();
    dest_ = dest;
    dest_size_ = dest_size;
  }

 private:
  void ContinueOnIOThread() {
    CEF_REQUIRE_IOT();

    // Return early if the callback has already been detached.
    if (!job_)
      return;

    if (type_ == HEADERS_AVAILABLE) {
      // Callback for headers available.
      if (!job_->has_response_started()) {
        // Send header information.
        job_->SendHeaders();
      }

      // This type of callback only ever needs to be called once.
      Detach();
    } else if (type_ == BYTES_AVAILABLE) {
      // Callback for bytes available.
      if (job_->has_response_started() &&
          job_->GetStatus().is_io_pending()) {
        // Read the bytes. They should be available but, if not, wait again.
        int bytes_read = 0;
        if (job_->ReadRawData(dest_, dest_size_, &bytes_read)) {
          if (bytes_read > 0) {
            // Clear the IO_PENDING status.
            job_->SetStatus(URLRequestStatus());

            // Notify about the available bytes.
            job_->NotifyReadComplete(bytes_read);

            dest_ = NULL;
            dest_size_ = 0;
          } else {
            // All done.
            job_->NotifyDone(URLRequestStatus());
            Detach();
          }
        } else if (!job_->GetStatus().is_io_pending()) {
          // Failed due to an error.
          NOTREACHED() <<
              "ReadRawData returned false without setting IO as pending";
          job_->NotifyDone(URLRequestStatus());
          Detach();
        }
      }
    }
  }

  void CancelOnIOThread() {
    CEF_REQUIRE_IOT();

    if (job_)
      job_->Kill();
  }

  CefResourceRequestJob* job_;
  Type type_;

  net::IOBuffer* dest_;
  int dest_size_;

  IMPLEMENT_REFCOUNTING(Callback);
};

CefResourceRequestJob::CefResourceRequestJob(
    net::URLRequest* request,
    net::NetworkDelegate* network_delegate,
    CefRefPtr<CefResourceHandler> handler)
    : net::URLRequestJob(request, network_delegate),
      handler_(handler),
      remaining_bytes_(0),
      response_cookies_save_index_(0),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
}

CefResourceRequestJob::~CefResourceRequestJob() {
}

void CefResourceRequestJob::Start() {
  CEF_REQUIRE_IOT();

  cef_request_ = CefRequest::Create();

  // Populate the request data.
  static_cast<CefRequestImpl*>(cef_request_.get())->Set(request_);

  // Add default headers if not already specified.
  const net::URLRequestContext* context = request_->context();
  if (context) {
    CefRequest::HeaderMap headerMap;
    cef_request_->GetHeaderMap(headerMap);
    bool changed = false;

    const net::HttpUserAgentSettings* ua_settings =
        context->http_user_agent_settings();
    if (ua_settings) {
      if (SetHeaderIfMissing(headerMap,
                             net::HttpRequestHeaders::kAcceptLanguage,
                             ua_settings->GetAcceptLanguage())) {
        changed = true;
      }

      if (SetHeaderIfMissing(headerMap,
                             net::HttpRequestHeaders::kUserAgent,
                             ua_settings->GetUserAgent(request_->url()))) {
        changed = true;
      }
    }

    if (changed)
      cef_request_->SetHeaderMap(headerMap);
  }

  AddCookieHeaderAndStart();
}

void CefResourceRequestJob::Kill() {
  CEF_REQUIRE_IOT();

  // Notify the handler that the request has been canceled.
  handler_->Cancel();

  if (callback_) {
    callback_->Detach();
    callback_ = NULL;
  }

  net::URLRequestJob::Kill();
}

bool CefResourceRequestJob::ReadRawData(net::IOBuffer* dest, int dest_size,
                                        int* bytes_read) {
  CEF_REQUIRE_IOT();

  DCHECK_NE(dest_size, 0);
  DCHECK(bytes_read);

  if (remaining_bytes_ == 0) {
    // No more data to read.
    *bytes_read = 0;
    return true;
  } else if (remaining_bytes_ > 0 && remaining_bytes_ < dest_size) {
    // The handler knows the content size beforehand.
    dest_size = static_cast<int>(remaining_bytes_);
  }

  if (!callback_.get()) {
    // Create the bytes available callback that will be used until the request
    // is completed.
    callback_ = new CefResourceRequestJobCallback(this,
        CefResourceRequestJobCallback::BYTES_AVAILABLE);
  }

  // Read response data from the handler.
  bool rv = handler_->ReadResponse(dest->data(), dest_size, *bytes_read,
                                   callback_.get());
  if (!rv) {
    // The handler has indicated completion of the request.
    *bytes_read = 0;
    return true;
  } else if (*bytes_read == 0) {
    if (!GetStatus().is_io_pending()) {
      // Report our status as IO pending.
      SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
      callback_->SetDestination(dest, dest_size);
    }
    return false;
  } else if (*bytes_read > dest_size) {
    // Normalize the return value.
    *bytes_read = dest_size;
  }

  if (remaining_bytes_ > 0)
    remaining_bytes_ -= *bytes_read;

  // Continue calling this method.
  return true;
}

void CefResourceRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
  CEF_REQUIRE_IOT();

  info->headers = GetResponseHeaders();
}

bool CefResourceRequestJob::GetResponseCookies(
    std::vector<std::string>* cookies) {
  CEF_REQUIRE_IOT();

  cookies->clear();
  FetchResponseCookies(cookies);
  return true;
}

bool CefResourceRequestJob::IsRedirectResponse(GURL* location,
                                               int* http_status_code) {
  CEF_REQUIRE_IOT();

  if (redirect_url_.is_valid()) {
    // Redirect to the new URL.
    *http_status_code = 303;
    location->Swap(&redirect_url_);
    return true;
  }

  if (response_.get()) {
    // Check for HTTP 302 or HTTP 303 redirect.
    int status = response_->GetStatus();
    if (status == 302 || status == 303) {
      CefResponse::HeaderMap headerMap;
      response_->GetHeaderMap(headerMap);
      CefRequest::HeaderMap::iterator iter = headerMap.find("Location");
      if (iter != headerMap.end()) {
          GURL new_url = GURL(std::string(iter->second));
          *http_status_code = status;
          location->Swap(&new_url);
          return true;
      }
    }
  }

  return false;
}

bool CefResourceRequestJob::GetMimeType(std::string* mime_type) const {
  CEF_REQUIRE_IOT();

  if (response_.get())
    *mime_type = response_->GetMimeType();
  return true;
}

void CefResourceRequestJob::SendHeaders() {
  CEF_REQUIRE_IOT();

  // Clear the headers available callback.
  callback_ = NULL;

  // We may have been orphaned...
  if (!request())
    return;

  response_ = new CefResponseImpl();
  remaining_bytes_ = 0;

  CefString redirectUrl;

  // Get header information from the handler.
  handler_->GetResponseHeaders(response_, remaining_bytes_, redirectUrl);
  if (!redirectUrl.empty()) {
    std::string redirectUrlStr = redirectUrl;
    redirect_url_ = GURL(redirectUrlStr);
  }

  if (remaining_bytes_ > 0)
    set_expected_content_size(remaining_bytes_);

  // Continue processing the request.
  SaveCookiesAndNotifyHeadersComplete();
}

void CefResourceRequestJob::AddCookieHeaderAndStart() {
  // No matter what, we want to report our status as IO pending since we will
  // be notifying our consumer asynchronously via OnStartCompleted.
  SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));

  // If the request was destroyed, then there is no more work to do.
  if (!request_)
    return;

  net::CookieStore* cookie_store =
      request_->context()->cookie_store();
  if (cookie_store &&
      !(request_->load_flags() & net::LOAD_DO_NOT_SEND_COOKIES)) {
    net::CookieMonster* cookie_monster = cookie_store->GetCookieMonster();
    if (cookie_monster) {
      cookie_monster->GetAllCookiesForURLAsync(
          request_->url(),
          base::Bind(&CefResourceRequestJob::CheckCookiePolicyAndLoad,
                      weak_factory_.GetWeakPtr()));
    } else {
      DoLoadCookies();
    }
  } else {
    DoStartTransaction();
  }
}

void CefResourceRequestJob::DoLoadCookies() {
  net::CookieOptions options;
  options.set_include_httponly();
  request_->context()->cookie_store()->GetCookiesWithOptionsAsync(
      request_->url(), options,
      base::Bind(&CefResourceRequestJob::OnCookiesLoaded,
                 weak_factory_.GetWeakPtr()));
}

void CefResourceRequestJob::CheckCookiePolicyAndLoad(
    const net::CookieList& cookie_list) {
  bool can_get_cookies = CanGetCookies(cookie_list);
  if (can_get_cookies) {
    net::CookieList::const_iterator it = cookie_list.begin();
    for (; it != cookie_list.end(); ++it) {
      CefCookie cookie;
      if (!CefCookieManagerImpl::GetCefCookie(*it, cookie) ||
          !handler_->CanGetCookie(cookie)) {
        can_get_cookies = false;
        break;
      }
    }
  }

  if (can_get_cookies)
    DoLoadCookies();
  else
    DoStartTransaction();
}

void CefResourceRequestJob::OnCookiesLoaded(const std::string& cookie_line) {
  if (!cookie_line.empty()) {
    CefRequest::HeaderMap headerMap;
    cef_request_->GetHeaderMap(headerMap);
    headerMap.insert(
        std::make_pair(net::HttpRequestHeaders::kCookie, cookie_line));
    cef_request_->SetHeaderMap(headerMap);
  }
  DoStartTransaction();
}

void CefResourceRequestJob::DoStartTransaction() {
  // We may have been canceled while retrieving cookies.
  if (GetStatus().is_success()) {
    StartTransaction();
  } else {
    NotifyCanceled();
  }
}

void CefResourceRequestJob::StartTransaction() {
  // Create the callback that will be used to notify when header information is
  // available.
  callback_ = new CefResourceRequestJobCallback(this,
      CefResourceRequestJobCallback::HEADERS_AVAILABLE);

  // Protect against deletion of this object.
  base::WeakPtr<CefResourceRequestJob> weak_ptr(weak_factory_.GetWeakPtr());

  // Handler can decide whether to process the request.
  bool rv = handler_->ProcessRequest(cef_request_, callback_.get());
  if (weak_ptr.get() && !rv) {
    // Cancel the request.
    NotifyCanceled();
  }
}

net::HttpResponseHeaders* CefResourceRequestJob::GetResponseHeaders() {
  DCHECK(response_);
  if (!response_headers_.get()) {
    CefResponseImpl* responseImpl =
        static_cast<CefResponseImpl*>(response_.get());
    response_headers_ = responseImpl->GetResponseHeaders();
  }
  return response_headers_;
}

void CefResourceRequestJob::SaveCookiesAndNotifyHeadersComplete() {
  if (request_->load_flags() & net::LOAD_DO_NOT_SAVE_COOKIES) {
    SetStatus(URLRequestStatus());  // Clear the IO_PENDING status
    NotifyHeadersComplete();
    return;
  }

  response_cookies_.clear();
  response_cookies_save_index_ = 0;

  FetchResponseCookies(&response_cookies_);

  // Now, loop over the response cookies, and attempt to persist each.
  SaveNextCookie();
}

void CefResourceRequestJob::SaveNextCookie() {
  if (response_cookies_save_index_ == response_cookies_.size()) {
    response_cookies_.clear();
    response_cookies_save_index_ = 0;
    SetStatus(URLRequestStatus());  // Clear the IO_PENDING status
    NotifyHeadersComplete();
    return;
  }

  // No matter what, we want to report our status as IO pending since we will
  // be notifying our consumer asynchronously via OnStartCompleted.
  SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));

  net::CookieOptions options;
  options.set_include_httponly();
  bool can_set_cookie = CanSetCookie(
      response_cookies_[response_cookies_save_index_], &options);
  if (can_set_cookie) {
    CefCookie cookie;
    if (CefCookieManagerImpl::GetCefCookie(request_->url(),
            response_cookies_[response_cookies_save_index_], cookie)) {
      can_set_cookie = handler_->CanSetCookie(cookie);
    } else {
      can_set_cookie = false;
    }
  }

  if (can_set_cookie) {
    request_->context()->cookie_store()->SetCookieWithOptionsAsync(
        request_->url(), response_cookies_[response_cookies_save_index_],
        options, base::Bind(&CefResourceRequestJob::OnCookieSaved,
                            weak_factory_.GetWeakPtr()));
    return;
  }

  CookieHandled();
}

void CefResourceRequestJob::OnCookieSaved(bool cookie_status) {
  CookieHandled();
}

void CefResourceRequestJob::CookieHandled() {
  response_cookies_save_index_++;
  // We may have been canceled within OnSetCookie.
  if (GetStatus().is_success()) {
    SaveNextCookie();
  } else {
    NotifyCanceled();
  }
}

void CefResourceRequestJob::FetchResponseCookies(
    std::vector<std::string>* cookies) {
  const std::string name = "Set-Cookie";
  std::string value;

  void* iter = NULL;
  net::HttpResponseHeaders* headers = GetResponseHeaders();
  while (headers->EnumerateHeader(&iter, name, &value)) {
    if (!value.empty())
      cookies->push_back(value);
  }
}
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993-2016 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "common.h"
#include "ConfigFileManager.h"
#include "FileManager.h"
#include "CommandManager.h"
#include "StateDatabase.h"
#include "KeyManager.h"

static const int	MaximumLineLength = 1024;

// initialize the singleton
template <>
ConfigFileManager* Singleton<ConfigFileManager>::_instance = (ConfigFileManager*)0;

void writeBZDB(const std::string& name, void *stream)
{
  std::ostream& s = *static_cast<std::ostream*>(stream);
  std::string value = BZDB.get(name);
  std::string defaultVal = BZDB.getDefault(name);
  std::string newkey;
  bool commentOut = (value == defaultVal);

  // quotify anything with a space and empty strings
  if ((value.find(' ') != value.npos) || (value.size() == 0)) {
    value = std::string("\"") + value + "\"";
  }

  // quotify the key if there's a space
  if (name.find(' ') != name.npos)
    newkey = std::string("\"") + name + "\"";
  else
    newkey = name;

  s << (commentOut ? "#set " : "set ") << newkey << ' ' << value << std::endl;
}

void writeKEYMGR(const std::string& name, bool press, const std::string& command, void* stream)
{
  std::ostream& s = *static_cast<std::ostream*>(stream);
  // quotify anything with a space
  std::string value = name;
  if (value.find(' ') != value.npos)
    value = std::string("\"") + value + "\"";
  s << "bind " << value << ' ' << (press ? "down " : "up ");
  value = command;
  if (value.find(' ') != value.npos)
    value = std::string("\"") + value + "\"";
  s << value << std::endl;
}

ConfigFileManager::ConfigFileManager()
{
  // do nothing
}

ConfigFileManager::~ConfigFileManager()
{
}

bool				ConfigFileManager::parse(std::istream& stream)
{
  char buffer[MaximumLineLength];
  while (stream.good()) {
    stream.getline(buffer, MaximumLineLength);
    CMDMGR.run(buffer);
  }
  return true;
}

bool				ConfigFileManager::read(const std::string& filename)
{
  std::istream* stream = FILEMGR.createDataInStream(filename);
  if (stream == NULL) {
    return false;
  }
  bool ret = parse(*stream);
  delete stream;
  return ret;
}

void				ConfigFileManager::read(std::istream& stream)
{
  parse(stream);
}

bool				ConfigFileManager::write(const std::string& filename)
{
  std::ostream* stream = FILEMGR.createDataOutStream(filename);
  if (stream == NULL) {
    return false;
  }
  BZDB.write(writeBZDB, stream);
  KEYMGR.iterate(writeKEYMGR, stream);
  delete stream;
  return true;
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>Properly store configuration values with semicolons<commit_after>/* bzflag
 * Copyright (c) 1993-2016 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "common.h"
#include "ConfigFileManager.h"
#include "FileManager.h"
#include "CommandManager.h"
#include "StateDatabase.h"
#include "KeyManager.h"

static const int	MaximumLineLength = 1024;

// initialize the singleton
template <>
ConfigFileManager* Singleton<ConfigFileManager>::_instance = (ConfigFileManager*)0;

void writeBZDB(const std::string& name, void *stream)
{
  std::ostream& s = *static_cast<std::ostream*>(stream);
  std::string value = BZDB.get(name);
  std::string defaultVal = BZDB.getDefault(name);
  std::string newkey;
  bool commentOut = (value == defaultVal);

  // quotify empty strings and anything with a space or a semicolon
  if ((value.find(' ') != value.npos) || (value.find(';') != value.npos) || (value.size() == 0)) {
    value = std::string("\"") + value + "\"";
  }

  // quotify the key if there's a space or a semicolon
  if ((name.find(' ') != name.npos) || (name.find(';') != name.npos))
    newkey = std::string("\"") + name + "\"";
  else
    newkey = name;

  s << (commentOut ? "#set " : "set ") << newkey << ' ' << value << std::endl;
}

void writeKEYMGR(const std::string& name, bool press, const std::string& command, void* stream)
{
  std::ostream& s = *static_cast<std::ostream*>(stream);
  // quotify anything with a space
  std::string value = name;
  if (value.find(' ') != value.npos)
    value = std::string("\"") + value + "\"";
  s << "bind " << value << ' ' << (press ? "down " : "up ");
  value = command;
  if (value.find(' ') != value.npos)
    value = std::string("\"") + value + "\"";
  s << value << std::endl;
}

ConfigFileManager::ConfigFileManager()
{
  // do nothing
}

ConfigFileManager::~ConfigFileManager()
{
}

bool				ConfigFileManager::parse(std::istream& stream)
{
  char buffer[MaximumLineLength];
  while (stream.good()) {
    stream.getline(buffer, MaximumLineLength);
    CMDMGR.run(buffer);
  }
  return true;
}

bool				ConfigFileManager::read(const std::string& filename)
{
  std::istream* stream = FILEMGR.createDataInStream(filename);
  if (stream == NULL) {
    return false;
  }
  bool ret = parse(*stream);
  delete stream;
  return ret;
}

void				ConfigFileManager::read(std::istream& stream)
{
  parse(stream);
}

bool				ConfigFileManager::write(const std::string& filename)
{
  std::ostream* stream = FILEMGR.createDataOutStream(filename);
  if (stream == NULL) {
    return false;
  }
  BZDB.write(writeBZDB, stream);
  KEYMGR.iterate(writeKEYMGR, stream);
  delete stream;
  return true;
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>/*
Tyra Draper, Section 1, tyra.draper@gmail.com
Purpose: implement the Lexer.h file
*/

#include "Lexer.h"

void Lexer::analyze() {
	while (ifs.peek() != EOF) {
		if (whitespace()) { continue; }
		if (alpha()) { continue; }
		if (str()) { continue; }
		if (comment()) { continue; }
		symbol();
	}
	tokens.push_back(new Token(MY_EOF, "", curLine));
	curToken = 0;
//	print();
}

bool Lexer::whitespace() {
	if (ifs.peek() == '\n') {
		ifs.get();
		curLine++;
		return true;
	}
	else if (isspace(ifs.peek())) {
		ifs.get();
		return true;
	}
	return false;
}

bool Lexer::alpha() {
	string x = "";
	while (isalpha(ifs.peek())) {
		x += ifs.get();
	}
	if (x.length() == 0) {
		return false;
	}
	if (isdigit(ifs.peek())) {
		while (isalpha(ifs.peek()) || isdigit(ifs.peek())) {
			x += ifs.get();
		}
	}
	else {
		if (x == "Schemes") {
			tokens.push_back(new Token(SCHEMES, x, curLine));
			return true;
		}
		else if (x == "Facts") {
			tokens.push_back(new Token(FACTS, x, curLine));
			return true;
		}
		else if (x == "Rules") {
			tokens.push_back(new Token(RULES, x, curLine));
			return true;
		}
		else if (x == "Queries") {
			tokens.push_back(new Token(QUERIES, x, curLine));
			return true;
		}
	}
	tokens.push_back(new Token(ID, x, curLine));
	return true;
}

bool Lexer::str() {
	if (ifs.peek() != '\'') {
		return false;
	}
	string x(1, ifs.get());
	int newLines = 0;
	while (ifs.peek() != EOF) {
		if (ifs.peek() == '\'') {
			x += ifs.get();
			if (ifs.peek() != '\'') {
				tokens.push_back(new Token(STRING, x, curLine));
				curLine += newLines;
				return true;
			}
		}
		else if (ifs.peek() == '\n') {
			newLines++;
		}
		x += ifs.get();
	}
	tokens.push_back(new Token(UNDEFINED, x, curLine));
	curLine += newLines;
	return true;
}

bool Lexer::comment() {
	if (ifs.peek() != '#') {
		return false;
	}
	ifs.get();
	if (ifs.peek() == '|') {
		return block();
	}
	else {
		return line();
	}
}

bool Lexer::block() {
	string x = "#";
	x += ifs.get();
	int newLines = 0;
	while (ifs.peek() != EOF) {
		if (ifs.peek() == '|') {
			x += ifs.get();
			if (ifs.peek() == '#') {
				x += ifs.get();
//				tokens.push_back(new Token(COMMENT, x, curLine));
				curLine += newLines;
				return true;
			}
		}
		if (ifs.peek() == '\n') {
			newLines++;
		}
		x += ifs.get();
	}
//	tokens.push_back(new Token(UNDEFINED, x, curLine));
	curLine += newLines;
	return true;
}

bool Lexer::line() {
	string x = "#";
	while (ifs.peek() != '\n' && ifs.peek() != EOF) {
		x += ifs.get();
	}
//	tokens.push_back(new Token(COMMENT, x, curLine));
	return true;
}

bool Lexer::symbol() {
	string x(1, ifs.get());
	if (x == ",") {
		tokens.push_back(new Token(COMMA, x, curLine));
		return true;
	}
	if (x == ".") {
		tokens.push_back(new Token(PERIOD, x, curLine));
		return true;
	}
	if (x == "?") {
		tokens.push_back(new Token(Q_MARK, x, curLine));
		return true;
	}
	if (x == "(") {
		tokens.push_back(new Token(LEFT_PAREN, x, curLine));
		return true;
	}
	if (x == ")") {
		tokens.push_back(new Token(RIGHT_PAREN, x, curLine));
		return true;
	}
	if (x == "*") {
		tokens.push_back(new Token(MULTIPLY, x, curLine));
		return true;
	}
	if (x == "+") {
		tokens.push_back(new Token(ADD, x, curLine));
		return true;
	}
	if (x == ":") {
		if (ifs.peek() == '-') {
			x += ifs.get();
			tokens.push_back(new Token(COLON_DASH, x, curLine));
			return true;
		}
		tokens.push_back(new Token(COLON, x, curLine));
		return true;
	}
	else {
		tokens.push_back(new Token(UNDEFINED, x, curLine));
		throw tokens.at(tokens.size() - 1);
	}
	return false;
}

void Lexer::print() {
	for (Token* t : tokens) {
		cout << t->toString() << endl;
	}
	cout << "Total Tokens = " << tokens.size() << endl;
}

Token* Lexer::next(TokenType a) {
	if (curToken < tokens.size()) {
		Token* result = tokens.at(curToken);
		if (result->type() != a || result->type() == UNDEFINED) {
			throw result;
		}
		curToken++;
		return result;
	}
	else {
		cout << "ya went too far";
		return tokens.at(curToken - 1);
	}
}<commit_msg>remove testing for undefined<commit_after>/*
Tyra Draper, Section 1, tyra.draper@gmail.com
Purpose: implement the Lexer.h file
*/

#include "Lexer.h"

void Lexer::analyze() {
	while (ifs.peek() != EOF) {
		if (whitespace()) { continue; }
		if (alpha()) { continue; }
		if (str()) { continue; }
		if (comment()) { continue; }
		symbol();
	}
	tokens.push_back(new Token(MY_EOF, "", curLine));
	curToken = 0;
//	print();
}

bool Lexer::whitespace() {
	if (ifs.peek() == '\n') {
		ifs.get();
		curLine++;
		return true;
	}
	else if (isspace(ifs.peek())) {
		ifs.get();
		return true;
	}
	return false;
}

bool Lexer::alpha() {
	string x = "";
	while (isalpha(ifs.peek())) {
		x += ifs.get();
	}
	if (x.length() == 0) {
		return false;
	}
	if (isdigit(ifs.peek())) {
		while (isalpha(ifs.peek()) || isdigit(ifs.peek())) {
			x += ifs.get();
		}
	}
	else {
		if (x == "Schemes") {
			tokens.push_back(new Token(SCHEMES, x, curLine));
			return true;
		}
		else if (x == "Facts") {
			tokens.push_back(new Token(FACTS, x, curLine));
			return true;
		}
		else if (x == "Rules") {
			tokens.push_back(new Token(RULES, x, curLine));
			return true;
		}
		else if (x == "Queries") {
			tokens.push_back(new Token(QUERIES, x, curLine));
			return true;
		}
	}
	tokens.push_back(new Token(ID, x, curLine));
	return true;
}

bool Lexer::str() {
	if (ifs.peek() != '\'') {
		return false;
	}
	string x(1, ifs.get());
	int newLines = 0;
	while (ifs.peek() != EOF) {
		if (ifs.peek() == '\'') {
			x += ifs.get();
			if (ifs.peek() != '\'') {
				tokens.push_back(new Token(STRING, x, curLine));
				curLine += newLines;
				return true;
			}
		}
		else if (ifs.peek() == '\n') {
			newLines++;
		}
		x += ifs.get();
	}
	tokens.push_back(new Token(UNDEFINED, x, curLine));
	curLine += newLines;
	return true;
}

bool Lexer::comment() {
	if (ifs.peek() != '#') {
		return false;
	}
	ifs.get();
	if (ifs.peek() == '|') {
		return block();
	}
	else {
		return line();
	}
}

bool Lexer::block() {
	string x = "#";
	x += ifs.get();
	int newLines = 0;
	while (ifs.peek() != EOF) {
		if (ifs.peek() == '|') {
			x += ifs.get();
			if (ifs.peek() == '#') {
				x += ifs.get();
//				tokens.push_back(new Token(COMMENT, x, curLine));
				curLine += newLines;
				return true;
			}
		}
		if (ifs.peek() == '\n') {
			newLines++;
		}
		x += ifs.get();
	}
//	tokens.push_back(new Token(UNDEFINED, x, curLine));
	curLine += newLines;
	return true;
}

bool Lexer::line() {
	string x = "#";
	while (ifs.peek() != '\n' && ifs.peek() != EOF) {
		x += ifs.get();
	}
//	tokens.push_back(new Token(COMMENT, x, curLine));
	return true;
}

bool Lexer::symbol() {
	string x(1, ifs.get());
	if (x == ",") {
		tokens.push_back(new Token(COMMA, x, curLine));
		return true;
	}
	if (x == ".") {
		tokens.push_back(new Token(PERIOD, x, curLine));
		return true;
	}
	if (x == "?") {
		tokens.push_back(new Token(Q_MARK, x, curLine));
		return true;
	}
	if (x == "(") {
		tokens.push_back(new Token(LEFT_PAREN, x, curLine));
		return true;
	}
	if (x == ")") {
		tokens.push_back(new Token(RIGHT_PAREN, x, curLine));
		return true;
	}
	if (x == "*") {
		tokens.push_back(new Token(MULTIPLY, x, curLine));
		return true;
	}
	if (x == "+") {
		tokens.push_back(new Token(ADD, x, curLine));
		return true;
	}
	if (x == ":") {
		if (ifs.peek() == '-') {
			x += ifs.get();
			tokens.push_back(new Token(COLON_DASH, x, curLine));
			return true;
		}
		tokens.push_back(new Token(COLON, x, curLine));
		return true;
	}
	else {
		tokens.push_back(new Token(UNDEFINED, x, curLine));
		throw tokens.at(tokens.size() - 1);
	}
	return false;
}

void Lexer::print() {
	for (Token* t : tokens) {
		cout << t->toString() << endl;
	}
	cout << "Total Tokens = " << tokens.size() << endl;
}

Token* Lexer::next(TokenType a) {
	if (curToken < tokens.size()) {
		Token* result = tokens.at(curToken);
		if (result->type() != a) {
			throw result;
		}
		curToken++;
		return result;
	}
	else {
		cout << "ya went too far";
		return tokens.at(curToken - 1);
	}
}<|endoftext|>


<commit_before>#include "webcam.hpp"

using namespace cv;
using namespace std;

int main (int argc, char** argv) {

 CvCapture* capture = 0;
 int width, height, fps;

 capture = cvCaptureFromCAM(0);

 if (!capture) {
  printf("No camera detected!");
  return -1;
 }

 ifstream configFile (".config");

 if (configFile.is_open()) {

  //probably want to support corrupted .config
  string line;
  getline(configFile, line);
  istringstream(line)>>width;
  getline(configFile, line);
  istringstream(line)>>height;
  cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, width);
  cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, height);

  configFile.close();

 } else {

  initResolutions();

  for (int i=36; i<150; i++) {
   cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, resolutions[i].width);
   cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, resolutions[i].height);
  }

  width = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
  height = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);

  ofstream configFileOut(".config");
  configFileOut << width;
  configFileOut << "\n";
  configFileOut << height;
  configFileOut << "\n";
  configFileOut.close();
 }

 bool keepGoing = true;

// srand(890);//not interested in good randomness

 Mat image;
 Mat channel[3];

 while (keepGoing) {

  image = cvQueryFrame(capture);
  imshow("webcam", image);

// thresholds on dark regions

  Mat gray, blurred_gray, threshold_gray;
  cvtColor(image, gray, CV_BGR2GRAY);
  blur(gray, blurred_gray, Size(width/20,height/20));
  equalizeHist(blurred_gray, blurred_gray);

  threshold(blurred_gray, threshold_gray, 40, 1, THRESH_BINARY_INV);
  imshow("threshold", threshold_gray.mul(gray));

  keepGoing = (waitKey(25)<0);

 }

 cvReleaseCapture(&capture);

 return 0;
}
<commit_msg>hacking<commit_after>#include "webcam.hpp"

using namespace cv;
using namespace std;

int main (int argc, char** argv) {

 CvCapture* capture = 0;
 int width, height, fps;

 capture = cvCaptureFromCAM(0);

 if (!capture) {
  printf("No camera detected!");
  return -1;
 }

 ifstream configFile (".config");

 if (configFile.is_open()) {

  //probably want to support corrupted .config
  string line;
  getline(configFile, line);
  istringstream(line)>>width;
  getline(configFile, line);
  istringstream(line)>>height;
  cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, width);
  cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, height);

  configFile.close();

 } else {

  initResolutions();

  for (int i=36; i<150; i++) {
   cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, resolutions[i].width);
   cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, resolutions[i].height);
  }

  width = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
  height = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);

  ofstream configFileOut(".config");
  configFileOut << width;
  configFileOut << "\n";
  configFileOut << height;
  configFileOut << "\n";
  configFileOut.close();
 }

 bool keepGoing = true;

// srand(890);//not interested in good randomness

 Mat image;
 Mat channel[3];

 while (keepGoing) {

  image = cvQueryFrame(capture);
  imshow("webcam", image);

// thresholds on dark regions

  Mat gray, blurred_gray, threshold_gray;
  cvtColor(image, gray, CV_BGR2GRAY);
  blur(gray, blurred_gray, Size(width/10,height/20));
  equalizeHist(blurred_gray, blurred_gray);

  threshold(blurred_gray, threshold_gray, 40, 1, THRESH_BINARY_INV);
  imshow("threshold", threshold_gray.mul(gray));

  keepGoing = (waitKey(25)<0);

 }

 cvReleaseCapture(&capture);

 return 0;
}
<|endoftext|>


<commit_before>/******************************************************************
 *
 * uSQL for C++
 *
 * Copyright (C) Satoshi Konno 2012
 *
 * This is licensed under BSD-style license, see file COPYING.
 *
 ******************************************************************/

#include "MySQLProxy.h"

using namespace std;
using namespace uSQL;

static const char *USQL_MYSQL_PROXY_MEMCACHED_HOST = "192.168.100.111";

uSQL::MySQLProxy::MySQLProxy()
{
    this->mySQL = mysql_init(NULL);
}

uSQL::MySQLProxy::~MySQLProxy()
{
    mysql_close(this->mySQL);
}

bool uSQL::MySQLProxy::connect(const std::string &host, const std::string &user, const std::string &passwd, const std::string &database)
{
    if (!MemcachedProxy::connect(USQL_MYSQL_PROXY_MEMCACHED_HOST))
        return false;
    if (!mysql_real_connect(this->mySQL, host.c_str(), user.c_str(), passwd.c_str(), database.c_str(), 0, NULL, 0))
        return false;
    return true;
}

bool uSQL::MySQLProxy::execCommand(SQLStatement *stmt, SQLProxyResult &result)
{
    string hashKey;
    if (getKey(stmt, hashKey, result)) {
        SQLError sqlError;
        MemcachedProxy::remove(stmt, result);
    }

    return true;
}

bool uSQL::MySQLProxy::select(SQLStatement *stmt, SQLProxyResult &result)
{
    string hashKey;
    if (getKey(stmt, hashKey, result) == false)
        return false;

    if (MemcachedProxy::get(stmt, result)) {
        result.setCashed(true);
        return true;
    }

    string stmtString;
    stmt->toString(stmtString);
    if (mysql_query(this->mySQL, stmtString.c_str()) != 0)
        return false;

    bool selectlResult = false;

    MYSQL_RES *mySQLRes;
    if ((mySQLRes = mysql_store_result(this->mySQL))) {
        int numRows = mysql_num_rows(mySQLRes);
        if (0 < numRows) {
            MYSQL_FIELD *mySQLField;
            std::vector<std::string> keys;
            while ((mySQLField = mysql_fetch_field(mySQLRes)))
                keys.push_back(mySQLField->name);
            MYSQL_ROW mySQLRow = mysql_fetch_row(mySQLRes);
            if (mySQLRow) {
                SQLProxyDataSet *resultSet = result.getResultSet();
                int numFields = mysql_num_fields(mySQLRes);
                for (int n=0 ; n<numFields; n++) {
                    string key = keys.at(n);
                    resultSet->set(key, mySQLRow[n] ? mySQLRow[n] : "");
                }
                selectlResult = set(stmt, resultSet, result);
            }
        }
        mysql_free_result(mySQLRes) ;
    }

    return selectlResult;
}

bool uSQL::MySQLProxy::query(SQLStatement *stmt, SQLProxyResult &result)
{
    result.clear();

    SQLCommand *sqlCmd = stmt->getCommandNode();

    if (!sqlCmd) {
        result.setErrorMessage("COMMAND section was not found");
        return false;
    }

    bool execResult = false;

    if (sqlCmd->isSelect()) {
        execResult = select(stmt, result);
    }
    else {
        execResult = execCommand(stmt, result);
    }

    return execResult;
}
<commit_msg><commit_after>/******************************************************************
 *
 * uSQL for C++
 *
 * Copyright (C) Satoshi Konno 2012
 *
 * This is licensed under BSD-style license, see file COPYING.
 *
 ******************************************************************/

#include "MySQLProxy.h"

using namespace std;
using namespace uSQL;

static const char *USQL_MYSQL_PROXY_MEMCACHED_HOST = "192.168.100.111";

uSQL::MySQLProxy::MySQLProxy()
{
    this->mySQL = mysql_init(NULL);
}

uSQL::MySQLProxy::~MySQLProxy()
{
    mysql_close(this->mySQL);
}

bool uSQL::MySQLProxy::connect(const std::string &host, const std::string &user, const std::string &passwd, const std::string &database)
{
    if (!MemcachedProxy::connect(USQL_MYSQL_PROXY_MEMCACHED_HOST))
        return false;
    if (!mysql_real_connect(this->mySQL, host.c_str(), user.c_str(), passwd.c_str(), database.c_str(), 0, NULL, 0))
        return false;
    return true;
}

bool uSQL::MySQLProxy::execCommand(SQLStatement *stmt, SQLProxyResult &result)
{
    string hashKey;
    if (getKey(stmt, hashKey, result)) {
        SQLError sqlError;
        MemcachedProxy::remove(stmt, result);
    }

    string stmtString;
    stmt->toString(stmtString);
    if (mysql_query(this->mySQL, stmtString.c_str()) != 0)
        return false;

    return true;
}

bool uSQL::MySQLProxy::select(SQLStatement *stmt, SQLProxyResult &result)
{
    string hashKey;
    if (getKey(stmt, hashKey, result) == false)
        return false;

    if (MemcachedProxy::get(stmt, result)) {
        result.setCashed(true);
        return true;
    }

    string stmtString;
    stmt->toString(stmtString);
    if (mysql_query(this->mySQL, stmtString.c_str()) != 0)
        return false;

    bool selectlResult = false;

    MYSQL_RES *mySQLRes;
    if ((mySQLRes = mysql_store_result(this->mySQL))) {
        int numRows = mysql_num_rows(mySQLRes);
        if (0 < numRows) {
            MYSQL_FIELD *mySQLField;
            std::vector<std::string> keys;
            while ((mySQLField = mysql_fetch_field(mySQLRes)))
                keys.push_back(mySQLField->name);
            MYSQL_ROW mySQLRow = mysql_fetch_row(mySQLRes);
            if (mySQLRow) {
                SQLProxyDataSet *resultSet = result.getResultSet();
                int numFields = mysql_num_fields(mySQLRes);
                for (int n=0 ; n<numFields; n++) {
                    string key = keys.at(n);
                    resultSet->set(key, mySQLRow[n] ? mySQLRow[n] : "");
                }
                selectlResult = set(stmt, resultSet, result);
            }
        }
        mysql_free_result(mySQLRes) ;
    }

    return selectlResult;
}

bool uSQL::MySQLProxy::query(SQLStatement *stmt, SQLProxyResult &result)
{
    result.clear();

    SQLCommand *sqlCmd = stmt->getCommandNode();

    if (!sqlCmd) {
        result.setErrorMessage("COMMAND section was not found");
        return false;
    }

    bool execResult = false;

    if (sqlCmd->isSelect()) {
        execResult = select(stmt, result);
    }
    else {
        execResult = execCommand(stmt, result);
    }

    return execResult;
}
<|endoftext|>


<commit_before>/*
    Tests for the emoticon engine

    Copyright (c) 2003      by Martijn Klingens       <klingens@kde.org>

    Kopete    (c) 2002-2003 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#include <qtextstream.h>

#include <kaboutdata.h>
#include <kapplication.h>
#include <kcmdlineargs.h>
#include <kdebug.h>
#include <kglobal.h>
#include <kstandarddirs.h>

#include "kopeteemoticons.h"
#include "kopeteprefs.h"

static QTextStream _out( stdout, IO_WriteOnly );

/*
  There are three sets of tests, the Kopete 0.7 baseline with tests that were
  working properly in Kopete 0.7.x. When these fail it's a real regression.

  The second set are those known to work in the current codebase.

  The last set is the set with tests that are known to fail right now.

  Each entry in a set consists of two strings. The first string is the input,
  the second string is the EXPECTED output. If the two differ the test is
  considered failed and the actual result is output for comparison.

  A set should end with an entry containing at least one null pointer.
*/

typedef const char * TestSet[][ 2 ];

static TestSet kopete07Baseline =
{
	{ NULL, NULL }
};

static TestSet knownGood =
{
	{ ":):)", ""
			  "" },
	{ "", "" },
	{ "End of sentence:p", "End of sentence" },
	{ "http://www.kde.org", "http://www.kde.org" },
	{ NULL, NULL }
};

static TestSet knownBroken =
{
	{ ">:-)", "" },
	{ ":))", ":))" },
	{ "In a sentence:practical example", "In a sentence:practical example" },
	{ "Bla ( )", "Bla ( )" },
	{ ":D and :-D are not the same as :d and :-d", " and  are not the same as :d and :-d" },
	// A future emoticon theme may very well have an emoticon for :d/:-d (an
	// upward tongue expression - Casey
	{ "4d:D>:)F:/>:-(:Pu:d9", "4d:D>:)F:/>:-(:Pu:d9" },
	{ "<::pvar:: test=1>", "<::pvar:: test=1>" },
	{ "a non-breaking space ( ) character", "a non-breaking space ( ) character" },
	{ "-+-[-:-(-:-)-:-]-+-", "-+-[-:-(-:-)-:-]-+-" },
	{ "::shrugs::", "::shrugs::" },
	{ NULL, NULL }
};

void runTests( QString description, TestSet tests )
{
	// Detect the image path by copying some code from kopeteemoticons.cpp
	// Use the KMess-Cartoon theme because it has a smiley for the troublesome ':/' pattern, which
	// also exists in http:// URIs. (Default doesn't have such a smiley, making it useless for
	// the test.)
	QString path = KGlobal::dirs()->findResource( "data", "kopete/pics/emoticons/KMess-Cartoon/smile.png" ).replace( "smile.png", QString::null );

	_out << endl;
	_out << "* Running test set '" << description << "'" << endl;

	uint i = 0;
	while ( tests[ i ][ 0 ] && tests[ i ][ 1 ] )
	{
		QString result = KopeteEmoticons::parseEmoticons( tests[ i ][ 0 ] ).replace( path, QString::null );

		if ( result == tests[ i ][ 1 ] )
		{
			_out << "  - Succeeded test for '" << tests[ i ][ 0 ] << "'" << endl;
		}
		else
		{
			_out << "  - FAILED test for '" << tests[ i ][ 0 ] << "'" << endl;
			_out << "    Expected output: '" << tests[ i ][ 1 ] << "'" << endl;
			_out << "    Real output:     '" << result << "'" << endl;
		}

		i++;
	}
}

int main( int argc, char *argv[] )
{
	KAboutData aboutData( "kopeteemoticontest", "kopeteemoticontest", "version" );
	KCmdLineArgs::init( argc, argv, &aboutData );
	KApplication app( "kopeteemoticontest" );

	// Set prefs (but don't save them :)
	KopetePrefs::prefs()->setUseEmoticons( true );
	KopetePrefs::prefs()->setIconTheme( "KMess-Cartoon" );

	runTests( "Baseline of working emoticons in Kopete 0.7", kopete07Baseline );
	runTests( "Known working tests", knownGood );
	runTests( "Known broken tests", knownBroken );

	return 0;
}

// vim: set noet ts=4 sts=4 sw=4:

<commit_msg>New test (bug 92890) CCBUG: 92890<commit_after>/*
    Tests for the emoticon engine

    Copyright (c) 2003      by Martijn Klingens       <klingens@kde.org>

    Kopete    (c) 2002-2003 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#include <qtextstream.h>

#include <kaboutdata.h>
#include <kapplication.h>
#include <kcmdlineargs.h>
#include <kdebug.h>
#include <kglobal.h>
#include <kstandarddirs.h>

#include "kopeteemoticons.h"
#include "kopeteprefs.h"

static QTextStream _out( stdout, IO_WriteOnly );

/*
  There are three sets of tests, the Kopete 0.7 baseline with tests that were
  working properly in Kopete 0.7.x. When these fail it's a real regression.

  The second set are those known to work in the current codebase.

  The last set is the set with tests that are known to fail right now.

  Each entry in a set consists of two strings. The first string is the input,
  the second string is the EXPECTED output. If the two differ the test is
  considered failed and the actual result is output for comparison.

  A set should end with an entry containing at least one null pointer.
*/

typedef const char * TestSet[][ 2 ];

static TestSet kopete07Baseline =
{
	{ NULL, NULL }
};

static TestSet knownGood =
{
	{ ":):)", ""
			  "" },
	{ "", "" },
	{ "End of sentence:p", "End of sentence" },
	{ "http://www.kde.org", "http://www.kde.org" },
	{ NULL, NULL }
};

static TestSet knownBroken =
{
	{ ">:-)", "" },
	{ ":))", ":))" },
	{ "In a sentence:practical example", "In a sentence:practical example" },
	{ "Bla ( )", "Bla ( )" },
	{ ":D and :-D are not the same as :d and :-d", " and  are not the same as :d and :-d" },
	// A future emoticon theme may very well have an emoticon for :d/:-d (an
	// upward tongue expression - Casey
	{ "4d:D>:)F:/>:-(:Pu:d9", "4d:D>:)F:/>:-(:Pu:d9" },
	{ "<::pvar:: test=1>", "<::pvar:: test=1>" },
	{ "a non-breaking space ( ) character", "a non-breaking space ( ) character" },
	{ "-+-[-:-(-:-)-:-]-+-", "-+-[-:-(-:-)-:-]-+-" },
	{ "::shrugs::", "::shrugs::" },
	{ ":Ptesting:P", ":Ptesting:P" },
	{ NULL, NULL }
};

void runTests( QString description, TestSet tests )
{
	// Detect the image path by copying some code from kopeteemoticons.cpp
	// Use the KMess-Cartoon theme because it has a smiley for the troublesome ':/' pattern, which
	// also exists in http:// URIs. (Default doesn't have such a smiley, making it useless for
	// the test.)
	QString path = KGlobal::dirs()->findResource( "data", "kopete/pics/emoticons/KMess-Cartoon/smile.png" ).replace( "smile.png", QString::null );

	_out << endl;
	_out << "* Running test set '" << description << "'" << endl;

	uint i = 0;
	while ( tests[ i ][ 0 ] && tests[ i ][ 1 ] )
	{
		QString result = KopeteEmoticons::parseEmoticons( tests[ i ][ 0 ] ).replace( path, QString::null );

		if ( result == tests[ i ][ 1 ] )
		{
			_out << "  - Succeeded test for '" << tests[ i ][ 0 ] << "'" << endl;
		}
		else
		{
			_out << "  - FAILED test for '" << tests[ i ][ 0 ] << "'" << endl;
			_out << "    Expected output: '" << tests[ i ][ 1 ] << "'" << endl;
			_out << "    Real output:     '" << result << "'" << endl;
		}

		i++;
	}
}

int main( int argc, char *argv[] )
{
	KAboutData aboutData( "kopeteemoticontest", "kopeteemoticontest", "version" );
	KCmdLineArgs::init( argc, argv, &aboutData );
	KApplication app( "kopeteemoticontest" );

	// Set prefs (but don't save them :)
	KopetePrefs::prefs()->setUseEmoticons( true );
	KopetePrefs::prefs()->setIconTheme( "KMess-Cartoon" );

	runTests( "Baseline of working emoticons in Kopete 0.7", kopete07Baseline );
	runTests( "Known working tests", knownGood );
	runTests( "Known broken tests", knownBroken );

	return 0;
}

// vim: set noet ts=4 sts=4 sw=4:

<|endoftext|>


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

#include "utils/linearAlg2D.h" //Distance from point to line.
#include "MergeInfillLines.h"

namespace cura
{

    MergeInfillLines::MergeInfillLines(ExtruderPlan& plan, const coord_t nozzle_size, const coord_t maximum_resolution) : extruder_plan(plan), nozzle_size(nozzle_size), maximum_resolution(maximum_resolution)
    {
        //Just copy the parameters to their fields.
    }

    coord_t MergeInfillLines::calcPathLength(const Point path_start, GCodePath& path) const
    {
        Point previous_point = path_start;
        coord_t result = 0;
        for (const Point point : path.points)
        {
            result += vSize(point - previous_point);
            previous_point = point;
        }
        return result;
    }

    /*
     * first_is_already_merged == false
     *
     *       o     o
     *      /     /
     *     /     /
     *    /  +  /      --->   -(t)-o-----o
     *   /     /
     *  /     /
     * o     o
     *
     * travel (t) to first location is done through first_path_start_changed and new_first_path_start.
     * this gets rid of the tiny "blips". Depending on the merged line distance a small gap may appear, but this is
     * accounted for in the volume.
     *
     * first_is_already_merged == true
     *
     *                   o
     *                  /
     *                 /
     *    o-----o  +  /     --->    o-----------o   or with slight   o-----o-----o
     *   /           /             /                   bend         /
     *  /           /             /                                /
     * o           o             o                                o
     *
     */
    bool MergeInfillLines::mergeLinesSideBySide(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const
    {
        Point average_first_path;

        coord_t first_path_length = calcPathLength(first_path_start, first_path);
        coord_t first_path_length_flow = first_path_length * first_path.flow; //To get the volume we don't need to include the line width since it's the same for both lines.
        const coord_t line_width = first_path.config->getLineWidth();

        if (first_is_already_merged)
        {
            // take second point of path, first_path.points[0]
            average_first_path = first_path.points[0];
        }
        else
        {
            average_first_path += first_path_start;
            for (const Point point : first_path.points)
            {
                average_first_path += point;
            }
            average_first_path /= (first_path.points.size() + 1);
        }

        coord_t second_path_length = calcPathLength(second_path_start, second_path);
        Point average_second_path = second_path_start;
        for (const Point point : second_path.points)
        {
            average_second_path += point;
        }
        coord_t second_path_length_flow = second_path_length *= second_path.flow;
        average_second_path /= (coord_t) (second_path.points.size() + 1);

        // predict new length and flow and if the new flow is to big, don't merge. conditions in this part must exactly match the actual merging
        coord_t new_path_length = first_path_length;
        coord_t dist2_from_line = 0;
        coord_t new_error_area = 0;
        coord_t merged_part_length = 0;
        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1) {
                dist2_from_line = LinearAlg2D::getDist2FromLine(average_second_path, first_path.points[first_path.points.size() - 2], first_path.points[first_path.points.size() - 1]);
                merged_part_length = vSize(first_path.points[first_path.points.size() - 2] - average_second_path);
                new_error_area = sqrt(dist2_from_line) * merged_part_length / 2;
            }
            // The max error margin uses the meshfix_maximum_resolution setting
            if (first_path.points.size() > 1 && error_area + new_error_area < merged_part_length * maximum_resolution)
            {
                new_path_length -= vSize(first_path.points[first_path.points.size() - 2] - first_path.points[first_path.points.size() - 1]);
                new_path_length += vSize(first_path.points[first_path.points.size() - 2] - average_second_path);
            }
            else
            {
                new_path_length += vSize(first_path.points[first_path.points.size() - 1] - average_second_path);
            }
        }
        else
        {
            new_path_length -= vSize(first_path.points.back() - first_path_start);
            new_path_length += vSize(average_second_path - average_first_path);
        }
        double new_flow = ((first_path_length_flow + second_path_length_flow) / static_cast<double>(new_path_length));
        if (new_flow > 2 * nozzle_size / line_width)  // line width becomes too wide.
        {
            return false;
        }

        // do the actual merging
        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1 && error_area + new_error_area < line_width * line_width)
            {
                first_path.points[first_path.points.size() - 1] = average_second_path;
                error_area += new_error_area;
            } else {
                first_path.points.push_back(average_second_path);
                error_area = 0;
            }
        }
        else
        {
            new_first_path_start = average_first_path;
            first_path.points.clear();
            first_path.points.push_back(average_second_path);
            error_area = 0;
        }

        first_path.flow = static_cast<double>(first_path_length_flow + second_path_length_flow) / new_path_length;

        return true;
    }

    bool MergeInfillLines::tryMerge(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const
    {
        const Point first_path_end = first_path.points.back();
        const Point second_path_end = second_path.points.back();
        const coord_t line_width = first_path.config->getLineWidth();

        if (vSize2(first_path_end - second_path_start) < line_width * line_width)
        {
            return false;
        }

        //Lines may be adjacent side-by-side then.
        Point first_path_leave_point;
        coord_t merged_size2;
        if (first_is_already_merged)
        {
            first_path_leave_point = first_path.points.back();  // this is the point that's going to merge
        } else {
            first_path_leave_point = (first_path_start + first_path_end) / 2;
        }
        const Point second_path_destination_point = (second_path_start + second_path_end) / 2;
        const Point merged_direction = second_path_destination_point - first_path_leave_point;
        if (first_is_already_merged)
        {
            merged_size2 = vSize2(second_path_destination_point - first_path.points.back());  // check distance with last point in merged line that is to be replaced
        }
        else
        {
            merged_size2 = vSize2(merged_direction);
        }
        if (merged_size2 > 25 * line_width * line_width)
        {
            return false; //Lines are too far away from each other.
        }
        if (merged_direction.X == 0 && merged_direction.Y == 0)
        {
            return true;  // we can just disregard the second point as it's exactly at the leave point of the first path.
        }

        // Max 1 line width to the side of the merged_direction
        if (LinearAlg2D::getDist2FromLine(first_path_end, second_path_destination_point, second_path_destination_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_start, first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_end,   first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            //|| abs(dot(normal(merged_direction, 1000), normal(second_path_end - second_path_start, 1000))) > 866000    // 866000 angle of old second_path with new merged direction should not be too small (30 degrees), as it will introduce holes
            )
        {
            return false; //One of the lines is too far from the merged line. Lines would be too wide or too far off.
        }
        if (first_is_already_merged && first_path.points.size() > 1 && first_path.points[first_path.points.size() - 2] == second_path_destination_point)  // yes this can actually happen
        {
            return false;
        }

        return mergeLinesSideBySide(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area);
    }

    bool MergeInfillLines::mergeInfillLines(std::vector<GCodePath>& paths, const Point& starting_position) const
    {
        /* Algorithm overview:
            1. Loop over all lines to see if they can be merged.
            1a. Check if two adjacent lines can be merged (skipping travel moves in
                between).
            1b. If they can, merge both lines into the first line.
            1c. If they are merged, check next that the first line can be merged
                with the line after the second line.
            2. Do a second iteration over all paths to remove the tombstones. */
        std::vector<size_t> remove_path_indices;
        std::set<size_t> is_merged;
        std::set<size_t> removed;  // keep track of what we already removed, so don't remove it again

        //For each two adjacent lines, see if they can be merged.
        size_t first_path_index = 0;
        Point first_path_start = Point(starting_position.X, starting_position.Y);  // this one is not going to be overwritten
        size_t second_path_index = 1;
        coord_t error_area = 0;

        for (; second_path_index < paths.size(); second_path_index++)
        {
            GCodePath& first_path = paths[first_path_index];
            GCodePath& second_path = paths[second_path_index];
            Point second_path_start = paths[second_path_index - 1].points.back();

            if (second_path.config->isTravelPath())
            {
                continue; //Skip travel paths, we're looking for the first non-travel path.
            }

            bool allow_try_merge = true;
            // see if we meet criteria to merge. should be: travel - path1 not travel - (...) - travel - path2 not travel - travel
            // we're checking the travels here
            if (first_path_index <= 1 || !paths[first_path_index - 1].isTravelPath())  // "<= 1" because we don't want the first travel being changed. That may introduce a hole somewhere
            {
                allow_try_merge = false;
            }
            if (second_path_index + 1 >= paths.size() || !paths[second_path_index + 1].isTravelPath())
            {
                allow_try_merge = false;
            }
            if (first_path.config->isTravelPath()) //Don't merge travel moves.
            {
                allow_try_merge = false;
            }
            if (first_path.config != second_path.config) //Only merge lines that have the same type.
            {
                allow_try_merge = false;
            }
            if (first_path.config->type != PrintFeatureType::Infill && first_path.config->type != PrintFeatureType::Skin) //Only merge skin and infill lines.
            {
                allow_try_merge = false;
            }
            const bool first_is_already_merged = is_merged.find(first_path_index) != is_merged.end();
            if ((!first_is_already_merged && first_path.points.size() > 1) || second_path.points.size() > 1)
            {
                // For now we only merge simple lines, not polylines, to keep it simple.
                // If the first line is already a merged line, then allow it.
                allow_try_merge = false;
            }

            Point new_first_path_start;
            if (allow_try_merge && tryMerge(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area))
            {
                if (!first_is_already_merged)
                {
                    paths[first_path_index - 1].points.back().X = new_first_path_start.X;
                    paths[first_path_index - 1].points.back().Y = new_first_path_start.Y;
                }
                /* If we combine two lines, the next path may also be merged into the fist line, so we do NOT update
                first_path_index. */
                for (size_t to_delete_index = first_path_index + 1; to_delete_index <= second_path_index; to_delete_index++)
                {
                    if (removed.find(to_delete_index) == removed.end())  // if there are line(s) between first and second, then those lines are already marked as to be deleted, only add the new line(s)
                    {
                        remove_path_indices.push_back(to_delete_index);
                        removed.insert(to_delete_index);
                    }
                }
                is_merged.insert(first_path_index);
            }
            else
            {
                /* If we do not combine, the next iteration we must simply merge the
                second path with the line after it. */
                first_path_index = second_path_index;
                first_path_start = second_path_start;
            }
        }

        //Delete all removed lines in one pass so that we need to move lines less often.
        if (!remove_path_indices.empty())
        {
            size_t path_index = remove_path_indices[0];
            for (size_t removed_position = 1; removed_position < remove_path_indices.size(); removed_position++)
            {
                for (; path_index < remove_path_indices[removed_position] - removed_position; path_index++)
                {
                    paths[path_index] = paths[path_index + removed_position]; //Shift all paths.
                }
            }
            for (; path_index < paths.size() - remove_path_indices.size(); path_index++) //Remaining shifts at the end.
            {
                paths[path_index] = paths[path_index + remove_path_indices.size()];
            }
            paths.erase(paths.begin() + path_index, paths.end());
            return true;
        }
        else
        {
            return false;
        }
    }
}//namespace cura
<commit_msg>Added comment.<commit_after>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "utils/linearAlg2D.h" //Distance from point to line.
#include "MergeInfillLines.h"

namespace cura
{

    MergeInfillLines::MergeInfillLines(ExtruderPlan& plan, const coord_t nozzle_size, const coord_t maximum_resolution) : extruder_plan(plan), nozzle_size(nozzle_size), maximum_resolution(maximum_resolution)
    {
        //Just copy the parameters to their fields.
    }

    coord_t MergeInfillLines::calcPathLength(const Point path_start, GCodePath& path) const
    {
        Point previous_point = path_start;
        coord_t result = 0;
        for (const Point point : path.points)
        {
            result += vSize(point - previous_point);
            previous_point = point;
        }
        return result;
    }

    /*
     * first_is_already_merged == false
     *
     *       o     o
     *      /     /
     *     /     /
     *    /  +  /      --->   -(t)-o-----o
     *   /     /
     *  /     /
     * o     o
     *
     * travel (t) to first location is done through first_path_start_changed and new_first_path_start.
     * this gets rid of the tiny "blips". Depending on the merged line distance a small gap may appear, but this is
     * accounted for in the volume.
     *
     * first_is_already_merged == true
     *
     *                   o
     *                  /
     *                 /
     *    o-----o  +  /     --->    o-----------o   or with slight   o-----o-----o
     *   /           /             /                   bend         /
     *  /           /             /                                /
     * o           o             o                                o
     *
     */
    bool MergeInfillLines::mergeLinesSideBySide(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const
    {
        Point average_first_path;

        coord_t first_path_length = calcPathLength(first_path_start, first_path);
        coord_t first_path_length_flow = first_path_length * first_path.flow; //To get the volume we don't need to include the line width since it's the same for both lines.
        const coord_t line_width = first_path.config->getLineWidth();

        if (first_is_already_merged)
        {
            // take second point of path, first_path.points[0]
            average_first_path = first_path.points[0];
        }
        else
        {
            average_first_path += first_path_start;
            for (const Point point : first_path.points)
            {
                average_first_path += point;
            }
            average_first_path /= (first_path.points.size() + 1);
        }

        coord_t second_path_length = calcPathLength(second_path_start, second_path);
        Point average_second_path = second_path_start;
        for (const Point point : second_path.points)
        {
            average_second_path += point;
        }
        coord_t second_path_length_flow = second_path_length *= second_path.flow;
        average_second_path /= (coord_t) (second_path.points.size() + 1);

        // predict new length and flow and if the new flow is to big, don't merge. conditions in this part must exactly match the actual merging
        coord_t new_path_length = first_path_length;
        coord_t dist2_from_line = 0;
        coord_t new_error_area = 0;
        coord_t merged_part_length = 0;
        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1) {
                dist2_from_line = LinearAlg2D::getDist2FromLine(average_second_path, first_path.points[first_path.points.size() - 2], first_path.points[first_path.points.size() - 1]);
                merged_part_length = vSize(first_path.points[first_path.points.size() - 2] - average_second_path);
                new_error_area = sqrt(dist2_from_line) * merged_part_length / 2;
            }
            // The max error margin uses the meshfix_maximum_resolution setting
            if (first_path.points.size() > 1 && error_area + new_error_area < merged_part_length * maximum_resolution)
            {
                new_path_length -= vSize(first_path.points[first_path.points.size() - 2] - first_path.points[first_path.points.size() - 1]);
                new_path_length += vSize(first_path.points[first_path.points.size() - 2] - average_second_path);
            }
            else
            {
                new_path_length += vSize(first_path.points[first_path.points.size() - 1] - average_second_path);
            }
        }
        else
        {
            new_path_length -= vSize(first_path.points.back() - first_path_start);
            new_path_length += vSize(average_second_path - average_first_path);
        }
        double new_flow = ((first_path_length_flow + second_path_length_flow) / static_cast<double>(new_path_length));
        if (new_flow > 2 * nozzle_size / line_width)  // line width becomes too wide.
        {
            return false;
        }

        // do the actual merging
        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1 && error_area + new_error_area < line_width * line_width)
            {
                first_path.points[first_path.points.size() - 1] = average_second_path;
                error_area += new_error_area;
            } else {
                first_path.points.push_back(average_second_path);
                error_area = 0;
            }
        }
        else
        {
            new_first_path_start = average_first_path;
            first_path.points.clear();
            first_path.points.push_back(average_second_path);
            error_area = 0;
        }

        first_path.flow = static_cast<double>(first_path_length_flow + second_path_length_flow) / new_path_length;

        return true;
    }

    bool MergeInfillLines::tryMerge(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const
    {
        const Point first_path_end = first_path.points.back();
        const Point second_path_end = second_path.points.back();
        const coord_t line_width = first_path.config->getLineWidth();

        // Reintroduction of this check prevents [CURA-5674] printing spurious infill-lines to origin:
        if (vSize2(first_path_end - second_path_start) < line_width * line_width)
        {
            return false;
        }

        //Lines may be adjacent side-by-side then.
        Point first_path_leave_point;
        coord_t merged_size2;
        if (first_is_already_merged)
        {
            first_path_leave_point = first_path.points.back();  // this is the point that's going to merge
        } else {
            first_path_leave_point = (first_path_start + first_path_end) / 2;
        }
        const Point second_path_destination_point = (second_path_start + second_path_end) / 2;
        const Point merged_direction = second_path_destination_point - first_path_leave_point;
        if (first_is_already_merged)
        {
            merged_size2 = vSize2(second_path_destination_point - first_path.points.back());  // check distance with last point in merged line that is to be replaced
        }
        else
        {
            merged_size2 = vSize2(merged_direction);
        }
        if (merged_size2 > 25 * line_width * line_width)
        {
            return false; //Lines are too far away from each other.
        }
        if (merged_direction.X == 0 && merged_direction.Y == 0)
        {
            return true;  // we can just disregard the second point as it's exactly at the leave point of the first path.
        }

        // Max 1 line width to the side of the merged_direction
        if (LinearAlg2D::getDist2FromLine(first_path_end, second_path_destination_point, second_path_destination_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_start, first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_end,   first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            //|| abs(dot(normal(merged_direction, 1000), normal(second_path_end - second_path_start, 1000))) > 866000    // 866000 angle of old second_path with new merged direction should not be too small (30 degrees), as it will introduce holes
            )
        {
            return false; //One of the lines is too far from the merged line. Lines would be too wide or too far off.
        }
        if (first_is_already_merged && first_path.points.size() > 1 && first_path.points[first_path.points.size() - 2] == second_path_destination_point)  // yes this can actually happen
        {
            return false;
        }

        return mergeLinesSideBySide(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area);
    }

    bool MergeInfillLines::mergeInfillLines(std::vector<GCodePath>& paths, const Point& starting_position) const
    {
        /* Algorithm overview:
            1. Loop over all lines to see if they can be merged.
            1a. Check if two adjacent lines can be merged (skipping travel moves in
                between).
            1b. If they can, merge both lines into the first line.
            1c. If they are merged, check next that the first line can be merged
                with the line after the second line.
            2. Do a second iteration over all paths to remove the tombstones. */
        std::vector<size_t> remove_path_indices;
        std::set<size_t> is_merged;
        std::set<size_t> removed;  // keep track of what we already removed, so don't remove it again

        //For each two adjacent lines, see if they can be merged.
        size_t first_path_index = 0;
        Point first_path_start = Point(starting_position.X, starting_position.Y);  // this one is not going to be overwritten
        size_t second_path_index = 1;
        coord_t error_area = 0;

        for (; second_path_index < paths.size(); second_path_index++)
        {
            GCodePath& first_path = paths[first_path_index];
            GCodePath& second_path = paths[second_path_index];
            Point second_path_start = paths[second_path_index - 1].points.back();

            if (second_path.config->isTravelPath())
            {
                continue; //Skip travel paths, we're looking for the first non-travel path.
            }

            bool allow_try_merge = true;
            // see if we meet criteria to merge. should be: travel - path1 not travel - (...) - travel - path2 not travel - travel
            // we're checking the travels here
            if (first_path_index <= 1 || !paths[first_path_index - 1].isTravelPath())  // "<= 1" because we don't want the first travel being changed. That may introduce a hole somewhere
            {
                allow_try_merge = false;
            }
            if (second_path_index + 1 >= paths.size() || !paths[second_path_index + 1].isTravelPath())
            {
                allow_try_merge = false;
            }
            if (first_path.config->isTravelPath()) //Don't merge travel moves.
            {
                allow_try_merge = false;
            }
            if (first_path.config != second_path.config) //Only merge lines that have the same type.
            {
                allow_try_merge = false;
            }
            if (first_path.config->type != PrintFeatureType::Infill && first_path.config->type != PrintFeatureType::Skin) //Only merge skin and infill lines.
            {
                allow_try_merge = false;
            }
            const bool first_is_already_merged = is_merged.find(first_path_index) != is_merged.end();
            if ((!first_is_already_merged && first_path.points.size() > 1) || second_path.points.size() > 1)
            {
                // For now we only merge simple lines, not polylines, to keep it simple.
                // If the first line is already a merged line, then allow it.
                allow_try_merge = false;
            }

            Point new_first_path_start;
            if (allow_try_merge && tryMerge(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area))
            {
                if (!first_is_already_merged)
                {
                    paths[first_path_index - 1].points.back().X = new_first_path_start.X;
                    paths[first_path_index - 1].points.back().Y = new_first_path_start.Y;
                }
                /* If we combine two lines, the next path may also be merged into the fist line, so we do NOT update
                first_path_index. */
                for (size_t to_delete_index = first_path_index + 1; to_delete_index <= second_path_index; to_delete_index++)
                {
                    if (removed.find(to_delete_index) == removed.end())  // if there are line(s) between first and second, then those lines are already marked as to be deleted, only add the new line(s)
                    {
                        remove_path_indices.push_back(to_delete_index);
                        removed.insert(to_delete_index);
                    }
                }
                is_merged.insert(first_path_index);
            }
            else
            {
                /* If we do not combine, the next iteration we must simply merge the
                second path with the line after it. */
                first_path_index = second_path_index;
                first_path_start = second_path_start;
            }
        }

        //Delete all removed lines in one pass so that we need to move lines less often.
        if (!remove_path_indices.empty())
        {
            size_t path_index = remove_path_indices[0];
            for (size_t removed_position = 1; removed_position < remove_path_indices.size(); removed_position++)
            {
                for (; path_index < remove_path_indices[removed_position] - removed_position; path_index++)
                {
                    paths[path_index] = paths[path_index + removed_position]; //Shift all paths.
                }
            }
            for (; path_index < paths.size() - remove_path_indices.size(); path_index++) //Remaining shifts at the end.
            {
                paths[path_index] = paths[path_index + remove_path_indices.size()];
            }
            paths.erase(paths.begin() + path_index, paths.end());
            return true;
        }
        else
        {
            return false;
        }
    }
}//namespace cura
<|endoftext|>