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

#include "chrome/tools/crash_service/crash_service.h"

#include <windows.h>

#include <iostream>
#include <fstream>
#include <map>

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "breakpad/src/client/windows/crash_generation/crash_generation_server.h"
#include "breakpad/src/client/windows/sender/crash_report_sender.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/win_util.h"

// TODO(cpu): Bug 1169078. There is a laundry list of things to do for this
// application. They will be addressed as they are required.

namespace {

const wchar_t kTestPipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";

const wchar_t kCrashReportURL[] = L"https://www.google.com/cr/report";
const wchar_t kCheckPointFile[] = L"crash_checkpoint.txt";

typedef std::map<std::wstring, std::wstring> CrashMap;

bool CustomInfoToMap(const google_breakpad::ClientInfo* client_info,
                     const std::wstring& reporter_tag, CrashMap* map) {
  google_breakpad::CustomClientInfo info = client_info->GetCustomInfo();

  for (int i = 0; i < info.count; ++i) {
    (*map)[info.entries[i].name] = info.entries[i].value;
  }

  (*map)[L"rept"] = reporter_tag;

  return (map->size() > 0);
}

bool WriteCustomInfoToFile(const std::wstring& dump_path, const CrashMap& map) {
  std::wstring file_path(dump_path);
  size_t last_dot = file_path.rfind(L'.');
  if (last_dot == std::wstring::npos)
    return false;
  file_path.resize(last_dot);
  file_path += L".txt";

  std::wofstream file(file_path.c_str(),
      std::ios_base::out | std::ios_base::app | std::ios::binary);
  if (!file.is_open())
    return false;

  CrashMap::const_iterator pos;
  for (pos = map.begin(); pos != map.end(); ++pos) {
    std::wstring line = pos->first;
    line += L':';
    line += pos->second;
    line += L'\n';
    file.write(line.c_str(), static_cast<std::streamsize>(line.length()));
  }
  return true;
}

// The window procedure task is to handle when a) the user logs off.
// b) the system shuts down or c) when the user closes the window.
LRESULT __stdcall CrashSvcWndProc(HWND hwnd, UINT message,
                                  WPARAM wparam, LPARAM lparam) {
  switch (message) {
    case WM_CLOSE:
    case WM_ENDSESSION:
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    default:
      return DefWindowProc(hwnd, message, wparam, lparam);
  }
  return 0;
}

// This is the main and only application window.
HWND g_top_window = NULL;

bool CreateTopWindow(HINSTANCE instance, bool visible) {
  WNDCLASSEXW wcx = {0};
  wcx.cbSize = sizeof(wcx);
  wcx.style = CS_HREDRAW | CS_VREDRAW;
  wcx.lpfnWndProc = CrashSvcWndProc;
  wcx.hInstance = instance;
  wcx.lpszClassName = L"crash_svc_class";
  ATOM atom = ::RegisterClassExW(&wcx);
  DWORD style = visible ? WS_POPUPWINDOW | WS_VISIBLE : WS_OVERLAPPED;

  // The window size is zero but being a popup window still shows in the
  // task bar and can be closed using the system menu or using task manager.
  HWND window = CreateWindowExW(0, wcx.lpszClassName, L"crash service", style,
                                CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
                                NULL, NULL, instance, NULL);
  if (!window)
    return false;

  ::UpdateWindow(window);
  LOG(INFO) << "window handle is " << window;
  g_top_window = window;
  return true;
}

// Simple helper class to keep the process alive until the current request
// finishes.
class ProcessingLock {
 public:
  ProcessingLock() {
    ::InterlockedIncrement(&op_count_);
  }
  ~ProcessingLock() {
    ::InterlockedDecrement(&op_count_);
  }
  static bool IsWorking() {
    return (op_count_ != 0);
  }
 private:
  static volatile LONG op_count_;
};

volatile LONG ProcessingLock::op_count_ = 0;

// This structure contains the information that the worker thread needs to
// send a crash dump to the server.
struct DumpJobInfo {
  DWORD pid;
  CrashService* self;
  CrashMap map;
  std::wstring dump_path;

  DumpJobInfo(DWORD process_id, CrashService* service,
              const CrashMap& crash_map, const std::wstring& path)
      : pid(process_id), self(service), map(crash_map), dump_path(path) {
  }
};

}  // namespace

// Command line switches:
const wchar_t CrashService::kMaxReports[] = L"max-reports";
const wchar_t CrashService::kNoWindow[] = L"no-window";
const wchar_t CrashService::kReporterTag[]= L"reporter";

CrashService::CrashService(const std::wstring& report_dir)
    : report_path_(report_dir),
      sender_(NULL),
      dumper_(NULL),
      requests_handled_(0),
      requests_sent_(0),
      clients_connected_(0),
      clients_terminated_(0) {
  chrome::RegisterPathProvider();
}

CrashService::~CrashService() {
  AutoLock lock(sending_);
  delete dumper_;
  delete sender_;
}

bool CrashService::Initialize(const std::wstring& command_line) {
  using google_breakpad::CrashReportSender;
  using google_breakpad::CrashGenerationServer;

  const wchar_t* pipe_name = kTestPipeName;
  std::wstring dumps_path;
  int max_reports = -1;

  // The checkpoint file allows CrashReportSender to enforce the the maximum
  // reports per day quota. Does not seem to serve any other purpose.
  std::wstring checkpoint_path = report_path_;
  file_util::AppendToPath(&checkpoint_path, kCheckPointFile);

  // The dumps path is typically : '<user profile>\Local settings\
  // Application data\Goggle\Chrome\Crash Reports' and the report path is
  // Application data\Google\Chrome\Reported Crashes.txt
  if (!PathService::Get(chrome::DIR_USER_DATA, &report_path_)) {
    LOG(ERROR) << "could not get DIR_USER_DATA";
    return false;
  }
  file_util::AppendToPath(&report_path_, chrome::kCrashReportLog);
  if (!PathService::Get(chrome::DIR_CRASH_DUMPS, &dumps_path)) {
    LOG(ERROR) << "could not get DIR_CRASH_DUMPS";
    return false;
  }

  CommandLine cmd_line(command_line);

  // We can override the send reports quota with a command line switch.
  if (cmd_line.HasSwitch(kMaxReports))
    max_reports = _wtoi(cmd_line.GetSwitchValue(kMaxReports).c_str());

  if (max_reports > 0) {
    // Create the http sender object.
    sender_ = new CrashReportSender(checkpoint_path);
    if (!sender_) {
      LOG(ERROR) << "could not create sender";
      return false;
    }
    sender_->set_max_reports_per_day(max_reports);
  }
  // Create the OOP crash generator object.
  dumper_ = new CrashGenerationServer(pipe_name, NULL,
                                      &CrashService::OnClientConnected, this,
                                      &CrashService::OnClientDumpRequest, this,
                                      &CrashService::OnClientExited, this,
                                      true, &dumps_path);
  if (!dumper_) {
    LOG(ERROR) << "could not create dumper";
    return false;
  }

  if (!CreateTopWindow(::GetModuleHandleW(NULL),
                       !cmd_line.HasSwitch(kNoWindow))) {
    LOG(ERROR) << "could not create window";
    return false;
  }

  reporter_tag_ = L"crash svc";
  if (cmd_line.HasSwitch(kReporterTag))
    reporter_tag_ = cmd_line.GetSwitchValue(kReporterTag);

  // Log basic information.
  LOG(INFO) << "pipe name is " << pipe_name;
  LOG(INFO) << "dumps at " << dumps_path;
  LOG(INFO) << "reports at " << report_path_;

  if (sender_) {
    LOG(INFO) << "checkpoint is " << checkpoint_path;
    LOG(INFO) << "server is " << kCrashReportURL;
    LOG(INFO) << "maximum " << sender_->max_reports_per_day() << " reports/day";
    LOG(INFO) << "reporter is " << reporter_tag_;
  }
  // Start servicing clients.
  if (!dumper_->Start()) {
    LOG(ERROR) << "could not start dumper";
    return false;
  }

  // This is throwaway code. We don't need to sync with the browser process
  // once Google Update is updated to a version supporting OOP crash handling.
  // Create or open an event to signal the browser process that the crash
  // service is initialized.
  HANDLE running_event =
      ::CreateEventW(NULL, TRUE, TRUE, L"g_chrome_crash_svc");
  // If the browser already had the event open, the CreateEvent call did not
  // signal it. We need to do it manually.
  ::SetEvent(running_event);

  return true;
}

void CrashService::OnClientConnected(void* context,
    const google_breakpad::ClientInfo* client_info) {
  ProcessingLock lock;
  LOG(INFO) << "client start. pid = " << client_info->pid();
  CrashService* self = static_cast<CrashService*>(context);
  ::InterlockedIncrement(&self->clients_connected_);
}

void CrashService::OnClientExited(void* context,
    const google_breakpad::ClientInfo* client_info) {
  ProcessingLock lock;
  LOG(INFO) << "client end. pid = " << client_info->pid();
  CrashService* self = static_cast<CrashService*>(context);
  ::InterlockedIncrement(&self->clients_terminated_);

  if (!self->sender_)
    return;

  // When we are instructed to send reports we need to exit if there are
  // no more clients to service. The next client that runs will start us.
  // Only chrome.exe starts crash_service with a non-zero max_reports.
  if (self->clients_connected_ > self->clients_terminated_)
    return;
  if (self->sender_->max_reports_per_day() > 0) {
    // Wait for the other thread to send crashes, if applicable. The sender
    // thread takes the sending_ lock, so the sleep is just to give it a
    // chance to start.
    ::Sleep(1000);
    AutoLock lock(self->sending_);
    // Some people can restart chrome very fast, check again if we have
    // a new client before exiting for real.
    if (self->clients_connected_ == self->clients_terminated_) {
      LOG(INFO) << "zero clients. exiting";
      ::PostMessage(g_top_window, WM_CLOSE, 0, 0);
    }
  }
}

void CrashService::OnClientDumpRequest(void* context,
    const google_breakpad::ClientInfo* client_info,
    const std::wstring* file_path) {
  ProcessingLock lock;

  if (!file_path) {
    LOG(ERROR) << "dump with no file path";
    return;
  }
  if (!client_info) {
    LOG(ERROR) << "dump with no client info";
    return;
  }

  DWORD pid = client_info->pid();
  LOG(INFO) << "dump for pid = " << pid << " is " << *file_path;

  CrashService* self = static_cast<CrashService*>(context);
  if (!self) {
    LOG(ERROR) << "dump with no context";
    return;
  }

  CrashMap map;
  CustomInfoToMap(client_info, self->reporter_tag_, &map);

  if (!WriteCustomInfoToFile(*file_path, map)) {
    LOG(ERROR) << "could not write custom info file";
  }

  if (!self->sender_)
    return;

  // Send the crash dump using a worker thread. This operation has retry
  // logic in case there is no internet connection at the time.
  DumpJobInfo* dump_job = new DumpJobInfo(pid, self, map, *file_path);
  if (!::QueueUserWorkItem(&CrashService::AsyncSendDump,
                           dump_job, WT_EXECUTELONGFUNCTION)) {
    LOG(ERROR) << "could not queue job";
  }
}

// We are going to try sending the report several times. If we can't send,
// we sleep from one minute to several hours depending on the retry round.
unsigned long CrashService::AsyncSendDump(void* context) {
  if (!context)
    return 0;

  DumpJobInfo* info = static_cast<DumpJobInfo*>(context);

  std::wstring report_id = L"<unsent>";

  const DWORD kOneMinute = 60*1000;
  const DWORD kOneHour = 60*kOneMinute;

  const DWORD kSleepSchedule[] = {
      24*kOneHour,
      8*kOneHour,
      4*kOneHour,
      kOneHour,
      15*kOneMinute,
      0};

  int retry_round = arraysize(kSleepSchedule) - 1;

  do {
    ::Sleep(kSleepSchedule[retry_round]);
    {
      // Take the server lock while sending. This also prevent early
      // termination of the service object.
      AutoLock lock(info->self->sending_);
      LOG(INFO) << "trying to send report for pid = " << info->pid;
      google_breakpad::ReportResult send_result
          = info->self->sender_->SendCrashReport(kCrashReportURL, info->map,
                                                 info->dump_path, &report_id);
      switch (send_result) {
        case google_breakpad::RESULT_FAILED:
          report_id = L"<network issue>";
          break;
        case google_breakpad::RESULT_REJECTED:
          report_id = L"<rejected>";
          ++info->self->requests_handled_;
          retry_round = 0;
          break;
        case google_breakpad::RESULT_SUCCEEDED:
          ++info->self->requests_sent_;
          ++info->self->requests_handled_;
          retry_round = 0;
          break;
        case google_breakpad::RESULT_THROTTLED:
          report_id = L"<throttled>";
          break;
        default:
          report_id = L"<unknown>";
          break;
      };
    }

    LOG(INFO) << "dump for pid =" << info->pid << " crash2 id =" << report_id;
    --retry_round;
  } while(retry_round >= 0);

  if (!::DeleteFileW(info->dump_path.c_str()))
    LOG(WARNING) << "could not delete " << info->dump_path;

  delete info;
  return 0;
}

int CrashService::ProcessingLoop() {
  MSG msg;
  while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  LOG(INFO) << "session ending..";
  while (ProcessingLock::IsWorking()) {
    ::Sleep(50);
  }

  LOG(INFO) << "clients connected :" << clients_connected_;
  LOG(INFO) << "clients terminated :" << clients_terminated_;
  LOG(INFO) << "dumps serviced :" << requests_handled_;
  LOG(INFO) << "dumps reported :" << requests_sent_;

  return static_cast<int>(msg.wParam);
}

<commit_msg>Switch to the new crash service URL.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/tools/crash_service/crash_service.h"

#include <windows.h>

#include <iostream>
#include <fstream>
#include <map>

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "breakpad/src/client/windows/crash_generation/crash_generation_server.h"
#include "breakpad/src/client/windows/sender/crash_report_sender.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/win_util.h"

// TODO(cpu): Bug 1169078. There is a laundry list of things to do for this
// application. They will be addressed as they are required.

namespace {

const wchar_t kTestPipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";

const wchar_t kCrashReportURL[] = L"https://clients2.google.com/cr/report";
const wchar_t kCheckPointFile[] = L"crash_checkpoint.txt";

typedef std::map<std::wstring, std::wstring> CrashMap;

bool CustomInfoToMap(const google_breakpad::ClientInfo* client_info,
                     const std::wstring& reporter_tag, CrashMap* map) {
  google_breakpad::CustomClientInfo info = client_info->GetCustomInfo();

  for (int i = 0; i < info.count; ++i) {
    (*map)[info.entries[i].name] = info.entries[i].value;
  }

  (*map)[L"rept"] = reporter_tag;

  return (map->size() > 0);
}

bool WriteCustomInfoToFile(const std::wstring& dump_path, const CrashMap& map) {
  std::wstring file_path(dump_path);
  size_t last_dot = file_path.rfind(L'.');
  if (last_dot == std::wstring::npos)
    return false;
  file_path.resize(last_dot);
  file_path += L".txt";

  std::wofstream file(file_path.c_str(),
      std::ios_base::out | std::ios_base::app | std::ios::binary);
  if (!file.is_open())
    return false;

  CrashMap::const_iterator pos;
  for (pos = map.begin(); pos != map.end(); ++pos) {
    std::wstring line = pos->first;
    line += L':';
    line += pos->second;
    line += L'\n';
    file.write(line.c_str(), static_cast<std::streamsize>(line.length()));
  }
  return true;
}

// The window procedure task is to handle when a) the user logs off.
// b) the system shuts down or c) when the user closes the window.
LRESULT __stdcall CrashSvcWndProc(HWND hwnd, UINT message,
                                  WPARAM wparam, LPARAM lparam) {
  switch (message) {
    case WM_CLOSE:
    case WM_ENDSESSION:
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    default:
      return DefWindowProc(hwnd, message, wparam, lparam);
  }
  return 0;
}

// This is the main and only application window.
HWND g_top_window = NULL;

bool CreateTopWindow(HINSTANCE instance, bool visible) {
  WNDCLASSEXW wcx = {0};
  wcx.cbSize = sizeof(wcx);
  wcx.style = CS_HREDRAW | CS_VREDRAW;
  wcx.lpfnWndProc = CrashSvcWndProc;
  wcx.hInstance = instance;
  wcx.lpszClassName = L"crash_svc_class";
  ATOM atom = ::RegisterClassExW(&wcx);
  DWORD style = visible ? WS_POPUPWINDOW | WS_VISIBLE : WS_OVERLAPPED;

  // The window size is zero but being a popup window still shows in the
  // task bar and can be closed using the system menu or using task manager.
  HWND window = CreateWindowExW(0, wcx.lpszClassName, L"crash service", style,
                                CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
                                NULL, NULL, instance, NULL);
  if (!window)
    return false;

  ::UpdateWindow(window);
  LOG(INFO) << "window handle is " << window;
  g_top_window = window;
  return true;
}

// Simple helper class to keep the process alive until the current request
// finishes.
class ProcessingLock {
 public:
  ProcessingLock() {
    ::InterlockedIncrement(&op_count_);
  }
  ~ProcessingLock() {
    ::InterlockedDecrement(&op_count_);
  }
  static bool IsWorking() {
    return (op_count_ != 0);
  }
 private:
  static volatile LONG op_count_;
};

volatile LONG ProcessingLock::op_count_ = 0;

// This structure contains the information that the worker thread needs to
// send a crash dump to the server.
struct DumpJobInfo {
  DWORD pid;
  CrashService* self;
  CrashMap map;
  std::wstring dump_path;

  DumpJobInfo(DWORD process_id, CrashService* service,
              const CrashMap& crash_map, const std::wstring& path)
      : pid(process_id), self(service), map(crash_map), dump_path(path) {
  }
};

}  // namespace

// Command line switches:
const wchar_t CrashService::kMaxReports[] = L"max-reports";
const wchar_t CrashService::kNoWindow[] = L"no-window";
const wchar_t CrashService::kReporterTag[]= L"reporter";

CrashService::CrashService(const std::wstring& report_dir)
    : report_path_(report_dir),
      sender_(NULL),
      dumper_(NULL),
      requests_handled_(0),
      requests_sent_(0),
      clients_connected_(0),
      clients_terminated_(0) {
  chrome::RegisterPathProvider();
}

CrashService::~CrashService() {
  AutoLock lock(sending_);
  delete dumper_;
  delete sender_;
}

bool CrashService::Initialize(const std::wstring& command_line) {
  using google_breakpad::CrashReportSender;
  using google_breakpad::CrashGenerationServer;

  const wchar_t* pipe_name = kTestPipeName;
  std::wstring dumps_path;
  int max_reports = -1;

  // The checkpoint file allows CrashReportSender to enforce the the maximum
  // reports per day quota. Does not seem to serve any other purpose.
  std::wstring checkpoint_path = report_path_;
  file_util::AppendToPath(&checkpoint_path, kCheckPointFile);

  // The dumps path is typically : '<user profile>\Local settings\
  // Application data\Goggle\Chrome\Crash Reports' and the report path is
  // Application data\Google\Chrome\Reported Crashes.txt
  if (!PathService::Get(chrome::DIR_USER_DATA, &report_path_)) {
    LOG(ERROR) << "could not get DIR_USER_DATA";
    return false;
  }
  file_util::AppendToPath(&report_path_, chrome::kCrashReportLog);
  if (!PathService::Get(chrome::DIR_CRASH_DUMPS, &dumps_path)) {
    LOG(ERROR) << "could not get DIR_CRASH_DUMPS";
    return false;
  }

  CommandLine cmd_line(command_line);

  // We can override the send reports quota with a command line switch.
  if (cmd_line.HasSwitch(kMaxReports))
    max_reports = _wtoi(cmd_line.GetSwitchValue(kMaxReports).c_str());

  if (max_reports > 0) {
    // Create the http sender object.
    sender_ = new CrashReportSender(checkpoint_path);
    if (!sender_) {
      LOG(ERROR) << "could not create sender";
      return false;
    }
    sender_->set_max_reports_per_day(max_reports);
  }
  // Create the OOP crash generator object.
  dumper_ = new CrashGenerationServer(pipe_name, NULL,
                                      &CrashService::OnClientConnected, this,
                                      &CrashService::OnClientDumpRequest, this,
                                      &CrashService::OnClientExited, this,
                                      true, &dumps_path);
  if (!dumper_) {
    LOG(ERROR) << "could not create dumper";
    return false;
  }

  if (!CreateTopWindow(::GetModuleHandleW(NULL),
                       !cmd_line.HasSwitch(kNoWindow))) {
    LOG(ERROR) << "could not create window";
    return false;
  }

  reporter_tag_ = L"crash svc";
  if (cmd_line.HasSwitch(kReporterTag))
    reporter_tag_ = cmd_line.GetSwitchValue(kReporterTag);

  // Log basic information.
  LOG(INFO) << "pipe name is " << pipe_name;
  LOG(INFO) << "dumps at " << dumps_path;
  LOG(INFO) << "reports at " << report_path_;

  if (sender_) {
    LOG(INFO) << "checkpoint is " << checkpoint_path;
    LOG(INFO) << "server is " << kCrashReportURL;
    LOG(INFO) << "maximum " << sender_->max_reports_per_day() << " reports/day";
    LOG(INFO) << "reporter is " << reporter_tag_;
  }
  // Start servicing clients.
  if (!dumper_->Start()) {
    LOG(ERROR) << "could not start dumper";
    return false;
  }

  // This is throwaway code. We don't need to sync with the browser process
  // once Google Update is updated to a version supporting OOP crash handling.
  // Create or open an event to signal the browser process that the crash
  // service is initialized.
  HANDLE running_event =
      ::CreateEventW(NULL, TRUE, TRUE, L"g_chrome_crash_svc");
  // If the browser already had the event open, the CreateEvent call did not
  // signal it. We need to do it manually.
  ::SetEvent(running_event);

  return true;
}

void CrashService::OnClientConnected(void* context,
    const google_breakpad::ClientInfo* client_info) {
  ProcessingLock lock;
  LOG(INFO) << "client start. pid = " << client_info->pid();
  CrashService* self = static_cast<CrashService*>(context);
  ::InterlockedIncrement(&self->clients_connected_);
}

void CrashService::OnClientExited(void* context,
    const google_breakpad::ClientInfo* client_info) {
  ProcessingLock lock;
  LOG(INFO) << "client end. pid = " << client_info->pid();
  CrashService* self = static_cast<CrashService*>(context);
  ::InterlockedIncrement(&self->clients_terminated_);

  if (!self->sender_)
    return;

  // When we are instructed to send reports we need to exit if there are
  // no more clients to service. The next client that runs will start us.
  // Only chrome.exe starts crash_service with a non-zero max_reports.
  if (self->clients_connected_ > self->clients_terminated_)
    return;
  if (self->sender_->max_reports_per_day() > 0) {
    // Wait for the other thread to send crashes, if applicable. The sender
    // thread takes the sending_ lock, so the sleep is just to give it a
    // chance to start.
    ::Sleep(1000);
    AutoLock lock(self->sending_);
    // Some people can restart chrome very fast, check again if we have
    // a new client before exiting for real.
    if (self->clients_connected_ == self->clients_terminated_) {
      LOG(INFO) << "zero clients. exiting";
      ::PostMessage(g_top_window, WM_CLOSE, 0, 0);
    }
  }
}

void CrashService::OnClientDumpRequest(void* context,
    const google_breakpad::ClientInfo* client_info,
    const std::wstring* file_path) {
  ProcessingLock lock;

  if (!file_path) {
    LOG(ERROR) << "dump with no file path";
    return;
  }
  if (!client_info) {
    LOG(ERROR) << "dump with no client info";
    return;
  }

  DWORD pid = client_info->pid();
  LOG(INFO) << "dump for pid = " << pid << " is " << *file_path;

  CrashService* self = static_cast<CrashService*>(context);
  if (!self) {
    LOG(ERROR) << "dump with no context";
    return;
  }

  CrashMap map;
  CustomInfoToMap(client_info, self->reporter_tag_, &map);

  if (!WriteCustomInfoToFile(*file_path, map)) {
    LOG(ERROR) << "could not write custom info file";
  }

  if (!self->sender_)
    return;

  // Send the crash dump using a worker thread. This operation has retry
  // logic in case there is no internet connection at the time.
  DumpJobInfo* dump_job = new DumpJobInfo(pid, self, map, *file_path);
  if (!::QueueUserWorkItem(&CrashService::AsyncSendDump,
                           dump_job, WT_EXECUTELONGFUNCTION)) {
    LOG(ERROR) << "could not queue job";
  }
}

// We are going to try sending the report several times. If we can't send,
// we sleep from one minute to several hours depending on the retry round.
unsigned long CrashService::AsyncSendDump(void* context) {
  if (!context)
    return 0;

  DumpJobInfo* info = static_cast<DumpJobInfo*>(context);

  std::wstring report_id = L"<unsent>";

  const DWORD kOneMinute = 60*1000;
  const DWORD kOneHour = 60*kOneMinute;

  const DWORD kSleepSchedule[] = {
      24*kOneHour,
      8*kOneHour,
      4*kOneHour,
      kOneHour,
      15*kOneMinute,
      0};

  int retry_round = arraysize(kSleepSchedule) - 1;

  do {
    ::Sleep(kSleepSchedule[retry_round]);
    {
      // Take the server lock while sending. This also prevent early
      // termination of the service object.
      AutoLock lock(info->self->sending_);
      LOG(INFO) << "trying to send report for pid = " << info->pid;
      google_breakpad::ReportResult send_result
          = info->self->sender_->SendCrashReport(kCrashReportURL, info->map,
                                                 info->dump_path, &report_id);
      switch (send_result) {
        case google_breakpad::RESULT_FAILED:
          report_id = L"<network issue>";
          break;
        case google_breakpad::RESULT_REJECTED:
          report_id = L"<rejected>";
          ++info->self->requests_handled_;
          retry_round = 0;
          break;
        case google_breakpad::RESULT_SUCCEEDED:
          ++info->self->requests_sent_;
          ++info->self->requests_handled_;
          retry_round = 0;
          break;
        case google_breakpad::RESULT_THROTTLED:
          report_id = L"<throttled>";
          break;
        default:
          report_id = L"<unknown>";
          break;
      };
    }

    LOG(INFO) << "dump for pid =" << info->pid << " crash2 id =" << report_id;
    --retry_round;
  } while(retry_round >= 0);

  if (!::DeleteFileW(info->dump_path.c_str()))
    LOG(WARNING) << "could not delete " << info->dump_path;

  delete info;
  return 0;
}

int CrashService::ProcessingLoop() {
  MSG msg;
  while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  LOG(INFO) << "session ending..";
  while (ProcessingLock::IsWorking()) {
    ::Sleep(50);
  }

  LOG(INFO) << "clients connected :" << clients_connected_;
  LOG(INFO) << "clients terminated :" << clients_terminated_;
  LOG(INFO) << "dumps serviced :" << requests_handled_;
  LOG(INFO) << "dumps reported :" << requests_sent_;

  return static_cast<int>(msg.wParam);
}

<|endoftext|>


<commit_before>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMdiSubWindow>
#include <QTextEdit>
#include "adapters/joystickadapter.h"

#include <iostream>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    configTreeModel.populateModel();
    ui->configTree->setModel(configTreeModel.model());
    mdiArea = ui->mainDisplayArea;
    connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)),
            this, SLOT(updateMenus()));
    windowMapper = new QSignalMapper(this);
    connect(windowMapper, SIGNAL(mapped(QWidget*)),
            this, SLOT(setActiveSubWindow(QWidget*)));

    ui->hardwareStatusList->addItem("Joystick");
    connect(ui->hardwareStatusList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(openHardwareView(QModelIndex)));

    setupMenus();
    updateWindowMenu();

    _joystick = new Joystick;
}

void MainWindow::setupMenus()
{
    connect(ui->actionClose, SIGNAL(triggered()), this, SLOT(close()));
    connect(ui->menuWindow, SIGNAL(aboutToShow()), this, SLOT(updateWindowMenu()));
    connect(ui->actionClose_2, SIGNAL(triggered()), mdiArea, SLOT(closeActiveSubWindow()));
    connect(ui->actionClose_All, SIGNAL(triggered()), mdiArea, SLOT(closeAllSubWindows()));
    connect(ui->actionCascade, SIGNAL(triggered()), mdiArea, SLOT(cascadeSubWindows()));
    connect(ui->actionTile, SIGNAL(triggered()), mdiArea, SLOT(tileSubWindows()));
}

MainWindow::~MainWindow()
{
    delete _joystick;
    delete ui;
}

void MainWindow::openHardwareView(QModelIndex index)
{
    if(index.row()==0)
    {
        if(MDIWindow* window = findWindowWithTitle("Joystick"))
        {
            if(!window->isVisible())
                window->show();
        }
        else
        {
            using namespace std;
            MDIWindow *newWindow = new MDIWindow;
            newWindow->setWindowTitle("Joystick");
            JoystickAdapter *adapter = new JoystickAdapter(_joystick);
            newWindow->setLayout(new QGridLayout);
            newWindow->layout()->addWidget(adapter);
            mdiArea->addSubWindow(newWindow);
            newWindow->show();
        }
    }
    updateWindowMenu();
}

void MainWindow::on_actionFullscreen_triggered()
{
    if(ui->actionFullscreen->isChecked())
        this->showFullScreen();
    else
        this->showNormal();
}

void MainWindow::updateMenus()
{
    bool hasMdiChild = (activeMdiChild() != 0);

    ui->actionTile->setEnabled(hasMdiChild);
    ui->actionCascade->setEnabled(hasMdiChild);
    ui->actionClose_2->setEnabled(hasMdiChild);
    ui->actionClose_All->setEnabled(hasMdiChild);
}

void MainWindow::updateWindowMenu()
{
    for(int i = ui->menuWindow->actions().size()-1; i > 5; i--)
    {
        ui->menuWindow->removeAction(ui->menuWindow->actions().at(i));
    }

    QList<QMdiSubWindow *> windows = mdiArea->subWindowList();

    for (int i = 0; i < windows.size(); ++i) {
        MDIWindow *child = qobject_cast<MDIWindow *>(windows.at(i)->widget());

        QString text = windows.at(i)->windowTitle();
        QAction *action  = ui->menuWindow->addAction(text);
        action->setCheckable(true);
        action ->setChecked(child == activeMdiChild());
        connect(action, SIGNAL(triggered()), windowMapper, SLOT(map()));
        windowMapper->setMapping(action, windows.at(i));
    }
}

MDIWindow* MainWindow::activeMdiChild()
{
    if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow())
        return qobject_cast<MDIWindow *>(activeSubWindow->widget());
    return 0;
}

void MainWindow::setActiveSubWindow(QWidget *window)
{
    if (!window)
        return;
    mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow *>(window));
}

MDIWindow* MainWindow::findWindowWithTitle(QString title)
{
    foreach(QMdiSubWindow *window, mdiArea->subWindowList())
    {
        MDIWindow *mdiChild = qobject_cast<MDIWindow*>(window->widget());
        if(mdiChild && mdiChild->windowTitle() == title)
            return mdiChild;
    }
    return 0;
}
<commit_msg>Prepares openHardwareView(...) to be expanded to handle other hardware besides the joystick.<commit_after>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMdiSubWindow>
#include <QTextEdit>
#include "adapters/joystickadapter.h"

#include <iostream>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    configTreeModel.populateModel();
    ui->configTree->setModel(configTreeModel.model());
    mdiArea = ui->mainDisplayArea;
    connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)),
            this, SLOT(updateMenus()));
    windowMapper = new QSignalMapper(this);
    connect(windowMapper, SIGNAL(mapped(QWidget*)),
            this, SLOT(setActiveSubWindow(QWidget*)));

    ui->hardwareStatusList->addItem("Joystick");
    connect(ui->hardwareStatusList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(openHardwareView(QModelIndex)));

    setupMenus();
    updateWindowMenu();

    _joystick = new Joystick;
}

void MainWindow::setupMenus()
{
    connect(ui->actionClose, SIGNAL(triggered()), this, SLOT(close()));
    connect(ui->menuWindow, SIGNAL(aboutToShow()), this, SLOT(updateWindowMenu()));
    connect(ui->actionClose_2, SIGNAL(triggered()), mdiArea, SLOT(closeActiveSubWindow()));
    connect(ui->actionClose_All, SIGNAL(triggered()), mdiArea, SLOT(closeAllSubWindows()));
    connect(ui->actionCascade, SIGNAL(triggered()), mdiArea, SLOT(cascadeSubWindows()));
    connect(ui->actionTile, SIGNAL(triggered()), mdiArea, SLOT(tileSubWindows()));
}

MainWindow::~MainWindow()
{
    delete _joystick;
    delete ui;
}

void MainWindow::openHardwareView(QModelIndex index)
{
    QString labelText = ui->hardwareStatusList->item(index.row())->text();
    if(MDIWindow* window = findWindowWithTitle(labelText))
    {
        if(!window->isVisible())
            window->show();
    }
    else
    {
        using namespace std;
        MDIWindow *newWindow = new MDIWindow;
        newWindow->setWindowTitle(labelText);
        newWindow->setLayout(new QGridLayout);

        //JoystickAdapter *adapter = new JoystickAdapter(_joystick);
        QWidget* adapter;

        if(labelText == "Joystick")
        {
            adapter = new JoystickAdapter(_joystick);
        }
        else
        {
            adapter = new QWidget();
        }

        newWindow->layout()->addWidget(adapter);

        mdiArea->addSubWindow(newWindow);
        newWindow->show();
    }

    updateWindowMenu();
}

void MainWindow::on_actionFullscreen_triggered()
{
    if(ui->actionFullscreen->isChecked())
        this->showFullScreen();
    else
        this->showNormal();
}

void MainWindow::updateMenus()
{
    bool hasMdiChild = (activeMdiChild() != 0);

    ui->actionTile->setEnabled(hasMdiChild);
    ui->actionCascade->setEnabled(hasMdiChild);
    ui->actionClose_2->setEnabled(hasMdiChild);
    ui->actionClose_All->setEnabled(hasMdiChild);
}

void MainWindow::updateWindowMenu()
{
    for(int i = ui->menuWindow->actions().size()-1; i > 5; i--)
    {
        ui->menuWindow->removeAction(ui->menuWindow->actions().at(i));
    }

    QList<QMdiSubWindow *> windows = mdiArea->subWindowList();

    for (int i = 0; i < windows.size(); ++i) {
        MDIWindow *child = qobject_cast<MDIWindow *>(windows.at(i)->widget());

        QString text = windows.at(i)->windowTitle();
        QAction *action  = ui->menuWindow->addAction(text);
        action->setCheckable(true);
        action ->setChecked(child == activeMdiChild());
        connect(action, SIGNAL(triggered()), windowMapper, SLOT(map()));
        windowMapper->setMapping(action, windows.at(i));
    }
}

MDIWindow* MainWindow::activeMdiChild()
{
    if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow())
        return qobject_cast<MDIWindow *>(activeSubWindow->widget());
    return 0;
}

void MainWindow::setActiveSubWindow(QWidget *window)
{
    if (!window)
        return;
    mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow *>(window));
}

MDIWindow* MainWindow::findWindowWithTitle(QString title)
{
    foreach(QMdiSubWindow *window, mdiArea->subWindowList())
    {
        MDIWindow *mdiChild = qobject_cast<MDIWindow*>(window->widget());
        if(mdiChild && mdiChild->windowTitle() == title)
            return mdiChild;
    }
    return 0;
}
<|endoftext|>


<commit_before><commit_msg>microoptimization<commit_after><|endoftext|>


<commit_before>#include "Nearest.h"

using namespace std;

namespace nearest {

    vector<Point> nearestN(const vector<Point>& points, int N) {
        if (points.empty() || N == 0) {
            return vector<Point>();
        }
        return nearestN(points, N, {0.0, 0.0, 0.0}, INFINITY);
    }

    vector<Point> nearestN(const vector<Point>& points,
                           int N,
                           const Point& reference,
                           double distanceThreshold) {

        assert(distanceThreshold >= 0 && N >= 0);
        vector<Point> temp;
        temp.insert(temp.begin(), points.begin(), points.end());

        // filtering vector to remove all points far then distance from reference
        temp.erase(remove_if(temp.begin(),
                             temp.end(),
                             [&reference, distanceThreshold](const Point& p){
                                return reference.dist(p) > distanceThreshold;
                             }),
                    temp.end());

        sort(temp.begin(), temp.end(),
            [&reference](const Point& p1, const Point& p2) {
                return reference.dist_square(p1) < reference.dist_square(p2);
            });

        auto sz = min(static_cast<int>(temp.size()), N);
        temp.resize(sz);
        return temp;
    }
}
<commit_msg>small optimizations<commit_after>#include "Nearest.h"

using namespace std;

namespace nearest {

    vector<Point> nearestN(const vector<Point>& points, int N) {
        if (points.empty() || N == 0) {
            return vector<Point>();
        }
        return nearestN(points, N, {0.0, 0.0, 0.0}, INFINITY);
    }

    vector<Point> nearestN(const vector<Point>& points,
                           int N,
                           const Point& reference,
                           double distanceThreshold) {

        assert(distanceThreshold >= 0 && N >= 0);
        if (points.empty() || N == 0) {
            return vector<Point>();
        }
        vector<Point> temp;
        temp.insert(temp.begin(), points.begin(), points.end());

        // filtering vector to remove all points far then distance from reference
        temp.erase(remove_if(temp.begin(),
                             temp.end(),
                             [&reference, distanceThreshold](const Point& p){
                                return reference.dist(p) > distanceThreshold;
                             }),
                    temp.end());

        sort(temp.begin(), temp.end(),
            [&reference](const Point& p1, const Point& p2) {
                return reference.dist_square(p1) < reference.dist_square(p2);
            });

        auto sz = min(static_cast<int>(temp.size()), N);
        temp.resize(sz);
        return temp;
    }
}
<|endoftext|>


<commit_before>/*
 * Renderer.cpp
 *
 * Renders the box2d content
 */

#ifndef PINBALL_BOT_RENDERER
#define PINBALL_BOT_RENDERER

#include <Box2D/Box2D.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_main.h>

#include <SDL2/SDL2_gfxPrimitives.h>

#include <iostream>
#include <vector>

#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#ifdef _WIN32
  #include <windows.h>
#endif
#include <GL/gl.h>
#include <GL/glu.h>
#endif

class Renderer : public b2Draw{

	private:

		const float NUMERATOR			= 7.0f;
		const float DENOMINATOR			= 9.0f;

		const float SCALING				= NUMERATOR / DENOMINATOR;
		const float PADDING_PERCENT		= (DENOMINATOR - NUMERATOR) / DENOMINATOR / 2;

		int		width;
		int		height;

		int		oneMeterInPX;

		SDL_Window*		window;
		SDL_Renderer*	renderer;

		/**
		 * Converts Box2D meters into screen pixels
		 * @param	meters		float		The amount of meters to convert
		 * @return				int			The rounded amount of corresponding screen pixels
		 */
		int metersToPixels(const float &meters){
			return round(meters * oneMeterInPX);
		}

		/**
		 * Converts a Box2D vector into screen coordinates using metersToPixels()
		 * @param	position	b2Vec2		The Box2D vector to convert
		 * @return				b2vec2		The converted Box2D vector
		 */
		b2Vec2 toScreenCoords(const b2Vec2 &position){
			return b2Vec2 (
					round(width * PADDING_PERCENT) + metersToPixels(position.x),
					round(height * PADDING_PERCENT) + metersToPixels(position.y)
			);
		}

	public:

		/**
		 * Inits all required values based on the given window width/height and the DPI
		 */
		Renderer(int width, int height) : width(width), height(height){

			SDL_Init( /*SDL_INIT_EVERYTHING*/SDL_INIT_VIDEO );

			window = SDL_CreateWindow(
				"PinballBot",						// window title
				0,									// initial x position
				0,									// initial y position
				this->width,						// width, in pixels
				this->height,						// height, in pixels
				SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI //enables retina support
			);

			if (window == nullptr) {
				printf("Could not create window: %s\n", SDL_GetError());
				SDL_Quit();
				return;
			}

			//updates the width and height if there's a high DPI and calc other vars afterwards
			SDL_GL_GetDrawableSize(window, &this->width, &this->height);

			oneMeterInPX = round(SCALING * this->height); /* one meter is equal to half of the width of the window */

			renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
			SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_ADD);

			SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); //white background
			SDL_RenderClear(renderer);
		}

		/**
		 * Uses SDL functions to remove objects
		 */
		~Renderer(){
			SDL_DestroyWindow(window);
			SDL_Quit();
		}

		/**
		 * Redraws the scene onto the window
		 * @return	void
		 */
		void redraw(){

			//filledCircleColor(renderer, 320, 91, 10, createRGBA(255,0,0,255));

			SDL_RenderPresent(renderer);

			SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
			SDL_RenderClear(renderer);
		}

		/**
		 * Draws a polygon
		 * @param	vertices		b2Vec2		The vertices of the polygon
		 * @param	vertexCount		int32		The amount of vertices
		 * @param	red				Uint8		The amount of red	in the color
		 * @param	green			Uint8		The amount of green	in the color
		 * @param	blue			Uint8		The amount of blue	in the color
		 * @param	alpha			Uint8		The opacity of the color
		 * @param	filled			bool		Draw a filled polygon or just the outlines?
		 * @return	void
		 */
		void dPolygon(const b2Vec2* vertices, int32 vertexCount, Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha, bool filled){
			std::vector<short> x(vertexCount);
			std::vector<short> y(vertexCount);

			for(int i=0;i<vertexCount;i++){
				b2Vec2 vec = toScreenCoords(vertices[i]);

				x[i] = (short) vec.x;
				y[i] = (short) vec.y;
			}

			if(filled){
				filledPolygonRGBA(renderer, x.data(), y.data(), vertexCount, red, green, blue, alpha);
			}else{
				polygonRGBA(renderer, x.data(), y.data(), vertexCount, red, green, blue, alpha);
			}
		}

		/**
		 * Draws a circle
		 * @param	center			b2Vec2		The coordinates of the center of the circle
		 * @param	radius			float32		The radius of the circle
		 * @param	red				Uint8		The amount of red	in the color
		 * @param	green			Uint8		The amount of green	in the color
		 * @param	blue			Uint8		The amount of blue	in the color
		 * @param	alpha			Uint8		The opacity of the color
		 * @param	filled			bool		Draw a filled polygon or just the outlines?
		 * @return	void
		 */
		void dCircle(const b2Vec2& center, float32 radius, Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha, bool filled){
			b2Vec2 coords = toScreenCoords(center);

			if(filled){
				filledCircleRGBA(renderer, (Sint16) coords.x, (Sint16) coords.y, (Sint16) metersToPixels(radius), red, green, blue, alpha);
			}else{
				circleRGBA(renderer, (Sint16) coords.x, (Sint16) coords.y, (Sint16) metersToPixels(radius), red, green, blue, alpha);
			}
		}

		/**
		 * Implements the b2Draw functions
		 */
			void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {
				dPolygon(vertices, vertexCount, 0, 0, 0, 255, false);
			}
			void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {
				dPolygon(vertices, vertexCount, 0, 0, 0, 255, true);
			}

			void DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color) {
				dCircle(center, radius, 0, 0, 0, 255, false);
			}
			void DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color) {
				dCircle(center, radius, 0, 0, 0, 255, true);
			}

			void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) {
				lineRGBA(renderer, (Sint16) p1.x, (Sint16) p1.y, (Sint16) p2.x, (Sint16) p2.y, 0, 0, 0, 255);
			}
			void DrawTransform(const b2Transform& xf) {

			}

};

#endif /* PINBALL_BOT_RENDERER */
<commit_msg>SDL window centered<commit_after>/*
 * Renderer.cpp
 *
 * Renders the box2d content
 */

#ifndef PINBALL_BOT_RENDERER
#define PINBALL_BOT_RENDERER

#include <Box2D/Box2D.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_main.h>

#include <SDL2/SDL2_gfxPrimitives.h>

#include <iostream>
#include <vector>

#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#ifdef _WIN32
  #include <windows.h>
#endif
#include <GL/gl.h>
#include <GL/glu.h>
#endif

class Renderer : public b2Draw{

	private:

		const float NUMERATOR			= 7.0f;
		const float DENOMINATOR			= 9.0f;

		const float SCALING				= NUMERATOR / DENOMINATOR;
		const float PADDING_PERCENT		= (DENOMINATOR - NUMERATOR) / DENOMINATOR / 2;

		int		width;
		int		height;

		int		oneMeterInPX;

		SDL_Window*		window;
		SDL_Renderer*	renderer;

		/**
		 * Converts Box2D meters into screen pixels
		 * @param	meters		float		The amount of meters to convert
		 * @return				int			The rounded amount of corresponding screen pixels
		 */
		int metersToPixels(const float &meters){
			return round(meters * oneMeterInPX);
		}

		/**
		 * Converts a Box2D vector into screen coordinates using metersToPixels()
		 * @param	position	b2Vec2		The Box2D vector to convert
		 * @return				b2vec2		The converted Box2D vector
		 */
		b2Vec2 toScreenCoords(const b2Vec2 &position){
			return b2Vec2 (
					round(width * PADDING_PERCENT) + metersToPixels(position.x),
					round(height * PADDING_PERCENT) + metersToPixels(position.y)
			);
		}

	public:

		/**
		 * Inits all required values based on the given window width/height and the DPI
		 */
		Renderer(int width, int height) : width(width), height(height){

			SDL_Init( /*SDL_INIT_EVERYTHING*/SDL_INIT_VIDEO );

			window = SDL_CreateWindow(
				"PinballBot",						// window title
				SDL_WINDOWPOS_CENTERED,				// initial x position
				SDL_WINDOWPOS_CENTERED,				// initial y position
				this->width,						// width, in pixels
				this->height,						// height, in pixels
				SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI //enables retina support
			);

			if (window == nullptr) {
				printf("Could not create window: %s\n", SDL_GetError());
				SDL_Quit();
				return;
			}

			//updates the width and height if there's a high DPI and calc other vars afterwards
			SDL_GL_GetDrawableSize(window, &this->width, &this->height);

			oneMeterInPX = round(SCALING * this->height); /* one meter is equal to half of the width of the window */

			renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
			SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_ADD);

			SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); //white background
			SDL_RenderClear(renderer);
		}

		/**
		 * Uses SDL functions to remove objects
		 */
		~Renderer(){
			SDL_DestroyWindow(window);
			SDL_Quit();
		}

		/**
		 * Redraws the scene onto the window
		 * @return	void
		 */
		void redraw(){

			//filledCircleColor(renderer, 320, 91, 10, createRGBA(255,0,0,255));

			SDL_RenderPresent(renderer);

			SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
			SDL_RenderClear(renderer);
		}

		/**
		 * Draws a polygon
		 * @param	vertices		b2Vec2		The vertices of the polygon
		 * @param	vertexCount		int32		The amount of vertices
		 * @param	red				Uint8		The amount of red	in the color
		 * @param	green			Uint8		The amount of green	in the color
		 * @param	blue			Uint8		The amount of blue	in the color
		 * @param	alpha			Uint8		The opacity of the color
		 * @param	filled			bool		Draw a filled polygon or just the outlines?
		 * @return	void
		 */
		void dPolygon(const b2Vec2* vertices, int32 vertexCount, Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha, bool filled){
			std::vector<short> x(vertexCount);
			std::vector<short> y(vertexCount);

			for(int i=0;i<vertexCount;i++){
				b2Vec2 vec = toScreenCoords(vertices[i]);

				x[i] = (short) vec.x;
				y[i] = (short) vec.y;
			}

			if(filled){
				filledPolygonRGBA(renderer, x.data(), y.data(), vertexCount, red, green, blue, alpha);
			}else{
				polygonRGBA(renderer, x.data(), y.data(), vertexCount, red, green, blue, alpha);
			}
		}

		/**
		 * Draws a circle
		 * @param	center			b2Vec2		The coordinates of the center of the circle
		 * @param	radius			float32		The radius of the circle
		 * @param	red				Uint8		The amount of red	in the color
		 * @param	green			Uint8		The amount of green	in the color
		 * @param	blue			Uint8		The amount of blue	in the color
		 * @param	alpha			Uint8		The opacity of the color
		 * @param	filled			bool		Draw a filled polygon or just the outlines?
		 * @return	void
		 */
		void dCircle(const b2Vec2& center, float32 radius, Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha, bool filled){
			b2Vec2 coords = toScreenCoords(center);

			if(filled){
				filledCircleRGBA(renderer, (Sint16) coords.x, (Sint16) coords.y, (Sint16) metersToPixels(radius), red, green, blue, alpha);
			}else{
				circleRGBA(renderer, (Sint16) coords.x, (Sint16) coords.y, (Sint16) metersToPixels(radius), red, green, blue, alpha);
			}
		}

		/**
		 * Implements the b2Draw functions
		 */
			void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {
				dPolygon(vertices, vertexCount, 0, 0, 0, 255, false);
			}
			void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {
				dPolygon(vertices, vertexCount, 0, 0, 0, 255, true);
			}

			void DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color) {
				dCircle(center, radius, 0, 0, 0, 255, false);
			}
			void DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color) {
				dCircle(center, radius, 0, 0, 0, 255, true);
			}

			void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) {
				lineRGBA(renderer, (Sint16) p1.x, (Sint16) p1.y, (Sint16) p2.x, (Sint16) p2.y, 0, 0, 0, 255);
			}
			void DrawTransform(const b2Transform& xf) {

			}

};

#endif /* PINBALL_BOT_RENDERER */
<|endoftext|>


<commit_before>// Makes distance matrix based on procrustes RMS between maps

#include "acmacs-base/argv.hh"
#include "acmacs-base/range-v3.hh"
#include "acmacs-base/quicklook.hh"
#include "acmacs-base/color-gradient.hh"
#include "acmacs-chart-2/factory-import.hh"
#include "acmacs-chart-2/chart-modify.hh"
#include "acmacs-chart-2/procrustes.hh"
#include "acmacs-chart-2/common.hh"
#include "acmacs-chart-2/stress.hh"
#include "acmacs-chart-2/randomizer.hh"
#include "acmacs-chart-2/bounding-ball.hh"
#include "acmacs-draw/surface-cairo.hh"
#include "acmacs-draw/drawi-generator.hh"

// ----------------------------------------------------------------------

using namespace acmacs::argv;
struct Options : public argv
{
    Options(int a_argc, const char* const a_argv[], on_error on_err = on_error::exit) : argv() { parse(a_argc, a_argv, on_err); }

    std::string_view help_pre() const override { return "compare titers of mutiple tables"; }
    argument<str_array> charts{*this, arg_name{"chart"}, mandatory};

    option<str> output{*this, 'o', desc{"output .drawi file"}};
    option<str> pdf_output{*this, "pdf", desc{"output .pdf file"}};
    option<size_t> number_of_optimizations{*this, 'n', dflt{100ul}};
    option<str> minimum_column_basis{*this, 'm', dflt{"none"}};
    option<size_t> number_of_dimensions{*this, 'd', dflt{2ul}};
    option<bool>   rough{*this, "rough"};
};

int main(int argc, char* const argv[])
{
    using namespace std::string_view_literals;
    int exit_code = 0;
    try {
        Options opt(argc, argv);
        const auto precision = (opt.rough) ? acmacs::chart::optimization_precision::rough : acmacs::chart::optimization_precision::fine;

        std::vector<acmacs::chart::ChartModify> charts;
        for (const auto& filename : opt.charts)
            charts.emplace_back(acmacs::chart::import_from_file(filename));

        const auto chart_name = [](const auto& chart) {
            if (const auto date = chart.info()->date(); !date.empty())
                return *date;
            else
                return chart.info()->make_name();
        };

        for (auto& chart : charts) {
            chart.projections_modify()->remove_all();
            chart.relax(acmacs::chart::number_of_optimizations_t{*opt.number_of_optimizations}, acmacs::chart::MinimumColumnBasis{opt.minimum_column_basis},
                        acmacs::number_of_dimensions_t{*opt.number_of_dimensions}, acmacs::chart::use_dimension_annealing::no, acmacs::chart::optimization_options{precision});
            chart.projections_modify()->sort();
            // AD_DEBUG("{}  {:9.4f}", chart_name(chart), chart.projections()->best()->stress());
        }

        acmacs::chart::TableDistances table_distances;
        for (size_t t1 = 0; t1 < (charts.size() - 1); ++t1) {
            for (size_t t2 = t1 + 1; t2 < charts.size(); ++t2) {
                acmacs::chart::CommonAntigensSera common(charts[t1], charts[t2], acmacs::chart::CommonAntigensSera::match_level_t::strict);
                if (!common.points().empty()) {
                    const auto procrustes_data = acmacs::chart::procrustes(*charts[t1].projection(0), *charts[t2].projection(0), common.points(), acmacs::chart::procrustes_scaling_t::no);
                    table_distances.add_value(acmacs::chart::Titer::Regular, t1, t2, procrustes_data.rms);
                    AD_DEBUG("{} {}  {:6.4f}", chart_name(charts[t1]), chart_name(charts[t2]), procrustes_data.rms);
                }
            }
        }

        // ----------------------------------------------------------------------
        // make-module, also for chart-table-compare

        acmacs::chart::Stress stress(acmacs::number_of_dimensions_t{2}, charts.size());
        stress.table_distances() = table_distances;

        double best_stress{9e99};
        acmacs::Layout best_layout(charts.size(), stress.number_of_dimensions());
        for ([[maybe_unused]] auto attempt : acmacs::range(*opt.number_of_optimizations)) {
            acmacs::Layout layout(charts.size(), stress.number_of_dimensions());
            acmacs::chart::LayoutRandomizerPlain rnd(10.0, std::nullopt);
            for (auto point_no : acmacs::range(layout.number_of_points()))
                layout.update(point_no, rnd.get(stress.number_of_dimensions()));

            const auto status1 =
                acmacs::chart::optimize(acmacs::chart::optimization_method::alglib_cg_pca, stress, layout.data(), layout.data() + layout.size(), acmacs::chart::optimization_precision::rough);
            // fmt::print("{:3d} stress: {} <- {} iterations: {}\n", attempt, status1.final_stress, status1.initial_stress, status1.number_of_iterations);
            if (status1.final_stress < best_stress) {
                best_stress = status1.final_stress;
                best_layout = layout;
            }
        }
        AD_DEBUG("best_stress: {}", best_stress);

        if (opt.output) {
            std::vector<std::string> years;
            acmacs::drawi::Generator gen;
            const acmacs::BoundingBall bb{minimum_bounding_ball(best_layout)};
            gen.viewport().set_from_center_size(bb.center(), bb.hv_diameter_whole_number());
            for (const auto t1 : range_from_0_to(charts.size())) {
                const auto label = chart_name(charts[t1]);
                gen.add<acmacs::drawi::Generator::Point>().coord(best_layout[t1]).label(label);
                if (label.size() >= 4 && (label[0] == '1' || label[0] == '2'))
                    years.push_back(label.substr(0, 4));
            }
            gen.add<acmacs::drawi::Generator::PointModify>().shape(acmacs::drawi::Generator::Point::Triangle).fill(GREEN).size(Pixels{10}).label_size(Pixels{7}).label_offset({0.0, 1.2});

            sort_unique(years);
            if (years.size() > 1) {
                for (const auto [no, year] : ranges::views::zip(ranges::views::iota(0ul), years)) {
                    AD_DEBUG("{:2d} {}", no, year);
                    gen.add<acmacs::drawi::Generator::PointModify>().select("name"sv, fmt::format("~{}", year)).fill(acmacs::color::perceptually_uniform_heatmap(years.size(), no));
                }
            }

            const auto title = fmt::format("{} {} {} {} {}-{}", charts[0].info()->lab(), charts[0].info()->virus_type(), charts[0].lineage(), charts[0].info()->assay(), years.front(), years.back());
            gen.title(title);
            // AD_DEBUG("title: \"{}\"", title);

            gen.generate(opt.output);

            if (opt.pdf_output)
                acmacs::run_and_detach({"drawi", opt.output->data(), opt.pdf_output->data(), "--open"}, 0);
        }
    }
    catch (std::exception& err) {
        AD_ERROR("{}", err);
        exit_code = 2;
    }
    return exit_code;
}

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>chart-table-map-compare<commit_after>// Makes distance matrix based on procrustes RMS between maps

#include "acmacs-base/argv.hh"
#include "acmacs-base/range-v3.hh"
#include "acmacs-base/quicklook.hh"
#include "acmacs-base/color-gradient.hh"
#include "acmacs-chart-2/factory-import.hh"
#include "acmacs-chart-2/chart-modify.hh"
#include "acmacs-chart-2/procrustes.hh"
#include "acmacs-chart-2/common.hh"
#include "acmacs-chart-2/stress.hh"
#include "acmacs-chart-2/randomizer.hh"
#include "acmacs-chart-2/bounding-ball.hh"
#include "acmacs-draw/surface-cairo.hh"
#include "acmacs-draw/drawi-generator.hh"

// ----------------------------------------------------------------------

using namespace acmacs::argv;
struct Options : public argv
{
    Options(int a_argc, const char* const a_argv[], on_error on_err = on_error::exit) : argv() { parse(a_argc, a_argv, on_err); }

    std::string_view help_pre() const override { return "compare titers of mutiple tables"; }
    argument<str_array> charts{*this, arg_name{"chart"}, mandatory};

    option<str> output{*this, 'o', desc{"output .drawi file"}};
    option<str> pdf_output{*this, "pdf", desc{"output .pdf file"}};
    option<size_t> number_of_optimizations{*this, 'n', dflt{100ul}};
    option<str> minimum_column_basis{*this, 'm', dflt{"none"}};
    option<size_t> number_of_dimensions{*this, 'd', dflt{2ul}};
    option<bool>   rough{*this, "rough"};
};

int main(int argc, char* const argv[])
{
    using namespace std::string_view_literals;
    int exit_code = 0;
    try {
        Options opt(argc, argv);
        const auto precision = (opt.rough) ? acmacs::chart::optimization_precision::rough : acmacs::chart::optimization_precision::fine;

        std::vector<acmacs::chart::ChartModify> charts;
        for (const auto& filename : opt.charts)
            charts.emplace_back(acmacs::chart::import_from_file(filename));

        const auto chart_name = [](const auto& chart) {
            if (const auto date = chart.info()->date(); !date.empty())
                return *date;
            else
                return chart.info()->make_name();
        };

        for (auto& chart : charts) {
            chart.projections_modify()->remove_all();
            chart.relax(acmacs::chart::number_of_optimizations_t{*opt.number_of_optimizations}, acmacs::chart::MinimumColumnBasis{opt.minimum_column_basis},
                        acmacs::number_of_dimensions_t{*opt.number_of_dimensions}, acmacs::chart::use_dimension_annealing::no, acmacs::chart::optimization_options{precision});
            chart.projections_modify()->sort();
            AD_DEBUG("{}  {:9.4f}", chart_name(chart), chart.projections()->best()->stress());
        }

        acmacs::chart::TableDistances table_distances;
        for (size_t t1 = 0; t1 < (charts.size() - 1); ++t1) {
            for (size_t t2 = t1 + 1; t2 < charts.size(); ++t2) {
                acmacs::chart::CommonAntigensSera common(charts[t1], charts[t2], acmacs::chart::CommonAntigensSera::match_level_t::strict);
                if (!common.points().empty()) {
                    const auto procrustes_data = acmacs::chart::procrustes(*charts[t1].projection(0), *charts[t2].projection(0), common.points(), acmacs::chart::procrustes_scaling_t::no);
                    if (!std::isnan(procrustes_data.rms))
                        table_distances.add_value(acmacs::chart::Titer::Regular, t1, t2, procrustes_data.rms);
                    AD_DEBUG("{} {}  {:6.4f}", chart_name(charts[t1]), chart_name(charts[t2]), procrustes_data.rms);
                }
            }
        }

        // ----------------------------------------------------------------------
        // make-module, also for chart-table-compare

        acmacs::chart::Stress stress(acmacs::number_of_dimensions_t{2}, charts.size());
        stress.table_distances() = table_distances;

        double best_stress{9e99};
        acmacs::Layout best_layout(charts.size(), stress.number_of_dimensions());
        for ([[maybe_unused]] auto attempt : acmacs::range(*opt.number_of_optimizations)) {
            acmacs::Layout layout(charts.size(), stress.number_of_dimensions());
            acmacs::chart::LayoutRandomizerPlain rnd(10.0, std::nullopt);
            for (auto point_no : acmacs::range(layout.number_of_points()))
                layout.update(point_no, rnd.get(stress.number_of_dimensions()));

            const auto status1 =
                acmacs::chart::optimize(acmacs::chart::optimization_method::alglib_cg_pca, stress, layout.data(), layout.data() + layout.size(), acmacs::chart::optimization_precision::rough);
            // fmt::print("{:3d} stress: {} <- {} iterations: {}\n", attempt, status1.final_stress, status1.initial_stress, status1.number_of_iterations);
            if (status1.final_stress < best_stress) {
                best_stress = status1.final_stress;
                best_layout = layout;
            }
        }
        AD_DEBUG("best_stress: {}", best_stress);

        if (opt.output) {
            std::vector<std::string> years;
            acmacs::drawi::Generator gen;
            const acmacs::BoundingBall bb{minimum_bounding_ball(best_layout)};
            gen.viewport().set_from_center_size(bb.center(), bb.hv_diameter_whole_number());
            for (const auto t1 : range_from_0_to(charts.size())) {
                const auto label = chart_name(charts[t1]);
                gen.add<acmacs::drawi::Generator::Point>().coord(best_layout[t1]).label(label);
                if (label.size() >= 4 && (label[0] == '1' || label[0] == '2'))
                    years.push_back(label.substr(0, 4));
            }
            gen.add<acmacs::drawi::Generator::PointModify>().shape(acmacs::drawi::Generator::Point::Triangle).fill(GREEN).size(Pixels{10}).label_size(Pixels{7}).label_offset({0.0, 1.2});

            sort_unique(years);
            if (years.size() > 1) {
                for (const auto [no, year] : ranges::views::zip(ranges::views::iota(0ul), years)) {
                    AD_DEBUG("{:2d} {}", no, year);
                    gen.add<acmacs::drawi::Generator::PointModify>().select("name"sv, fmt::format("~{}", year)).fill(acmacs::color::perceptually_uniform_heatmap(years.size(), no));
                }
            }

            const auto title = fmt::format("{} {} {} {} {}-{}", charts[0].info()->lab(), charts[0].info()->virus_type(), charts[0].lineage(), charts[0].info()->assay(), years.front(), years.back());
            gen.title(title);
            // AD_DEBUG("title: \"{}\"", title);

            gen.generate(opt.output);

            if (opt.pdf_output)
                acmacs::run_and_detach({"drawi", opt.output->data(), opt.pdf_output->data(), "--open"}, 0);
        }
    }
    catch (std::exception& err) {
        AD_ERROR("{}", err);
        exit_code = 2;
    }
    return exit_code;
}

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkDerivativeImageFilterTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Insight Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include <iostream>
#include "itkImage.h"
#include "itkDerivativeImageFilter.h"
#include "itkNullImageToImageFilterDriver.txx"

int itkDerivativeImageFilterTest(int , char * [] )
{
  //  try
    {
      typedef  itk::Image<float, 3> ImageType;

      // Set up filter
      itk::DerivativeImageFilter<ImageType, ImageType>::Pointer filter
        = itk::DerivativeImageFilter<ImageType, ImageType>::New();
      filter->SetOrder(1);
      filter->SetDirection(1);
      std::cout << "About to execute" << std::endl;
      // Run Test
      itk::Size<3> sz;
      sz[0]=256;
      sz[1]=256;
      sz[2]=5;
      itk::NullImageToImageFilterDriver< ImageType, ImageType >
        test1;
      test1.SetImageSize(sz);
      test1.SetFilter(filter.GetPointer());
      test1.Execute();
      std::cout << "Finished executing" << std::endl;
    }
//   catch(itk::ExceptionObject &err)
//     {
//       (&err)->Print(std::cerr);
//       return 1;
//     }
  return 0;
}
<commit_msg>ENH: added a filter watcher.<commit_after>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkDerivativeImageFilterTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Insight Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include <iostream>
#include "itkImage.h"
#include "itkDerivativeImageFilter.h"
#include "itkNullImageToImageFilterDriver.txx"
#include "itkFilterWatcher.h"

int itkDerivativeImageFilterTest(int , char * [] )
{
  //  try
    {
      typedef  itk::Image<float, 3> ImageType;

      // Set up filter
      itk::DerivativeImageFilter<ImageType, ImageType>::Pointer filter
        = itk::DerivativeImageFilter<ImageType, ImageType>::New();
      FilterWatcher watcher(filter);

      filter->SetOrder(1);
      filter->SetDirection(1);
      std::cout << "About to execute" << std::endl;
      // Run Test
      itk::Size<3> sz;
      sz[0]=256;
      sz[1]=256;
      sz[2]=5;
      itk::NullImageToImageFilterDriver< ImageType, ImageType >
        test1;
      test1.SetImageSize(sz);
      test1.SetFilter(filter.GetPointer());
      test1.Execute();
      std::cout << "Finished executing" << std::endl;
    }
//   catch(itk::ExceptionObject &err)
//     {
//       (&err)->Print(std::cerr);
//       return 1;
//     }
  return 0;
}
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkRigid3DPerspectiveTransformTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 2001 Insight Consortium
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

 * The name of the Insight Consortium, nor the names of any consortium members,
   nor of any contributors, may be used to endorse or promote products derived
   from this software without specific prior written permission.

  * Modified source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

=========================================================================*/

#include <iostream>

#include "itkRigid3DPerspectiveTransform.h"
#include "vnl/vnl_vector_fixed.h"
#include "itkVector.h"

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

  typedef itk::Rigid3DPerspectiveTransform<double>  TransformType;

  const double epsilon = 1e-10;
  const unsigned int N = 3;

  bool Ok = true;

  /* Create a 3D identity transformation and show its parameters */
  {
    TransformType::Pointer  identityTransform = TransformType::New();
    TransformType::OffsetType offset = identityTransform->GetOffset();
    std::cout << "Vector from instantiating an identity transform:  ";
    std::cout << offset << std::endl;

    for(unsigned int i=0; i<N; i++)
    {
      if( fabs( offset[i]-0.0 ) > epsilon )
      {
        Ok = false;
        break;
      }
    }
    if( !Ok )
    {
      std::cerr << "Identity doesn't have a null offset" << std::endl;
      return EXIT_FAILURE;
    }
  }

  /* Create a Rigid 3D transform with translation */
  {
    TransformType::Pointer  translation = TransformType::New();
    TransformType::OffsetType ioffset;
    ioffset = 1,4,9;

    translation->SetOffset( ioffset );

    TransformType::OffsetType offset = translation->GetOffset();
    std::cout << "pure Translation test:  ";
    std::cout << offset << std::endl;

    for(unsigned int i=0; i<N; i++)
    {
      if( fabs( offset[i]- ioffset[i] ) > epsilon )
      {
        Ok = false;
        break;
      }
    }
    if( !Ok )
    {
      std::cerr << "Get Offset  differs from SetOffset value " << std::endl;
      return EXIT_FAILURE;
    }

    {
      // Translate an itk::Point
      TransformType::InputPointType p;
      p = 10,10,10;
      TransformType::InputPointType q;
      q = p + ioffset;
      TransformType::OutputPointType r;
      r = translation->TransformPoint( p );
      for(unsigned int i=0; i<N; i++)
      {
        if( fabs( q[i]- r[i] ) > epsilon )
        {
          Ok = false;
          break;
        }
      }
      if( !Ok )
      {
        std::cerr << "Error translating point: " << p << std::endl;
        std::cerr << "Result should be       : " << q << std::endl;
        std::cerr << "Reported Result is     : " << r << std::endl;
        return EXIT_FAILURE;
      }
      else
      {
        std::cout << "Ok translating an itk::Point " << std::endl;
      }
    }

    {
      // Translate an itk::Vector
      TransformType::InputVectorType p;
      p = 10,10,10;
      TransformType::OutputVectorType q;
      q = translation->TransformVector( p );
      for(unsigned int i=0; i<N; i++)
      {
        if( fabs( q[i]- p[i] ) > epsilon )
        {
          Ok = false;
          break;
        }
      }
      if( !Ok )
      {
        std::cerr << "Error translating vector: " << p << std::endl;
        std::cerr << "Reported Result is      : " << q << std::endl;
        return EXIT_FAILURE;
      }
      else
      {
        std::cout << "Ok translating an itk::Vector " << std::endl;
      }
    }

    {
      // Translate an itk::CovariantVector
      TransformType::InputCovariantVectorType p;
      p = 10,10,10;
      TransformType::OutputCovariantVectorType q;
      q = translation->TransformCovariantVector( p );
      for(unsigned int i=0; i<N; i++)
      {
        if( fabs( q[i]- p[i] ) > epsilon )
        {
          Ok = false;
          break;
        }
      }
      if( !Ok )
      {
        std::cerr << "Error translating covariant vector: " << p << std::endl;
        std::cerr << "Reported Result is      : " << q << std::endl;
        return EXIT_FAILURE;
      }
      else
      {
        std::cout << "Ok translating an itk::CovariantVector " << std::endl;
      }
    }

    {
      // Translate a vnl_vector
      TransformType::InputVnlVectorType p;
      p[0] = 11;
      p[1] =  7;
      p[2] = 15;
      TransformType::OutputVnlVectorType q;
      q = translation->TransformVnlVector( p );
      for(unsigned int i=0; i<N; i++)
      {
        if( fabs( q[i] - p[i] ) > epsilon )
        {
          Ok = false;
          break;
        }
      }
      if( !Ok )
      {
        std::cerr << "Error translating vnl_vector: " << p << std::endl;
        std::cerr << "Reported Result is      : " << q << std::endl;
        return EXIT_FAILURE;
      }
      else
      {
        std::cout << "Ok translating an vnl_Vector " << std::endl;
      }
    }

  }

  /* Create a Rigid 3D transform with rotation */
  {
    TransformType::Pointer  rotation = TransformType::New();

    itk::Vector<double,3> axis;
    axis = 1,1,1;

    const double angle = (atan(1.0)/45.0)*120.0; // turn 120 degrees

    // this rotation will permute the axis x->y, y->z, z->x
    rotation->SetRotation( axis, angle );

    TransformType::OffsetType offset = rotation->GetOffset();
    std::cout << "pure Rotation test:  ";
    std::cout << offset << std::endl;

    for(unsigned int i=0; i<N; i++)
    {
      if( fabs( offset[i] - 0.0 ) > epsilon )
      {
        Ok = false;
        break;
      }
    }
    if( !Ok )
    {
      std::cerr << "Get Offset  differs from null in rotation " << std::endl;
      return EXIT_FAILURE;
    }

    {
      // Rotate an itk::Point
      TransformType::InputPointType p;
      p = 1,4,9;
      TransformType::OutputPointType q;
      q[0] = p[1];
      q[1] = p[2];
      q[2] = p[0];

      TransformType::OutputPointType r;
      r = rotation->TransformPoint( p );
      for(unsigned int i=0; i<N; i++)
      {
        if( fabs( q[i]- r[i] ) > epsilon )
        {
          Ok = false;
          break;
        }
      }
      if( !Ok )
      {
        std::cerr << "Error rotating point : " << p << std::endl;
        std::cerr << "Result should be     : " << q << std::endl;
        std::cerr << "Reported Result is   : " << r << std::endl;
        return EXIT_FAILURE;
      }
      else
      {
        std::cout << "Ok rotating an itk::Point " << std::endl;
      }
    }

    {
      // Translate an itk::Vector
      TransformType::InputVectorType p;
      p = 1,4,9;
      TransformType::OutputVectorType q;
      q[0] = p[1];
      q[1] = p[2];
      q[2] = p[0];

      TransformType::OutputVectorType r;
      r = rotation->TransformVector( p );
      for(unsigned int i=0; i<N; i++)
      {
        if( fabs( q[i] - r[i] ) > epsilon )
        {
          Ok = false;
          break;
        }
      }
      if( !Ok )
      {
        std::cerr << "Error rotating vector : " << p << std::endl;
        std::cerr << "Result should be      : " << q << std::endl;
        std::cerr << "Reported Result is    : " << r << std::endl;
        return EXIT_FAILURE;
      }
      else
      {
        std::cout << "Ok rotating an itk::Vector " << std::endl;
      }
    }

    {
      // Translate an itk::CovariantVector
      TransformType::InputCovariantVectorType p;
      p = 1,4,9;
      TransformType::OutputCovariantVectorType q;
      q[0] = p[1];
      q[1] = p[2];
      q[2] = p[0];

      TransformType::OutputCovariantVectorType r;
      r = rotation->TransformCovariantVector( p );
      for(unsigned int i=0; i<N; i++)
      {
        if( fabs( q[i] - r[i] ) > epsilon )
        {
          Ok = false;
          break;
        }
      }
      if( !Ok )
      {
        std::cerr << "Error rotating covariant vector : " << p << std::endl;
        std::cerr << "Result should be                : " << q << std::endl;
        std::cerr << "Reported Result is              : " << r << std::endl;
        return EXIT_FAILURE;
      }
      else
      {
        std::cout << "Ok rotating an itk::CovariantVector " << std::endl;
      }
    }

    {
      // Translate a vnl_vector
      TransformType::InputVnlVectorType p;
      p[0] = 1;
      p[1] = 4;
      p[2] = 9;

      TransformType::OutputVnlVectorType q;

      q[0] = p[1];
      q[1] = p[2];
      q[2] = p[0];

      TransformType::OutputVnlVectorType r;
      r = rotation->TransformVnlVector( p );
      for(unsigned int i=0; i<N; i++)
      {
        if( fabs( q[i] - r[i] ) > epsilon )
        {
          Ok = false;
          break;
        }
      }
      if( !Ok )
      {
        std::cerr << "Error rotating vnl_vector : " << p << std::endl;
        std::cerr << "Result should be          : " << q << std::endl;
        std::cerr << "Reported Result is        : " << r << std::endl;
        return EXIT_FAILURE;
      }
      else
      {
        std::cout << "Ok rotating an vnl_Vector " << std::endl;
      }
    }

  }

  return EXIT_SUCCESS;

}
<commit_msg>FIX: Test corrected for testing for perspective. Tests for vectors removed<commit_after>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkRigid3DPerspectiveTransformTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 2001 Insight Consortium
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

 * The name of the Insight Consortium, nor the names of any consortium members,
   nor of any contributors, may be used to endorse or promote products derived
   from this software without specific prior written permission.

  * Modified source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

=========================================================================*/

#include <iostream>

#include "itkRigid3DPerspectiveTransform.h"
#include "vnl/vnl_vector_fixed.h"
#include "itkVector.h"

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

  typedef itk::Rigid3DPerspectiveTransform<double>  TransformType;

  const double epsilon = 1e-10;
  const unsigned int N = 3;

  const double focal   = 100.0;
  const double width   = 100.0;
  const double height  = 100.0;

  bool Ok = true;

  /* Create a 3D identity transformation and show its parameters */
  {
    TransformType::Pointer  identityTransform = TransformType::New();
    identityTransform->SetFocalDistance(  focal );
    identityTransform->SetHeight( height );
    identityTransform->SetWidth(  width );

    TransformType::OffsetType offset = identityTransform->GetOffset();
    std::cout << "Vector from instantiating an identity transform:  ";
    std::cout << offset << std::endl;

    for(unsigned int i=0; i<N; i++)
    {
      if( fabs( offset[i]-0.0 ) > epsilon )
      {
        Ok = false;
        break;
      }
    }
    if( !Ok )
    {
      std::cerr << "Identity doesn't have a null offset" << std::endl;
      return EXIT_FAILURE;
    }
  }

  /* Create a Rigid 3D transform with translation */
  {
    TransformType::Pointer  translation = TransformType::New();
    translation->SetFocalDistance( focal );
    translation->SetHeight( height );
    translation->SetWidth(  width );

    TransformType::OffsetType ioffset;
    ioffset.Fill(0.0);

    translation->SetOffset( ioffset );

    TransformType::OffsetType offset = translation->GetOffset();
    std::cout << "pure Translation test:  ";
    std::cout << offset << std::endl;

    for(unsigned int i=0; i<N; i++)
    {
      if( fabs( offset[i]- ioffset[i] ) > epsilon )
      {
        Ok = false;
        break;
      }
    }
    if( !Ok )
    {
      std::cerr << "Get Offset  differs from SetOffset value " << std::endl;
      return EXIT_FAILURE;
    }

    {
      // Project an itk::Point
      TransformType::InputPointType p;
      p.Fill(0.0);
      TransformType::InputPointType q;
      q = p + ioffset;
      TransformType::OutputPointType s;
      const double factor = height/(q[2]+focal);
      s[0] = q[0] * factor + width/2.0;
      s[1] = q[1] * factor + height/2.0;
      TransformType::OutputPointType r;
      r = translation->TransformPoint( p );
      for(unsigned int i=0; i<N-1; i++)
      {
        if( fabs( s[i]- r[i] ) > epsilon )
        {
          Ok = false;
          break;
        }
      }
      if( !Ok )
      {
        std::cerr << "Error translating point: " << p << std::endl;
        std::cerr << "Result should be       : " << s << std::endl;
        std::cerr << "Reported Result is     : " << r << std::endl;
        return EXIT_FAILURE;
      }
      else
      {
        std::cout << "Ok translating an itk::Point " << std::endl;
      }
    }

    {
      // Projecting  an itk::Point
      TransformType::InputPointType p;
      p[0] = 10;
      p[1] = 10;
      p[2] = 10;
      TransformType::InputPointType q;
      q = p + ioffset;
      TransformType::OutputPointType s;
      const double factor = height/(q[2]+focal);
      s[0] = q[0] * factor + width/2.0;
      s[1] = q[1] * factor + height/2.0;
      TransformType::OutputPointType r;
      r = translation->TransformPoint( p );
      for(unsigned int i=0; i<N-1; i++)
      {
        if( fabs( s[i]- r[i] ) > epsilon )
        {
          Ok = false;
          break;
        }
      }
      if( !Ok )
      {
        std::cerr << "Error translating point: " << p << std::endl;
        std::cerr << "Result should be       : " << s << std::endl;
        std::cerr << "Reported Result is     : " << r << std::endl;
        return EXIT_FAILURE;
      }
      else
      {
        std::cout << "Ok translating an itk::Point " << std::endl;
      }
    }

  }

  return EXIT_SUCCESS;

}
<|endoftext|>


<commit_before>// vim:ts=2:sw=2:expandtab:autoindent:filetype=cpp:
/*
Copyright (c) 2008 Aristid Breitkreuz, Ruediger Sonderfeld

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "flusspferd/io/stream_base.hpp"
#include "flusspferd/local_root_scope.hpp"
#include "flusspferd/create.hpp"
#include "flusspferd/string.hpp"
#include "flusspferd/string_io.hpp"
#include "flusspferd/create.hpp"
#include "flusspferd/blob.hpp"
#include <boost/scoped_array.hpp>
#include <cstdlib>

using namespace flusspferd;
using namespace flusspferd::io;

stream_base::stream_base(object const &o, std::streambuf *p)
  : native_object_base(o), streambuf(p)
{
  register_native_method("readWhole", &stream_base::read_whole);
  register_native_method("read", &stream_base::read);
  register_native_method("readWholeBlob", &stream_base::read_whole_blob);
  register_native_method("readBlob", &stream_base::read_blob);
  register_native_method("write", &stream_base::write);
  register_native_method("print", &stream_base::print);
  register_native_method("flush", &stream_base::flush);

  define_property("fieldSeparator", string(" "));
  define_property("recordSeparator", string("\n"));
  define_property("autoflush", false);
}

stream_base::~stream_base()
{}

void stream_base::set_streambuf(std::streambuf *p) {
  streambuf = p;
}

std::streambuf *stream_base::get_streambuf() {
  return streambuf;
}

object stream_base::class_info::create_prototype() {
  local_root_scope scope;

  object proto = create_object();

  create_native_method(proto, "readWhole", 0);
  create_native_method(proto, "read", 1);
  create_native_method(proto, "readWholeBlob", 0);
  create_native_method(proto, "readBlob", 1);
  create_native_method(proto, "write", 1);
  create_native_method(proto, "print", 0);
  create_native_method(proto, "flush", 0);

  return proto;
}

string stream_base::read_whole() {
  std::string data;
  char buf[4096];

  std::streamsize length;

  do {
    length = streambuf->sgetn(buf, sizeof(buf));
    if (length < 0)
      length = 0;
    data.append(buf, length);
  } while (length > 0);

  return string(data);
}

object stream_base::read_whole_blob() {
  unsigned const N = 4096;

  std::vector<char> data;

  std::streamsize length;

  do {
    data.resize(data.size() + N);
    length = streambuf->sgetn(&data[data.size() - N], N);
    if (length < 0)
      length = 0;
    data.resize(data.size() - N + length);
  } while (length > 0);

  return create_native_object<blob>(
      object(), (unsigned char const *)&data[0], data.size());
}

string stream_base::read(unsigned size) {
  if (!size)
    size = 4096;

  boost::scoped_array<char> buf(new char[size + 1]);

  std::streamsize length = streambuf->sgetn(buf.get(), size);
  if (length < 0)
    length = 0;
  buf[length] = '\0';

  return string(buf.get());
}

object stream_base::read_blob(unsigned size) {
  if (!size)
    size = 4096;

  boost::scoped_array<char> buf(new char[size]);

  std::streamsize length = streambuf->sgetn(buf.get(), size);
  if (length < 0)
    length = 0;

  return create_native_object<blob>(
      object(),
      (unsigned char const *) buf.get(),
      length);
}

void stream_base::write(value const &data) {
  if (data.is_string()) {
    string text = data.get_string();
    char const *str = text.c_str();
    streambuf->sputn(text.c_str(), std::strlen(str));
  } else if (data.is_object()) {
    blob &b = flusspferd::get_native<blob>(data.get_object());
    streambuf->sputn((char const*) b.get_data(), b.size());
  } else {
    throw exception("Cannot write non-object non-string value to Stream");
  }
  //TODO slow?
  if (get_property("autoflush").to_boolean())
    flush();
}

void stream_base::print(call_context &x) {
  local_root_scope scope;

  value delim_v = get_property("fieldSeparator");
  string delim;
  if (!delim_v.is_void_or_null())
    delim = delim_v.to_string();

  std::size_t n = x.arg.size();
  for (std::size_t i = 0; i < n; ++i) {
    write(x.arg[i].to_string());
    if (i < n - 1)
      write(delim);
  }

  value record_v = get_property("recordSeparator");
  if (!record_v.is_void_or_null())
    write(record_v.to_string());

  flush();
}

void stream_base::flush() {
  streambuf->pubsync();
}
<commit_msg>IO: make print([1,2,3]) behave same as print(1,2,3);<commit_after>// vim:ts=2:sw=2:expandtab:autoindent:filetype=cpp:
/*
Copyright (c) 2008 Aristid Breitkreuz, Ruediger Sonderfeld

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "flusspferd/io/stream_base.hpp"
#include "flusspferd/local_root_scope.hpp"
#include "flusspferd/create.hpp"
#include "flusspferd/string.hpp"
#include "flusspferd/string_io.hpp"
#include "flusspferd/create.hpp"
#include "flusspferd/blob.hpp"
#include <boost/scoped_array.hpp>
#include <cstdlib>

using namespace flusspferd;
using namespace flusspferd::io;

stream_base::stream_base(object const &o, std::streambuf *p)
  : native_object_base(o), streambuf(p)
{
  register_native_method("readWhole", &stream_base::read_whole);
  register_native_method("read", &stream_base::read);
  register_native_method("readWholeBlob", &stream_base::read_whole_blob);
  register_native_method("readBlob", &stream_base::read_blob);
  register_native_method("write", &stream_base::write);
  register_native_method("print", &stream_base::print);
  register_native_method("flush", &stream_base::flush);

  define_property("fieldSeparator", string(" "));
  define_property("recordSeparator", string("\n"));
  define_property("autoflush", false);
}

stream_base::~stream_base()
{}

void stream_base::set_streambuf(std::streambuf *p) {
  streambuf = p;
}

std::streambuf *stream_base::get_streambuf() {
  return streambuf;
}

object stream_base::class_info::create_prototype() {
  local_root_scope scope;

  object proto = create_object();

  create_native_method(proto, "readWhole", 0);
  create_native_method(proto, "read", 1);
  create_native_method(proto, "readWholeBlob", 0);
  create_native_method(proto, "readBlob", 1);
  create_native_method(proto, "write", 1);
  create_native_method(proto, "print", 0);
  create_native_method(proto, "flush", 0);

  return proto;
}

string stream_base::read_whole() {
  std::string data;
  char buf[4096];

  std::streamsize length;

  do {
    length = streambuf->sgetn(buf, sizeof(buf));
    if (length < 0)
      length = 0;
    data.append(buf, length);
  } while (length > 0);

  return string(data);
}

object stream_base::read_whole_blob() {
  unsigned const N = 4096;

  std::vector<char> data;

  std::streamsize length;

  do {
    data.resize(data.size() + N);
    length = streambuf->sgetn(&data[data.size() - N], N);
    if (length < 0)
      length = 0;
    data.resize(data.size() - N + length);
  } while (length > 0);

  return create_native_object<blob>(
      object(), (unsigned char const *)&data[0], data.size());
}

string stream_base::read(unsigned size) {
  if (!size)
    size = 4096;

  boost::scoped_array<char> buf(new char[size + 1]);

  std::streamsize length = streambuf->sgetn(buf.get(), size);
  if (length < 0)
    length = 0;
  buf[length] = '\0';

  return string(buf.get());
}

object stream_base::read_blob(unsigned size) {
  if (!size)
    size = 4096;

  boost::scoped_array<char> buf(new char[size]);

  std::streamsize length = streambuf->sgetn(buf.get(), size);
  if (length < 0)
    length = 0;

  return create_native_object<blob>(
      object(),
      (unsigned char const *) buf.get(),
      length);
}

void stream_base::write(value const &data) {
  if (data.is_string()) {
    string text = data.get_string();
    char const *str = text.c_str();
    streambuf->sputn(text.c_str(), std::strlen(str));
  } else if (data.is_object()) {
    blob &b = flusspferd::get_native<blob>(data.get_object());
    streambuf->sputn((char const*) b.get_data(), b.size());
  } else {
    throw exception("Cannot write non-object non-string value to Stream");
  }
  //TODO slow?
  if (get_property("autoflush").to_boolean())
    flush();
}

void stream_base::print(call_context &x) {
  local_root_scope scope;

  value delim_v = get_property("fieldSeparator");
  string delim;
  if (!delim_v.is_void_or_null())
    delim = delim_v.to_string();

  // If passed a single array as an arugment, treat it as the arguments. i.e:
  // print([1,2,3]) would produces same output as print(1,2,3);

  if (x.arg.size() == 1 && x.arg[0].is_object() &&
      x.arg[0].get_object().is_array())
  {
    array a = x.arg[0].get_object();
    std::size_t n = a.get_length();
    for (std::size_t i = 0; i < n; ++i) {
      write(a.get_element(i).to_string());
      if (i < n - 1)
        write(delim);
    }
  }
  else {
    std::size_t n = x.arg.size();
    for (std::size_t i = 0; i < n; ++i) {
      write(x.arg[i].to_string());
      if (i < n - 1)
        write(delim);
    }
  }

  value record_v = get_property("recordSeparator");
  if (!record_v.is_void_or_null())
    write(record_v.to_string());

  flush();
}

void stream_base::flush() {
  streambuf->pubsync();
}
<|endoftext|>