<commit_before>//===-- AddressSanitizerRuntime.cpp -----------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "AddressSanitizerRuntime.h"

#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginInterface.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/RegularExpression.h"
#include "lldb/Core/Stream.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Expression/UserExpression.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"

using namespace lldb;
using namespace lldb_private;

lldb::InstrumentationRuntimeSP
AddressSanitizerRuntime::CreateInstance(const lldb::ProcessSP &process_sp) {
  return InstrumentationRuntimeSP(new AddressSanitizerRuntime(process_sp));
}

void AddressSanitizerRuntime::Initialize() {
  PluginManager::RegisterPlugin(
      GetPluginNameStatic(), "AddressSanitizer instrumentation runtime plugin.",
      CreateInstance, GetTypeStatic);
}

void AddressSanitizerRuntime::Terminate() {
  PluginManager::UnregisterPlugin(CreateInstance);
}

lldb_private::ConstString AddressSanitizerRuntime::GetPluginNameStatic() {
  return ConstString("AddressSanitizer");
}

lldb::InstrumentationRuntimeType AddressSanitizerRuntime::GetTypeStatic() {
  return eInstrumentationRuntimeTypeAddressSanitizer;
}

AddressSanitizerRuntime::~AddressSanitizerRuntime() { Deactivate(); }

const RegularExpression &
AddressSanitizerRuntime::GetPatternForRuntimeLibrary() {
  // FIXME: This shouldn't include the "dylib" suffix.
  static RegularExpression regex(
      llvm::StringRef("libclang_rt.asan_(.*)_dynamic\\.dylib"));
  return regex;
}

bool AddressSanitizerRuntime::CheckIfRuntimeIsValid(
    const lldb::ModuleSP module_sp) {
  const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(
      ConstString("__asan_get_alloc_stack"), lldb::eSymbolTypeAny);

  return symbol != nullptr;
}

#define RETRIEVE_REPORT_DATA_FUNCTION_TIMEOUT_USEC 2 * 1000 * 1000
const char *address_sanitizer_retrieve_report_data_prefix = R"(
extern "C"
{
int __asan_report_present();
void *__asan_get_report_pc();
void *__asan_get_report_bp();
void *__asan_get_report_sp();
void *__asan_get_report_address();
const char *__asan_get_report_description();
int __asan_get_report_access_type();
size_t __asan_get_report_access_size();
}
)";

const char *address_sanitizer_retrieve_report_data_command = R"(
struct {
    int present;
    int access_type;
    void *pc;
    void *bp;
    void *sp;
    void *address;
    size_t access_size;
    const char *description;
} t;

t.present = __asan_report_present();
t.access_type = __asan_get_report_access_type();
t.pc = __asan_get_report_pc();
t.bp = __asan_get_report_bp();
t.sp = __asan_get_report_sp();
t.address = __asan_get_report_address();
t.access_size = __asan_get_report_access_size();
t.description = __asan_get_report_description();
t
)";

StructuredData::ObjectSP AddressSanitizerRuntime::RetrieveReportData() {
  ProcessSP process_sp = GetProcessSP();
  if (!process_sp)
    return StructuredData::ObjectSP();

  ThreadSP thread_sp =
      process_sp->GetThreadList().GetExpressionExecutionThread();
  StackFrameSP frame_sp = thread_sp->GetSelectedFrame();

  if (!frame_sp)
    return StructuredData::ObjectSP();

  EvaluateExpressionOptions options;
  options.SetUnwindOnError(true);
  options.SetTryAllThreads(true);
  options.SetStopOthers(true);
  options.SetIgnoreBreakpoints(true);
  options.SetTimeoutUsec(RETRIEVE_REPORT_DATA_FUNCTION_TIMEOUT_USEC);
  options.SetPrefix(address_sanitizer_retrieve_report_data_prefix);
  options.SetAutoApplyFixIts(false);
  options.SetLanguage(eLanguageTypeObjC_plus_plus);

  ValueObjectSP return_value_sp;
  ExecutionContext exe_ctx;
  Error eval_error;
  frame_sp->CalculateExecutionContext(exe_ctx);
  ExpressionResults result = UserExpression::Evaluate(
      exe_ctx, options, address_sanitizer_retrieve_report_data_command, "",
      return_value_sp, eval_error);
  if (result != eExpressionCompleted) {
    process_sp->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf(
        "Warning: Cannot evaluate AddressSanitizer expression:\n%s\n",
        eval_error.AsCString());
    return StructuredData::ObjectSP();
  }

  int present = return_value_sp->GetValueForExpressionPath(".present")
                    ->GetValueAsUnsigned(0);
  if (present != 1)
    return StructuredData::ObjectSP();

  addr_t pc =
      return_value_sp->GetValueForExpressionPath(".pc")->GetValueAsUnsigned(0);
  /* commented out because rdar://problem/18533301
  addr_t bp =
  return_value_sp->GetValueForExpressionPath(".bp")->GetValueAsUnsigned(0);
  addr_t sp =
  return_value_sp->GetValueForExpressionPath(".sp")->GetValueAsUnsigned(0);
  */
  addr_t address = return_value_sp->GetValueForExpressionPath(".address")
                       ->GetValueAsUnsigned(0);
  addr_t access_type =
      return_value_sp->GetValueForExpressionPath(".access_type")
          ->GetValueAsUnsigned(0);
  addr_t access_size =
      return_value_sp->GetValueForExpressionPath(".access_size")
          ->GetValueAsUnsigned(0);
  addr_t description_ptr =
      return_value_sp->GetValueForExpressionPath(".description")
          ->GetValueAsUnsigned(0);
  std::string description;
  Error error;
  process_sp->ReadCStringFromMemory(description_ptr, description, error);

  StructuredData::Dictionary *dict = new StructuredData::Dictionary();
  dict->AddStringItem("instrumentation_class", "AddressSanitizer");
  dict->AddStringItem("stop_type", "fatal_error");
  dict->AddIntegerItem("pc", pc);
  /* commented out because rdar://problem/18533301
  dict->AddIntegerItem("bp", bp);
  dict->AddIntegerItem("sp", sp);
  */
  dict->AddIntegerItem("address", address);
  dict->AddIntegerItem("access_type", access_type);
  dict->AddIntegerItem("access_size", access_size);
  dict->AddStringItem("description", description);

  return StructuredData::ObjectSP(dict);
}

std::string
AddressSanitizerRuntime::FormatDescription(StructuredData::ObjectSP report) {
  std::string description = report->GetAsDictionary()
                                ->GetValueForKey("description")
                                ->GetAsString()
                                ->GetValue();
  if (description == "heap-use-after-free") {
    return "Use of deallocated memory detected";
  } else if (description == "heap-buffer-overflow") {
    return "Heap buffer overflow detected";
  } else if (description == "stack-buffer-underflow") {
    return "Stack buffer underflow detected";
  } else if (description == "initialization-order-fiasco") {
    return "Initialization order problem detected";
  } else if (description == "stack-buffer-overflow") {
    return "Stack buffer overflow detected";
  } else if (description == "stack-use-after-return") {
    return "Use of returned stack memory detected";
  } else if (description == "use-after-poison") {
    return "Use of poisoned memory detected";
  } else if (description == "container-overflow") {
    return "Container overflow detected";
  } else if (description == "stack-use-after-scope") {
    return "Use of out-of-scope stack memory detected";
  } else if (description == "global-buffer-overflow") {
    return "Global buffer overflow detected";
  } else if (description == "unknown-crash") {
    return "Invalid memory access detected";
  }

  // for unknown report codes just show the code
  return description;
}

bool AddressSanitizerRuntime::NotifyBreakpointHit(
    void *baton, StoppointCallbackContext *context, user_id_t break_id,
    user_id_t break_loc_id) {
  assert(baton && "null baton");
  if (!baton)
    return false;

  AddressSanitizerRuntime *const instance =
      static_cast<AddressSanitizerRuntime *>(baton);

  StructuredData::ObjectSP report = instance->RetrieveReportData();
  std::string description;
  if (report) {
    description = instance->FormatDescription(report);
  }
  ProcessSP process_sp = instance->GetProcessSP();
  // Make sure this is the right process
  if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) {
    ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
    if (thread_sp)
      thread_sp->SetStopInfo(InstrumentationRuntimeStopInfo::
                                 CreateStopReasonWithInstrumentationData(
                                     *thread_sp, description, report));

    StreamFileSP stream_sp(
        process_sp->GetTarget().GetDebugger().GetOutputFile());
    if (stream_sp) {
      stream_sp->Printf("AddressSanitizer report breakpoint hit. Use 'thread "
                        "info -s' to get extended information about the "
                        "report.\n");
    }
    return true; // Return true to stop the target
  } else
    return false; // Let target run
}

void AddressSanitizerRuntime::Activate() {
  if (IsActive())
    return;

  ProcessSP process_sp = GetProcessSP();
  if (!process_sp)
    return;

  ConstString symbol_name("__asan::AsanDie()");
  const Symbol *symbol = GetRuntimeModuleSP()->FindFirstSymbolWithNameAndType(
      symbol_name, eSymbolTypeCode);

  if (symbol == NULL)
    return;

  if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
    return;

  Target &target = process_sp->GetTarget();
  addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);

  if (symbol_address == LLDB_INVALID_ADDRESS)
    return;

  bool internal = true;
  bool hardware = false;
  Breakpoint *breakpoint =
      process_sp->GetTarget()
          .CreateBreakpoint(symbol_address, internal, hardware)
          .get();
  breakpoint->SetCallback(AddressSanitizerRuntime::NotifyBreakpointHit, this,
                          true);
  breakpoint->SetBreakpointKind("address-sanitizer-report");
  SetBreakpointID(breakpoint->GetID());

  StreamFileSP stream_sp(process_sp->GetTarget().GetDebugger().GetOutputFile());
  if (stream_sp) {
    stream_sp->Printf("AddressSanitizer debugger support is active. Memory "
                      "error breakpoint has been installed and you can now use "
                      "the 'memory history' command.\n");
  }

  SetActive(true);
}

void AddressSanitizerRuntime::Deactivate() {
  if (GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
    ProcessSP process_sp = GetProcessSP();
    if (process_sp) {
      process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID());
      SetBreakpointID(LLDB_INVALID_BREAK_ID);
    }
  }
  SetActive(false);
}
<commit_msg>Support more report types in AddressSanitizerRuntime.cpp, re-word existing ones.<commit_after>//===-- AddressSanitizerRuntime.cpp -----------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "AddressSanitizerRuntime.h"

#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginInterface.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/RegularExpression.h"
#include "lldb/Core/Stream.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Expression/UserExpression.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"

#include "llvm/ADT/StringSwitch.h"

using namespace lldb;
using namespace lldb_private;

lldb::InstrumentationRuntimeSP
AddressSanitizerRuntime::CreateInstance(const lldb::ProcessSP &process_sp) {
  return InstrumentationRuntimeSP(new AddressSanitizerRuntime(process_sp));
}

void AddressSanitizerRuntime::Initialize() {
  PluginManager::RegisterPlugin(
      GetPluginNameStatic(), "AddressSanitizer instrumentation runtime plugin.",
      CreateInstance, GetTypeStatic);
}

void AddressSanitizerRuntime::Terminate() {
  PluginManager::UnregisterPlugin(CreateInstance);
}

lldb_private::ConstString AddressSanitizerRuntime::GetPluginNameStatic() {
  return ConstString("AddressSanitizer");
}

lldb::InstrumentationRuntimeType AddressSanitizerRuntime::GetTypeStatic() {
  return eInstrumentationRuntimeTypeAddressSanitizer;
}

AddressSanitizerRuntime::~AddressSanitizerRuntime() { Deactivate(); }

const RegularExpression &
AddressSanitizerRuntime::GetPatternForRuntimeLibrary() {
  // FIXME: This shouldn't include the "dylib" suffix.
  static RegularExpression regex(
      llvm::StringRef("libclang_rt.asan_(.*)_dynamic\\.dylib"));
  return regex;
}

bool AddressSanitizerRuntime::CheckIfRuntimeIsValid(
    const lldb::ModuleSP module_sp) {
  const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(
      ConstString("__asan_get_alloc_stack"), lldb::eSymbolTypeAny);

  return symbol != nullptr;
}

#define RETRIEVE_REPORT_DATA_FUNCTION_TIMEOUT_USEC 2 * 1000 * 1000
const char *address_sanitizer_retrieve_report_data_prefix = R"(
extern "C"
{
int __asan_report_present();
void *__asan_get_report_pc();
void *__asan_get_report_bp();
void *__asan_get_report_sp();
void *__asan_get_report_address();
const char *__asan_get_report_description();
int __asan_get_report_access_type();
size_t __asan_get_report_access_size();
}
)";

const char *address_sanitizer_retrieve_report_data_command = R"(
struct {
    int present;
    int access_type;
    void *pc;
    void *bp;
    void *sp;
    void *address;
    size_t access_size;
    const char *description;
} t;

t.present = __asan_report_present();
t.access_type = __asan_get_report_access_type();
t.pc = __asan_get_report_pc();
t.bp = __asan_get_report_bp();
t.sp = __asan_get_report_sp();
t.address = __asan_get_report_address();
t.access_size = __asan_get_report_access_size();
t.description = __asan_get_report_description();
t
)";

StructuredData::ObjectSP AddressSanitizerRuntime::RetrieveReportData() {
  ProcessSP process_sp = GetProcessSP();
  if (!process_sp)
    return StructuredData::ObjectSP();

  ThreadSP thread_sp =
      process_sp->GetThreadList().GetExpressionExecutionThread();
  StackFrameSP frame_sp = thread_sp->GetSelectedFrame();

  if (!frame_sp)
    return StructuredData::ObjectSP();

  EvaluateExpressionOptions options;
  options.SetUnwindOnError(true);
  options.SetTryAllThreads(true);
  options.SetStopOthers(true);
  options.SetIgnoreBreakpoints(true);
  options.SetTimeoutUsec(RETRIEVE_REPORT_DATA_FUNCTION_TIMEOUT_USEC);
  options.SetPrefix(address_sanitizer_retrieve_report_data_prefix);
  options.SetAutoApplyFixIts(false);
  options.SetLanguage(eLanguageTypeObjC_plus_plus);

  ValueObjectSP return_value_sp;
  ExecutionContext exe_ctx;
  Error eval_error;
  frame_sp->CalculateExecutionContext(exe_ctx);
  ExpressionResults result = UserExpression::Evaluate(
      exe_ctx, options, address_sanitizer_retrieve_report_data_command, "",
      return_value_sp, eval_error);
  if (result != eExpressionCompleted) {
    process_sp->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf(
        "Warning: Cannot evaluate AddressSanitizer expression:\n%s\n",
        eval_error.AsCString());
    return StructuredData::ObjectSP();
  }

  int present = return_value_sp->GetValueForExpressionPath(".present")
                    ->GetValueAsUnsigned(0);
  if (present != 1)
    return StructuredData::ObjectSP();

  addr_t pc =
      return_value_sp->GetValueForExpressionPath(".pc")->GetValueAsUnsigned(0);
  /* commented out because rdar://problem/18533301
  addr_t bp =
  return_value_sp->GetValueForExpressionPath(".bp")->GetValueAsUnsigned(0);
  addr_t sp =
  return_value_sp->GetValueForExpressionPath(".sp")->GetValueAsUnsigned(0);
  */
  addr_t address = return_value_sp->GetValueForExpressionPath(".address")
                       ->GetValueAsUnsigned(0);
  addr_t access_type =
      return_value_sp->GetValueForExpressionPath(".access_type")
          ->GetValueAsUnsigned(0);
  addr_t access_size =
      return_value_sp->GetValueForExpressionPath(".access_size")
          ->GetValueAsUnsigned(0);
  addr_t description_ptr =
      return_value_sp->GetValueForExpressionPath(".description")
          ->GetValueAsUnsigned(0);
  std::string description;
  Error error;
  process_sp->ReadCStringFromMemory(description_ptr, description, error);

  StructuredData::Dictionary *dict = new StructuredData::Dictionary();
  dict->AddStringItem("instrumentation_class", "AddressSanitizer");
  dict->AddStringItem("stop_type", "fatal_error");
  dict->AddIntegerItem("pc", pc);
  /* commented out because rdar://problem/18533301
  dict->AddIntegerItem("bp", bp);
  dict->AddIntegerItem("sp", sp);
  */
  dict->AddIntegerItem("address", address);
  dict->AddIntegerItem("access_type", access_type);
  dict->AddIntegerItem("access_size", access_size);
  dict->AddStringItem("description", description);

  return StructuredData::ObjectSP(dict);
}

std::string
AddressSanitizerRuntime::FormatDescription(StructuredData::ObjectSP report) {
  std::string description = report->GetAsDictionary()
                                ->GetValueForKey("description")
                                ->GetAsString()
                                ->GetValue();
  return llvm::StringSwitch<std::string>(description)
      .Case("heap-use-after-free", "Use of deallocated memory")
      .Case("heap-buffer-overflow", "Heap buffer overflow")
      .Case("stack-buffer-underflow", "Stack buffer underflow")
      .Case("initialization-order-fiasco", "Initialization order problem")
      .Case("stack-buffer-overflow", "Stack buffer overflow")
      .Case("stack-use-after-return", "Use of stack memory after return")
      .Case("use-after-poison", "Use of poisoned memory")
      .Case("container-overflow", "Container overflow")
      .Case("stack-use-after-scope", "Use of out-of-scope stack memory")
      .Case("global-buffer-overflow", "Global buffer overflow")
      .Case("unknown-crash", "Invalid memory access")
      .Case("stack-overflow", "Stack space exhausted")
      .Case("null-deref", "Dereference of null pointer")
      .Case("wild-jump", "Jump to non-executable address")
      .Case("wild-addr-write", "Write through wild pointer")
      .Case("wild-addr-read", "Read from wild pointer")
      .Case("wild-addr", "Access through wild pointer")
      .Case("signal", "Deadly signal")
      .Case("double-free", "Deallocation of freed memory")
      .Case("new-delete-type-mismatch",
            "Deallocation size different from allocation size")
      .Case("bad-free", "Deallocation of non-allocated memory")
      .Case("alloc-dealloc-mismatch",
            "Mismatch between allocation and deallocation APIs")
      .Case("bad-malloc_usable_size", "Invalid argument to malloc_usable_size")
      .Case("bad-__sanitizer_get_allocated_size",
            "Invalid argument to __sanitizer_get_allocated_size")
      .Case("param-overlap",
            "Call to function disallowing overlapping memory ranges")
      .Case("negative-size-param", "Negative size used when accessing memory")
      .Case("bad-__sanitizer_annotate_contiguous_container",
            "Invalid argument to __sanitizer_annotate_contiguous_container")
      .Case("odr-violation", "Symbol defined in multiple translation units")
      .Case(
          "invalid-pointer-pair",
          "Comparison or arithmetic on pointers from different memory regions")
      // for unknown report codes just show the code
      .Default("AddressSanitizer detected: " + description);
}

bool AddressSanitizerRuntime::NotifyBreakpointHit(
    void *baton, StoppointCallbackContext *context, user_id_t break_id,
    user_id_t break_loc_id) {
  assert(baton && "null baton");
  if (!baton)
    return false;

  AddressSanitizerRuntime *const instance =
      static_cast<AddressSanitizerRuntime *>(baton);

  StructuredData::ObjectSP report = instance->RetrieveReportData();
  std::string description;
  if (report) {
    description = instance->FormatDescription(report);
  }
  ProcessSP process_sp = instance->GetProcessSP();
  // Make sure this is the right process
  if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) {
    ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
    if (thread_sp)
      thread_sp->SetStopInfo(InstrumentationRuntimeStopInfo::
                                 CreateStopReasonWithInstrumentationData(
                                     *thread_sp, description, report));

    StreamFileSP stream_sp(
        process_sp->GetTarget().GetDebugger().GetOutputFile());
    if (stream_sp) {
      stream_sp->Printf("AddressSanitizer report breakpoint hit. Use 'thread "
                        "info -s' to get extended information about the "
                        "report.\n");
    }
    return true; // Return true to stop the target
  } else
    return false; // Let target run
}

void AddressSanitizerRuntime::Activate() {
  if (IsActive())
    return;

  ProcessSP process_sp = GetProcessSP();
  if (!process_sp)
    return;

  ConstString symbol_name("__asan::AsanDie()");
  const Symbol *symbol = GetRuntimeModuleSP()->FindFirstSymbolWithNameAndType(
      symbol_name, eSymbolTypeCode);

  if (symbol == NULL)
    return;

  if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
    return;

  Target &target = process_sp->GetTarget();
  addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);

  if (symbol_address == LLDB_INVALID_ADDRESS)
    return;

  bool internal = true;
  bool hardware = false;
  Breakpoint *breakpoint =
      process_sp->GetTarget()
          .CreateBreakpoint(symbol_address, internal, hardware)
          .get();
  breakpoint->SetCallback(AddressSanitizerRuntime::NotifyBreakpointHit, this,
                          true);
  breakpoint->SetBreakpointKind("address-sanitizer-report");
  SetBreakpointID(breakpoint->GetID());

  StreamFileSP stream_sp(process_sp->GetTarget().GetDebugger().GetOutputFile());
  if (stream_sp) {
    stream_sp->Printf("AddressSanitizer debugger support is active. Memory "
                      "error breakpoint has been installed and you can now use "
                      "the 'memory history' command.\n");
  }

  SetActive(true);
}

void AddressSanitizerRuntime::Deactivate() {
  if (GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
    ProcessSP process_sp = GetProcessSP();
    if (process_sp) {
      process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID());
      SetBreakpointID(LLDB_INVALID_BREAK_ID);
    }
  }
  SetActive(false);
}
<|endoftext|>


<commit_before>/*******************************************************************************
 *  Copyright 2012-2019 Esri
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 ******************************************************************************/

#include "ArKitPointCloudRenderer.h"
#include "ArKitWrapper.h"
#include <QOpenGLShaderProgram>

using namespace Esri::ArcGISRuntime::Toolkit::Internal;

ArKitPointCloudRenderer::ArKitPointCloudRenderer(ArKitWrapper* ArKitWrapper) :
  m_arKitWrapper(ArKitWrapper)
{
}

ArKitPointCloudRenderer::~ArKitPointCloudRenderer()
{
}

void ArKitPointCloudRenderer::initGL()
{
  initializeOpenGLFunctions();

  m_program.reset(new QOpenGLShaderProgram());
  m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex,
                                              "uniform mat4 u_modelViewProjection;"
                                              "uniform vec4 u_color;"
                                              "uniform float u_pointSize;"
                                              "attribute vec4 a_position;"
                                              "varying vec4 v_color;"
                                              "void main() {"
                                              "  v_color = u_color;"
                                              "  vec4 position = u_modelViewProjection * vec4(a_position.xyz, 1.0);"
                                              "  gl_Position = vec4(position.x, -position.y, position.z, position.w);"
                                              "  gl_PointSize = u_pointSize;"
                                              "}");
  m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment,
                                              "precision mediump float;"
                                              "varying vec4 v_color;"
                                              "void main() {"
                                              "    gl_FragColor = v_color;"
                                              "}");

  m_program->link();
  m_program->bind();

  m_uniformModelViewProjection = m_program->uniformLocation("u_modelViewProjection");
  m_uniformColor = m_program->uniformLocation("u_color");
  m_uniformPointSize = m_program->uniformLocation("u_pointSize");
  m_attributeVertices = m_program->attributeLocation("a_position");

  m_program->release();
}

void ArKitPointCloudRenderer::render()
{
  Q_CHECK_PTR(m_arKitWrapper);

  const std::vector<float> points = m_arKitWrapper->pointCloudData();
  if (points.empty())
    return;

  m_program->bind();

  glUniformMatrix4fv(m_uniformModelViewProjection, 1, GL_FALSE, m_arKitWrapper->viewProjectionMatrix().data());
  glEnableVertexAttribArray(m_attributeVertices);
  glVertexAttribPointer(m_attributeVertices, 3, GL_FLOAT, GL_FALSE, 0, points.data());

  // Set cyan color to the point cloud.
  glUniform4f(m_uniformColor, m_pointCloudColor.redF(), m_pointCloudColor.greenF(), m_pointCloudColor.blueF(),
              m_pointCloudColor.alphaF());
  glUniform1f(m_uniformPointSize, static_cast<float>(m_pointCloudSize));

  glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(points.size()));

  m_program->release();
}

// properties for debug mode
QColor ArKitPointCloudRenderer::pointCloudColor() const
{
  return m_pointCloudColor;
}

void ArKitPointCloudRenderer::setPointCloudColor(const QColor& pointCloudColor)
{
  m_pointCloudColor = pointCloudColor;
}

int ArKitPointCloudRenderer::pointCloudSize() const
{
  return m_pointCloudSize;
}

void ArKitPointCloudRenderer::setPointCloudSize(int pointCloudSize)
{
  m_pointCloudSize = pointCloudSize;
}
<commit_msg>remove implementation for ArKitPointCloudRenderer<commit_after>/*******************************************************************************
 *  Copyright 2012-2019 Esri
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 ******************************************************************************/

#include "ArKitPointCloudRenderer.h"
#include "ArKitWrapper.h"
#include <QOpenGLShaderProgram>

using namespace Esri::ArcGISRuntime::Toolkit::Internal;

ArKitPointCloudRenderer::ArKitPointCloudRenderer(ArKitWrapper* ArKitWrapper) :
  m_arKitWrapper(ArKitWrapper)
{
}

ArKitPointCloudRenderer::~ArKitPointCloudRenderer()
{
}

void ArKitPointCloudRenderer::initGL()
{
  initializeOpenGLFunctions();
  // not implemented
}

void ArKitPointCloudRenderer::render()
{
  Q_CHECK_PTR(m_arKitWrapper);
  // not implemented
}

// properties for debug mode
QColor ArKitPointCloudRenderer::pointCloudColor() const
{
  return m_pointCloudColor;
}

void ArKitPointCloudRenderer::setPointCloudColor(const QColor& pointCloudColor)
{
  m_pointCloudColor = pointCloudColor;
}

int ArKitPointCloudRenderer::pointCloudSize() const
{
  return m_pointCloudSize;
}

void ArKitPointCloudRenderer::setPointCloudSize(int pointCloudSize)
{
  m_pointCloudSize = pointCloudSize;
}
<|endoftext|>


<commit_before>#include <cstdio>
#include <poll.h>
#include <unistd.h>
#include <algorithm>

#include <kms++/kms++.h>
#include <kms++util/kms++util.h>
#include <kms++util/videodevice.h>

#define CAMERA_BUF_QUEUE_SIZE 5

using namespace std;
using namespace kms;

static vector<DumbFramebuffer*> s_fbs;
static vector<DumbFramebuffer*> s_free_fbs;
static vector<DumbFramebuffer*> s_wb_fbs;
static vector<DumbFramebuffer*> s_ready_fbs;

class WBStreamer
{
public:
	WBStreamer(VideoStreamer* streamer, Crtc* crtc, uint32_t width, uint32_t height, PixelFormat pixfmt)
		: m_capdev(*streamer)
	{
		m_capdev.set_port(crtc->idx());
		m_capdev.set_format(pixfmt, width, height);
		m_capdev.set_queue_size(s_fbs.size());

		for (auto fb : s_free_fbs) {
			m_capdev.queue(fb);
			s_wb_fbs.push_back(fb);
		}

		s_free_fbs.clear();
	}

	~WBStreamer()
	{
	}

	WBStreamer(const WBStreamer& other) = delete;
	WBStreamer& operator=(const WBStreamer& other) = delete;

	int fd() const { return m_capdev.fd(); }

	void start_streaming()
	{
		m_capdev.stream_on();
	}

	void stop_streaming()
	{
		m_capdev.stream_off();
	}

	void Dequeue()
	{
		auto fb = m_capdev.dequeue();

		auto iter = find(s_wb_fbs.begin(), s_wb_fbs.end(), fb);
		s_wb_fbs.erase(iter);

		s_ready_fbs.insert(s_ready_fbs.begin(), fb);
	}

	void Queue()
	{
		if (s_free_fbs.size() == 0)
			return;

		auto fb = s_free_fbs.back();
		s_free_fbs.pop_back();

		m_capdev.queue(fb);

		s_wb_fbs.insert(s_wb_fbs.begin(), fb);
	}

private:
	VideoStreamer& m_capdev;
};

class WBFlipState : private PageFlipHandlerBase
{
public:
	WBFlipState(Card& card, Crtc* crtc, Plane* plane)
		: m_card(card), m_crtc(crtc), m_plane(plane)
	{
	}

	void setup(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
	{
		auto fb = s_ready_fbs.back();
		s_ready_fbs.pop_back();

		AtomicReq req(m_card);

		req.add(m_plane, "CRTC_ID", m_crtc->id());
		req.add(m_plane, "FB_ID", fb->id());

		req.add(m_plane, "CRTC_X", x);
		req.add(m_plane, "CRTC_Y", y);
		req.add(m_plane, "CRTC_W", width);
		req.add(m_plane, "CRTC_H", height);

		req.add(m_plane, "SRC_X", 0);
		req.add(m_plane, "SRC_Y", 0);
		req.add(m_plane, "SRC_W", fb->width() << 16);
		req.add(m_plane, "SRC_H", fb->height() << 16);

		int r = req.commit_sync();
		FAIL_IF(r, "initial plane setup failed");

		m_current_fb = fb;
	}

	void queue_next()
	{
		if (m_queued_fb)
			return;

		if (s_ready_fbs.size() == 0)
			return;

		auto fb = s_ready_fbs.back();
		s_ready_fbs.pop_back();

		AtomicReq req(m_card);
		req.add(m_plane, "FB_ID", fb->id());

		int r = req.commit(this);
		if (r)
			EXIT("Flip commit failed: %d\n", r);

		m_queued_fb = fb;
	}

private:
	void handle_page_flip(uint32_t frame, double time)
	{
		if (m_queued_fb) {
			if (m_current_fb)
				s_free_fbs.insert(s_free_fbs.begin(), m_current_fb);

			m_current_fb = m_queued_fb;
			m_queued_fb = nullptr;
		}

		queue_next();
	}

	Card& m_card;
	Crtc* m_crtc;
	Plane* m_plane;

	DumbFramebuffer* m_current_fb = nullptr;
	DumbFramebuffer* m_queued_fb = nullptr;
};

class BarFlipState : private PageFlipHandlerBase
{
public:
	BarFlipState(Card& card, Crtc* crtc)
		: m_card(card), m_crtc(crtc)
	{
		m_plane = m_crtc->get_primary_plane();

		uint32_t w = m_crtc->mode().hdisplay;
		uint32_t h = m_crtc->mode().vdisplay;

		for (unsigned i = 0; i < s_num_buffers; ++i)
			m_fbs[i] = new DumbFramebuffer(card, w, h, PixelFormat::XRGB8888);
	}

	~BarFlipState()
	{
		for (unsigned i = 0; i < s_num_buffers; ++i)
			delete m_fbs[i];
	}

	void start_flipping()
	{
		m_frame_num = 0;
		queue_next();
	}

private:
	void handle_page_flip(uint32_t frame, double time)
	{
		m_frame_num++;
		queue_next();
	}

	static unsigned get_bar_pos(DumbFramebuffer* fb, unsigned frame_num)
	{
		return (frame_num * bar_speed) % (fb->width() - bar_width + 1);
	}

	void draw_bar(DumbFramebuffer* fb, unsigned frame_num)
	{
		int old_xpos = frame_num < s_num_buffers ? -1 : get_bar_pos(fb, frame_num - s_num_buffers);
		int new_xpos = get_bar_pos(fb, frame_num);

		draw_color_bar(*fb, old_xpos, new_xpos, bar_width);
		draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255));
	}

	void queue_next()
	{
		AtomicReq req(m_card);

		unsigned cur = m_frame_num % s_num_buffers;

		auto fb = m_fbs[cur];

		draw_bar(fb, m_frame_num);

		req.add(m_plane, {
				{ "FB_ID", fb->id() },
			});

		int r = req.commit(this);
		if (r)
			EXIT("Flip commit failed: %d\n", r);
	}

	static const unsigned s_num_buffers = 3;

	DumbFramebuffer* m_fbs[s_num_buffers];

	Card& m_card;
	Crtc* m_crtc;
	Plane* m_plane;

	unsigned m_frame_num;

	static const unsigned bar_width = 20;
	static const unsigned bar_speed = 8;
};

static const char* usage_str =
		"Usage: wbcap [OPTIONS]\n\n"
		"Options:\n"
		"  -s, --src=CONN            Source connector\n"
		"  -d, --dst=CONN            Destination connector\n"
		"  -f, --format=4CC          Format"
		"  -h, --help                Print this help\n"
		;

int main(int argc, char** argv)
{
	string src_conn_name = "unknown";
	string dst_conn_name = "hdmi";
	PixelFormat pixfmt = PixelFormat::XRGB8888;

	OptionSet optionset = {
		Option("s|src=", [&](string s)
		{
			src_conn_name = s;
		}),
		Option("d|dst=", [&](string s)
		{
			dst_conn_name = s;
		}),
		Option("f|format=", [&](string s)
		{
			pixfmt = FourCCToPixelFormat(s);
		}),
		Option("h|help", [&]()
		{
			puts(usage_str);
			exit(-1);
		}),
	};

	optionset.parse(argc, argv);

	if (optionset.params().size() > 0) {
		puts(usage_str);
		exit(-1);
	}

	VideoDevice vid("/dev/video11");

	Card card;
	ResourceManager resman(card);

	auto src_conn = resman.reserve_connector(src_conn_name);
	auto src_crtc = resman.reserve_crtc(src_conn);
	src_crtc->refresh();
	Videomode src_mode = src_conn->get_default_mode();
	DumbFramebuffer src_fb(card, src_mode.hdisplay, src_mode.vdisplay, PixelFormat::ARGB8888);
	src_crtc->set_mode(src_conn, src_fb, src_mode);

	uint32_t src_width = src_crtc->mode().hdisplay;
	uint32_t src_height = src_crtc->mode().vdisplay;

	printf("src %s, crtc %ux%u\n", src_conn->fullname().c_str(), src_width, src_height);

	auto dst_conn = resman.reserve_connector(dst_conn_name);
	auto dst_crtc = resman.reserve_crtc(dst_conn);
	Videomode dst_mode = dst_conn->get_default_mode();
	DumbFramebuffer dst_fb(card, dst_mode.hdisplay, dst_mode.vdisplay, PixelFormat::ARGB8888);
	dst_crtc->set_mode(dst_conn, dst_fb, dst_mode);
	dst_crtc->refresh();
	auto dst_plane = resman.reserve_overlay_plane(dst_crtc, pixfmt);
	FAIL_IF(!dst_plane, "Plane not found");

	uint32_t dst_width = min((uint32_t)dst_crtc->mode().hdisplay, src_width);
	uint32_t dst_height = min((uint32_t)dst_crtc->mode().vdisplay, src_height);

	printf("dst %s, crtc %ux%u, plane %ux%u\n", dst_conn->fullname().c_str(),
	       dst_crtc->mode().hdisplay, dst_crtc->mode().vdisplay,
	       dst_width, dst_height);

	for (int i = 0; i < CAMERA_BUF_QUEUE_SIZE; ++i) {
		auto fb = new DumbFramebuffer(card, src_width, src_height, pixfmt);
		s_fbs.push_back(fb);
		s_free_fbs.push_back(fb);
	}

	// get one fb for initial setup
	s_ready_fbs.push_back(s_free_fbs.back());
	s_free_fbs.pop_back();

	// This draws a moving bar to SRC display
	BarFlipState barflipper(card, src_crtc);
	barflipper.start_flipping();

	// This shows the captures SRC frames on DST display
	WBFlipState wbflipper(card, dst_crtc, dst_plane);
	wbflipper.setup(0, 0, dst_width, dst_height);

	WBStreamer wb(vid.get_capture_streamer(), src_crtc, src_width, src_height, pixfmt);
	wb.start_streaming();

	vector<pollfd> fds(3);

	fds[0].fd = 0;
	fds[0].events =  POLLIN;
	fds[1].fd = wb.fd();
	fds[1].events =  POLLIN;
	fds[2].fd = card.fd();
	fds[2].events =  POLLIN;

	while (true) {
		int r = poll(fds.data(), fds.size(), -1);
		ASSERT(r > 0);

		if (fds[0].revents != 0)
			break;

		if (fds[1].revents) {
			fds[1].revents = 0;

			wb.Dequeue();
			wbflipper.queue_next();
		}

		if (fds[2].revents) {
			fds[2].revents = 0;

			card.call_page_flip_handlers();
			wb.Queue();
		}
	}

	printf("exiting...\n");
}
<commit_msg>utils: improve wbcap<commit_after>#include <cstdio>
#include <poll.h>
#include <unistd.h>
#include <algorithm>

#include <kms++/kms++.h>
#include <kms++util/kms++util.h>
#include <kms++util/videodevice.h>

#define CAMERA_BUF_QUEUE_SIZE 5

using namespace std;
using namespace kms;

static vector<DumbFramebuffer*> s_fbs;
static vector<DumbFramebuffer*> s_free_fbs;
static vector<DumbFramebuffer*> s_wb_fbs;
static vector<DumbFramebuffer*> s_ready_fbs;

class WBStreamer
{
public:
	WBStreamer(VideoStreamer* streamer, Crtc* crtc, PixelFormat pixfmt)
		: m_capdev(*streamer)
	{
		m_capdev.set_port(crtc->idx());
		m_capdev.set_format(pixfmt, crtc->mode().hdisplay, crtc->mode().vdisplay);
		m_capdev.set_queue_size(s_fbs.size());

		for (auto fb : s_free_fbs) {
			m_capdev.queue(fb);
			s_wb_fbs.push_back(fb);
		}

		s_free_fbs.clear();
	}

	~WBStreamer()
	{
	}

	WBStreamer(const WBStreamer& other) = delete;
	WBStreamer& operator=(const WBStreamer& other) = delete;

	int fd() const { return m_capdev.fd(); }

	void start_streaming()
	{
		m_capdev.stream_on();
	}

	void stop_streaming()
	{
		m_capdev.stream_off();
	}

	void Dequeue()
	{
		auto fb = m_capdev.dequeue();

		auto iter = find(s_wb_fbs.begin(), s_wb_fbs.end(), fb);
		s_wb_fbs.erase(iter);

		s_ready_fbs.insert(s_ready_fbs.begin(), fb);
	}

	void Queue()
	{
		if (s_free_fbs.size() == 0)
			return;

		auto fb = s_free_fbs.back();
		s_free_fbs.pop_back();

		m_capdev.queue(fb);

		s_wb_fbs.insert(s_wb_fbs.begin(), fb);
	}

private:
	VideoStreamer& m_capdev;
};

class WBFlipState : private PageFlipHandlerBase
{
public:
	WBFlipState(Card& card, Crtc* crtc, Plane* plane)
		: m_card(card), m_crtc(crtc), m_plane(plane)
	{
	}

	void setup(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
	{
		auto fb = s_ready_fbs.back();
		s_ready_fbs.pop_back();

		AtomicReq req(m_card);

		req.add(m_plane, "CRTC_ID", m_crtc->id());
		req.add(m_plane, "FB_ID", fb->id());

		req.add(m_plane, "CRTC_X", x);
		req.add(m_plane, "CRTC_Y", y);
		req.add(m_plane, "CRTC_W", min((uint32_t)m_crtc->mode().hdisplay, fb->width()));
		req.add(m_plane, "CRTC_H", min((uint32_t)m_crtc->mode().vdisplay, fb->height()));

		req.add(m_plane, "SRC_X", 0);
		req.add(m_plane, "SRC_Y", 0);
		req.add(m_plane, "SRC_W", fb->width() << 16);
		req.add(m_plane, "SRC_H", fb->height() << 16);

		int r = req.commit_sync();
		FAIL_IF(r, "initial plane setup failed");

		m_current_fb = fb;
	}

	void queue_next()
	{
		if (m_queued_fb)
			return;

		if (s_ready_fbs.size() == 0)
			return;

		auto fb = s_ready_fbs.back();
		s_ready_fbs.pop_back();

		AtomicReq req(m_card);
		req.add(m_plane, "FB_ID", fb->id());

		int r = req.commit(this);
		if (r)
			EXIT("Flip commit failed: %d\n", r);

		m_queued_fb = fb;
	}

private:
	void handle_page_flip(uint32_t frame, double time)
	{
		if (m_queued_fb) {
			if (m_current_fb)
				s_free_fbs.insert(s_free_fbs.begin(), m_current_fb);

			m_current_fb = m_queued_fb;
			m_queued_fb = nullptr;
		}

		queue_next();
	}

	Card& m_card;
	Crtc* m_crtc;
	Plane* m_plane;

	DumbFramebuffer* m_current_fb = nullptr;
	DumbFramebuffer* m_queued_fb = nullptr;
};

class BarFlipState : private PageFlipHandlerBase
{
public:
	BarFlipState(Card& card, Crtc* crtc, Plane* plane, uint32_t width, uint32_t height)
		: m_card(card), m_crtc(crtc), m_plane(plane)
	{
		for (unsigned i = 0; i < s_num_buffers; ++i)
			m_fbs[i] = new DumbFramebuffer(card, width, height, PixelFormat::XRGB8888);
	}

	~BarFlipState()
	{
		for (unsigned i = 0; i < s_num_buffers; ++i)
			delete m_fbs[i];
	}

	void start_flipping()
	{
		m_frame_num = 0;
		queue_next();
	}

private:
	void handle_page_flip(uint32_t frame, double time)
	{
		m_frame_num++;
		queue_next();
	}

	static unsigned get_bar_pos(DumbFramebuffer* fb, unsigned frame_num)
	{
		return (frame_num * bar_speed) % (fb->width() - bar_width + 1);
	}

	void draw_bar(DumbFramebuffer* fb, unsigned frame_num)
	{
		int old_xpos = frame_num < s_num_buffers ? -1 : get_bar_pos(fb, frame_num - s_num_buffers);
		int new_xpos = get_bar_pos(fb, frame_num);

		draw_color_bar(*fb, old_xpos, new_xpos, bar_width);
		draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255));
	}

	void queue_next()
	{
		AtomicReq req(m_card);

		unsigned cur = m_frame_num % s_num_buffers;

		auto fb = m_fbs[cur];

		draw_bar(fb, m_frame_num);

		req.add(m_plane, {
				{ "CRTC_ID", m_crtc->id() },
				{ "FB_ID", fb->id() },

				{ "CRTC_X", 0 },
				{ "CRTC_Y", 0 },
				{ "CRTC_W", min((uint32_t)m_crtc->mode().hdisplay, fb->width()) },
				{ "CRTC_H", min((uint32_t)m_crtc->mode().vdisplay, fb->height()) },

				{ "SRC_X", 0 },
				{ "SRC_Y", 0 },
				{ "SRC_W", fb->width() << 16 },
				{ "SRC_H", fb->height() << 16 },
			});

		int r = req.commit(this);
		if (r)
			EXIT("Flip commit failed: %d\n", r);
	}

	static const unsigned s_num_buffers = 3;

	DumbFramebuffer* m_fbs[s_num_buffers];

	Card& m_card;
	Crtc* m_crtc;
	Plane* m_plane;

	unsigned m_frame_num;

	static const unsigned bar_width = 20;
	static const unsigned bar_speed = 8;
};

static const char* usage_str =
		"Usage: wbcap [OPTIONS]\n\n"
		"Options:\n"
		"  -s, --src=CONN            Source connector\n"
		"  -d, --dst=CONN            Destination connector\n"
		"  -f, --format=4CC          Format"
		"  -h, --help                Print this help\n"
		;

int main(int argc, char** argv)
{
	string src_conn_name;
	string src_mode_name;
	string dst_conn_name;
	string dst_mode_name;
	PixelFormat pixfmt = PixelFormat::XRGB8888;

	OptionSet optionset = {
		Option("s|src=", [&](string s)
		{
			src_conn_name = s;
		}),
		Option("m|smode=", [&](string s)
		{
			src_mode_name = s;
		}),
		Option("d|dst=", [&](string s)
		{
			dst_conn_name = s;
		}),
		Option("M|dmode=", [&](string s)
		{
			dst_mode_name = s;
		}),
		Option("f|format=", [&](string s)
		{
			pixfmt = FourCCToPixelFormat(s);
		}),
		Option("h|help", [&]()
		{
			puts(usage_str);
			exit(-1);
		}),
	};

	optionset.parse(argc, argv);

	if (optionset.params().size() > 0) {
		puts(usage_str);
		exit(-1);
	}

	if (src_conn_name.empty())
		EXIT("No source connector defined");

	if (dst_conn_name.empty())
		EXIT("No destination connector defined");

	VideoDevice vid("/dev/video11");

	Card card;
	ResourceManager resman(card);

	card.disable_all();

	auto src_conn = resman.reserve_connector(src_conn_name);
	auto src_crtc = resman.reserve_crtc(src_conn);
	auto src_plane = resman.reserve_generic_plane(src_crtc, pixfmt);
	FAIL_IF(!src_plane, "Plane not found");
	Videomode src_mode = src_mode_name.empty() ? src_conn->get_default_mode() : src_conn->get_mode(src_mode_name);
	src_crtc->set_mode(src_conn, src_mode);

	auto dst_conn = resman.reserve_connector(dst_conn_name);
	auto dst_crtc = resman.reserve_crtc(dst_conn);
	auto dst_plane = resman.reserve_overlay_plane(dst_crtc, pixfmt);
	FAIL_IF(!dst_plane, "Plane not found");
	Videomode dst_mode = dst_mode_name.empty() ? dst_conn->get_default_mode() : dst_conn->get_mode(dst_mode_name);
	dst_crtc->set_mode(dst_conn, dst_mode);

	uint32_t width = src_mode.hdisplay;
	uint32_t height = src_mode.vdisplay;

	printf("src %s, crtc %s\n", src_conn->fullname().c_str(), src_mode.to_string().c_str());

	printf("dst %s, crtc %s\n", dst_conn->fullname().c_str(), dst_mode.to_string().c_str());

	printf("fb %ux%u\n", width, height);

	for (int i = 0; i < CAMERA_BUF_QUEUE_SIZE; ++i) {
		auto fb = new DumbFramebuffer(card, width, height, pixfmt);
		s_fbs.push_back(fb);
		s_free_fbs.push_back(fb);
	}

	// get one fb for initial setup
	s_ready_fbs.push_back(s_free_fbs.back());
	s_free_fbs.pop_back();

	// This draws a moving bar to SRC display
	BarFlipState barflipper(card, src_crtc, src_plane, width, height);
	barflipper.start_flipping();

	// This shows the captures SRC frames on DST display
	WBFlipState wbflipper(card, dst_crtc, dst_plane);
	wbflipper.setup(0, 0, width, height);

	WBStreamer wb(vid.get_capture_streamer(), src_crtc, pixfmt);
	wb.start_streaming();

	vector<pollfd> fds(3);

	fds[0].fd = 0;
	fds[0].events =  POLLIN;
	fds[1].fd = wb.fd();
	fds[1].events =  POLLIN;
	fds[2].fd = card.fd();
	fds[2].events =  POLLIN;

	while (true) {
		int r = poll(fds.data(), fds.size(), -1);
		ASSERT(r > 0);

		if (fds[0].revents != 0)
			break;

		if (fds[1].revents) {
			fds[1].revents = 0;

			wb.Dequeue();
			wbflipper.queue_next();
		}

		if (fds[2].revents) {
			fds[2].revents = 0;

			card.call_page_flip_handlers();
			wb.Queue();
		}
	}

	printf("exiting...\n");
}
<|endoftext|>


<commit_before>#include "bct.h"
#include <cmath>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>

/*
 * Returns a copy of the given matrix with each element inverted.
 */
gsl_matrix* bct::invert_elements(const gsl_matrix* m) {
	return pow_elements(m, -1.0);
}

/*
 * Returns a copy of the given matrix with no loops.
 */
gsl_matrix* bct::remove_loops(const gsl_matrix* m) {
	gsl_matrix* nl_m = copy(m);
	gsl_vector_view diag_nl_m = gsl_matrix_diagonal(nl_m);
	gsl_vector_set_zero(&diag_nl_m.vector);
	return nl_m;
}

/*
 * Returns a binary copy of the given matrix.
 */
gsl_matrix* bct::to_binary(const gsl_matrix* m) {
	return compare_elements(m, fp_not_equal, 0.0);
}

/*
 * Returns a positive copy of the given matrix.
 */
gsl_matrix* bct::to_positive(const gsl_matrix* m) {
	gsl_matrix* pos_m = gsl_matrix_alloc(m->size1, m->size2);
	for (int i = 0; i < (int)m->size1; i++) {
		for (int j = 0; j < (int)m->size2; j++) {
			gsl_matrix_set(pos_m, i, j, std::abs(gsl_matrix_get(m, i, j)));
		}
	}
	return pos_m;
}

/*
 * Returns an undirected copy of the given matrix.  For every pair of nodes:
 * - If m(i, j) and m(j, i) are the same, no change is made.
 * - If m(i, j) is zero and m(j, i) is nonzero, m(i, j) is set to m(j, i).
 * - If m(i, j) is nonzero and m(j, i) is zero, m(j, i) is set to m(i, j).
 * - If m(i, j) and m(j, i) are different nonzero values, they are both set to
 *   the average of these values.
 */
gsl_matrix* bct::to_undirected(const gsl_matrix* m) {
	if (m->size1 != m->size2) throw size_exception();
	gsl_matrix* und_m = copy(m);
	for (int i = 0; i < (int)m->size1; i++) {
		for (int j = i; j < (int)m->size2; j++) {
			double value_ij = gsl_matrix_get(m, i, j);
			double value_ji = gsl_matrix_get(m, j, i);
			if (fp_zero(value_ij) && fp_nonzero(value_ji)) {
				gsl_matrix_set(und_m, i, j, value_ji);
			} else if (fp_nonzero(value_ij) && fp_zero(value_ji)) {
				gsl_matrix_set(und_m, j, i, value_ij);
			} else if (fp_not_equal(value_ij, value_ji)) {
				double average = (value_ij + value_ji) / 2.0;
				gsl_matrix_set(und_m, i, j, average);
				gsl_matrix_set(und_m, j, i, average);
			}
		}
	}
	return und_m;
}
<commit_msg>Only invert nonzero elements in bct::invert_elements.<commit_after>#include "bct.h"
#include <cmath>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>

/*
 * Returns a copy of the given matrix with each nonzero element inverted.
 */
gsl_matrix* bct::invert_elements(const gsl_matrix* m) {
	gsl_matrix* inv_m = gsl_matrix_alloc(m->size1, m->size2);
	for (int i = 0; i < (int)m->size1; i++) {
		for (int j = 0; j < (int)m->size2; j++) {
			double value = gsl_matrix_get(m, i, j);
			if (fp_nonzero(value)) {
				gsl_matrix_set(inv_m, i, j, 1.0 / value);
			} else {
				gsl_matrix_set(inv_m, i, j, 0.0);
			}
		}
	}
	return inv_m;
}

/*
 * Returns a copy of the given matrix with no loops.
 */
gsl_matrix* bct::remove_loops(const gsl_matrix* m) {
	gsl_matrix* nl_m = copy(m);
	gsl_vector_view diag_nl_m = gsl_matrix_diagonal(nl_m);
	gsl_vector_set_zero(&diag_nl_m.vector);
	return nl_m;
}

/*
 * Returns a binary copy of the given matrix.
 */
gsl_matrix* bct::to_binary(const gsl_matrix* m) {
	return compare_elements(m, fp_not_equal, 0.0);
}

/*
 * Returns a positive copy of the given matrix.
 */
gsl_matrix* bct::to_positive(const gsl_matrix* m) {
	gsl_matrix* pos_m = gsl_matrix_alloc(m->size1, m->size2);
	for (int i = 0; i < (int)m->size1; i++) {
		for (int j = 0; j < (int)m->size2; j++) {
			gsl_matrix_set(pos_m, i, j, std::abs(gsl_matrix_get(m, i, j)));
		}
	}
	return pos_m;
}

/*
 * Returns an undirected copy of the given matrix.  For every pair of nodes:
 * - If m(i, j) and m(j, i) are the same, no change is made.
 * - If m(i, j) is zero and m(j, i) is nonzero, m(i, j) is set to m(j, i).
 * - If m(i, j) is nonzero and m(j, i) is zero, m(j, i) is set to m(i, j).
 * - If m(i, j) and m(j, i) are different nonzero values, they are both set to
 *   the average of these values.
 */
gsl_matrix* bct::to_undirected(const gsl_matrix* m) {
	if (m->size1 != m->size2) throw size_exception();
	gsl_matrix* und_m = copy(m);
	for (int i = 0; i < (int)m->size1; i++) {
		for (int j = i; j < (int)m->size2; j++) {
			double value_ij = gsl_matrix_get(m, i, j);
			double value_ji = gsl_matrix_get(m, j, i);
			if (fp_zero(value_ij) && fp_nonzero(value_ji)) {
				gsl_matrix_set(und_m, i, j, value_ji);
			} else if (fp_nonzero(value_ij) && fp_zero(value_ji)) {
				gsl_matrix_set(und_m, j, i, value_ij);
			} else if (fp_not_equal(value_ij, value_ji)) {
				double average = (value_ij + value_ji) / 2.0;
				gsl_matrix_set(und_m, i, j, average);
				gsl_matrix_set(und_m, j, i, average);
			}
		}
	}
	return und_m;
}
<|endoftext|>


<commit_before>// This file is part of gltfpack; see gltfpack.h for version/license details
#include "gltfpack.h"

#include <algorithm>

#include <math.h>
#include <string.h>

static float getDelta(const Attr& l, const Attr& r, cgltf_animation_path_type type)
{
	switch (type)
	{
	case cgltf_animation_path_type_translation:
		return std::max(std::max(fabsf(l.f[0] - r.f[0]), fabsf(l.f[1] - r.f[1])), fabsf(l.f[2] - r.f[2]));

	case cgltf_animation_path_type_rotation:
		return acosf(std::min(1.f, fabsf(l.f[0] * r.f[0] + l.f[1] * r.f[1] + l.f[2] * r.f[2] + l.f[3] * r.f[3])));

	case cgltf_animation_path_type_scale:
		return std::max(std::max(fabsf(l.f[0] / r.f[0] - 1), fabsf(l.f[1] / r.f[1] - 1)), fabsf(l.f[2] / r.f[2] - 1));

	case cgltf_animation_path_type_weights:
		return fabsf(l.f[0] - r.f[0]);

	default:
		assert(!"Uknown animation path");
		return 0;
	}
}

static float getDeltaTolerance(cgltf_animation_path_type type)
{
	switch (type)
	{
	case cgltf_animation_path_type_translation:
		return 0.001f; // linear

	case cgltf_animation_path_type_rotation:
		return 0.001f; // radians

	case cgltf_animation_path_type_scale:
		return 0.001f; // ratio

	case cgltf_animation_path_type_weights:
		return 0.001f; // linear

	default:
		assert(!"Uknown animation path");
		return 0;
	}
}

static Attr interpolateLinear(const Attr& l, const Attr& r, float t, cgltf_animation_path_type type)
{
	if (type == cgltf_animation_path_type_rotation)
	{
		// Approximating slerp, https://zeux.io/2015/07/23/approximating-slerp/
		// We also handle quaternion double-cover
		float ca = l.f[0] * r.f[0] + l.f[1] * r.f[1] + l.f[2] * r.f[2] + l.f[3] * r.f[3];

		float d = fabsf(ca);
		float A = 1.0904f + d * (-3.2452f + d * (3.55645f - d * 1.43519f));
		float B = 0.848013f + d * (-1.06021f + d * 0.215638f);
		float k = A * (t - 0.5f) * (t - 0.5f) + B;
		float ot = t + t * (t - 0.5f) * (t - 1) * k;

		float t0 = 1 - ot;
		float t1 = ca > 0 ? ot : -ot;

		Attr lerp = {{
		    l.f[0] * t0 + r.f[0] * t1,
		    l.f[1] * t0 + r.f[1] * t1,
		    l.f[2] * t0 + r.f[2] * t1,
		    l.f[3] * t0 + r.f[3] * t1,
		}};

		float len = sqrtf(lerp.f[0] * lerp.f[0] + lerp.f[1] * lerp.f[1] + lerp.f[2] * lerp.f[2] + lerp.f[3] * lerp.f[3]);

		if (len > 0.f)
		{
			lerp.f[0] /= len;
			lerp.f[1] /= len;
			lerp.f[2] /= len;
			lerp.f[3] /= len;
		}

		return lerp;
	}
	else
	{
		Attr lerp = {{
		    l.f[0] * (1 - t) + r.f[0] * t,
		    l.f[1] * (1 - t) + r.f[1] * t,
		    l.f[2] * (1 - t) + r.f[2] * t,
		    l.f[3] * (1 - t) + r.f[3] * t,
		}};

		return lerp;
	}
}

static Attr interpolateHermite(const Attr& v0, const Attr& t0, const Attr& v1, const Attr& t1, float t, float dt, cgltf_animation_path_type type)
{
	float s0 = 1 + t * t * (2 * t - 3);
	float s1 = t + t * t * (t - 2);
	float s2 = 1 - s0;
	float s3 = t * t * (t - 1);

	float ts1 = dt * s1;
	float ts3 = dt * s3;

	Attr lerp = {{
	    s0 * v0.f[0] + ts1 * t0.f[0] + s2 * v1.f[0] + ts3 * t1.f[0],
	    s0 * v0.f[1] + ts1 * t0.f[1] + s2 * v1.f[1] + ts3 * t1.f[1],
	    s0 * v0.f[2] + ts1 * t0.f[2] + s2 * v1.f[2] + ts3 * t1.f[2],
	    s0 * v0.f[3] + ts1 * t0.f[3] + s2 * v1.f[3] + ts3 * t1.f[3],
	}};

	if (type == cgltf_animation_path_type_rotation)
	{
		float len = sqrtf(lerp.f[0] * lerp.f[0] + lerp.f[1] * lerp.f[1] + lerp.f[2] * lerp.f[2] + lerp.f[3] * lerp.f[3]);

		if (len > 0.f)
		{
			lerp.f[0] /= len;
			lerp.f[1] /= len;
			lerp.f[2] /= len;
			lerp.f[3] /= len;
		}
	}

	return lerp;
}

static void resampleKeyframes(std::vector<Attr>& data, const std::vector<float>& input, const std::vector<Attr>& output, cgltf_animation_path_type type, cgltf_interpolation_type interpolation, size_t components, int frames, float mint, int freq)
{
	size_t cursor = 0;

	for (int i = 0; i < frames; ++i)
	{
		float time = mint + float(i) / freq;

		while (cursor + 1 < input.size())
		{
			float next_time = input[cursor + 1];

			if (next_time > time)
				break;

			cursor++;
		}

		if (cursor + 1 < input.size())
		{
			float cursor_time = input[cursor + 0];
			float next_time = input[cursor + 1];

			float range = next_time - cursor_time;
			float inv_range = (range == 0.f) ? 0.f : 1.f / (next_time - cursor_time);
			float t = std::max(0.f, std::min(1.f, (time - cursor_time) * inv_range));

			for (size_t j = 0; j < components; ++j)
			{
				switch (interpolation)
				{
				case cgltf_interpolation_type_linear:
				{
					const Attr& v0 = output[(cursor + 0) * components + j];
					const Attr& v1 = output[(cursor + 1) * components + j];
					data.push_back(interpolateLinear(v0, v1, t, type));
				}
				break;

				case cgltf_interpolation_type_step:
				{
					const Attr& v = output[cursor * components + j];
					data.push_back(v);
				}
				break;

				case cgltf_interpolation_type_cubic_spline:
				{
					const Attr& v0 = output[(cursor * 3 + 1) * components + j];
					const Attr& b0 = output[(cursor * 3 + 2) * components + j];
					const Attr& a1 = output[(cursor * 3 + 3) * components + j];
					const Attr& v1 = output[(cursor * 3 + 4) * components + j];
					data.push_back(interpolateHermite(v0, b0, v1, a1, t, range, type));
				}
				break;

				default:
					assert(!"Unknown interpolation type");
				}
			}
		}
		else
		{
			size_t offset = (interpolation == cgltf_interpolation_type_cubic_spline) ? cursor * 3 + 1 : cursor;

			for (size_t j = 0; j < components; ++j)
			{
				const Attr& v = output[offset * components + j];
				data.push_back(v);
			}
		}
	}
}

static bool isTrackEqual(const std::vector<Attr>& data, cgltf_animation_path_type type, int frames, const Attr* value, size_t components)
{
	assert(data.size() == frames * components);

	float tolerance = getDeltaTolerance(type);

	for (int i = 0; i < frames; ++i)
	{
		for (size_t j = 0; j < components; ++j)
		{
			float delta = getDelta(value[j], data[i * components + j], type);

			if (delta > tolerance)
				return false;
		}
	}

	return true;
}

static void getBaseTransform(Attr* result, size_t components, cgltf_animation_path_type type, cgltf_node* node)
{
	switch (type)
	{
	case cgltf_animation_path_type_translation:
		memcpy(result->f, node->translation, 3 * sizeof(float));
		break;

	case cgltf_animation_path_type_rotation:
		memcpy(result->f, node->rotation, 4 * sizeof(float));
		break;

	case cgltf_animation_path_type_scale:
		memcpy(result->f, node->scale, 3 * sizeof(float));
		break;

	case cgltf_animation_path_type_weights:
		if (node->weights_count)
		{
			assert(node->weights_count == components);
			memcpy(result->f, node->weights, components * sizeof(float));
		}
		else if (node->mesh && node->mesh->weights_count)
		{
			assert(node->mesh->weights_count == components);
			memcpy(result->f, node->mesh->weights, components * sizeof(float));
		}
		break;

	default:
		assert(!"Unknown animation path");
	}
}

void processAnimation(Animation& animation, const Settings& settings)
{
	float mint = 0, maxt = 0;

	for (size_t i = 0; i < animation.tracks.size(); ++i)
	{
		const Track& track = animation.tracks[i];
		assert(!track.time.empty());

		mint = std::min(mint, track.time.front());
		maxt = std::max(maxt, track.time.back());
	}

	// round the number of frames to nearest but favor the "up" direction
	// this means that at 10 Hz resampling, we will try to preserve the last frame <10ms
	// but if the last frame is <2ms we favor just removing this data
	int frames = 1 + int((maxt - mint) * settings.anim_freq + 0.8f);

	animation.start = mint;
	animation.frames = frames;

	std::vector<Attr> base;

	for (size_t i = 0; i < animation.tracks.size(); ++i)
	{
		Track& track = animation.tracks[i];

		std::vector<Attr> result;
		resampleKeyframes(result, track.time, track.data, track.path, track.interpolation, track.components, frames, mint, settings.anim_freq);

		track.time.clear();
		track.data.swap(result);

		if (isTrackEqual(track.data, track.path, frames, &track.data[0], track.components))
		{
			// track is constant (equal to first keyframe), we only need the first keyframe
			track.data.resize(track.components);

			// track.dummy is true iff track redundantly sets up the value to be equal to default node transform
			base.resize(track.components);
			getBaseTransform(&base[0], track.components, track.path, track.node);

			track.dummy = isTrackEqual(track.data, track.path, 1, &base[0], track.components);
		}
	}
}
<commit_msg>gltfpack: Fix output animation start time<commit_after>// This file is part of gltfpack; see gltfpack.h for version/license details
#include "gltfpack.h"

#include <algorithm>

#include <float.h>
#include <math.h>
#include <string.h>

static float getDelta(const Attr& l, const Attr& r, cgltf_animation_path_type type)
{
	switch (type)
	{
	case cgltf_animation_path_type_translation:
		return std::max(std::max(fabsf(l.f[0] - r.f[0]), fabsf(l.f[1] - r.f[1])), fabsf(l.f[2] - r.f[2]));

	case cgltf_animation_path_type_rotation:
		return acosf(std::min(1.f, fabsf(l.f[0] * r.f[0] + l.f[1] * r.f[1] + l.f[2] * r.f[2] + l.f[3] * r.f[3])));

	case cgltf_animation_path_type_scale:
		return std::max(std::max(fabsf(l.f[0] / r.f[0] - 1), fabsf(l.f[1] / r.f[1] - 1)), fabsf(l.f[2] / r.f[2] - 1));

	case cgltf_animation_path_type_weights:
		return fabsf(l.f[0] - r.f[0]);

	default:
		assert(!"Uknown animation path");
		return 0;
	}
}

static float getDeltaTolerance(cgltf_animation_path_type type)
{
	switch (type)
	{
	case cgltf_animation_path_type_translation:
		return 0.001f; // linear

	case cgltf_animation_path_type_rotation:
		return 0.001f; // radians

	case cgltf_animation_path_type_scale:
		return 0.001f; // ratio

	case cgltf_animation_path_type_weights:
		return 0.001f; // linear

	default:
		assert(!"Uknown animation path");
		return 0;
	}
}

static Attr interpolateLinear(const Attr& l, const Attr& r, float t, cgltf_animation_path_type type)
{
	if (type == cgltf_animation_path_type_rotation)
	{
		// Approximating slerp, https://zeux.io/2015/07/23/approximating-slerp/
		// We also handle quaternion double-cover
		float ca = l.f[0] * r.f[0] + l.f[1] * r.f[1] + l.f[2] * r.f[2] + l.f[3] * r.f[3];

		float d = fabsf(ca);
		float A = 1.0904f + d * (-3.2452f + d * (3.55645f - d * 1.43519f));
		float B = 0.848013f + d * (-1.06021f + d * 0.215638f);
		float k = A * (t - 0.5f) * (t - 0.5f) + B;
		float ot = t + t * (t - 0.5f) * (t - 1) * k;

		float t0 = 1 - ot;
		float t1 = ca > 0 ? ot : -ot;

		Attr lerp = {{
		    l.f[0] * t0 + r.f[0] * t1,
		    l.f[1] * t0 + r.f[1] * t1,
		    l.f[2] * t0 + r.f[2] * t1,
		    l.f[3] * t0 + r.f[3] * t1,
		}};

		float len = sqrtf(lerp.f[0] * lerp.f[0] + lerp.f[1] * lerp.f[1] + lerp.f[2] * lerp.f[2] + lerp.f[3] * lerp.f[3]);

		if (len > 0.f)
		{
			lerp.f[0] /= len;
			lerp.f[1] /= len;
			lerp.f[2] /= len;
			lerp.f[3] /= len;
		}

		return lerp;
	}
	else
	{
		Attr lerp = {{
		    l.f[0] * (1 - t) + r.f[0] * t,
		    l.f[1] * (1 - t) + r.f[1] * t,
		    l.f[2] * (1 - t) + r.f[2] * t,
		    l.f[3] * (1 - t) + r.f[3] * t,
		}};

		return lerp;
	}
}

static Attr interpolateHermite(const Attr& v0, const Attr& t0, const Attr& v1, const Attr& t1, float t, float dt, cgltf_animation_path_type type)
{
	float s0 = 1 + t * t * (2 * t - 3);
	float s1 = t + t * t * (t - 2);
	float s2 = 1 - s0;
	float s3 = t * t * (t - 1);

	float ts1 = dt * s1;
	float ts3 = dt * s3;

	Attr lerp = {{
	    s0 * v0.f[0] + ts1 * t0.f[0] + s2 * v1.f[0] + ts3 * t1.f[0],
	    s0 * v0.f[1] + ts1 * t0.f[1] + s2 * v1.f[1] + ts3 * t1.f[1],
	    s0 * v0.f[2] + ts1 * t0.f[2] + s2 * v1.f[2] + ts3 * t1.f[2],
	    s0 * v0.f[3] + ts1 * t0.f[3] + s2 * v1.f[3] + ts3 * t1.f[3],
	}};

	if (type == cgltf_animation_path_type_rotation)
	{
		float len = sqrtf(lerp.f[0] * lerp.f[0] + lerp.f[1] * lerp.f[1] + lerp.f[2] * lerp.f[2] + lerp.f[3] * lerp.f[3]);

		if (len > 0.f)
		{
			lerp.f[0] /= len;
			lerp.f[1] /= len;
			lerp.f[2] /= len;
			lerp.f[3] /= len;
		}
	}

	return lerp;
}

static void resampleKeyframes(std::vector<Attr>& data, const std::vector<float>& input, const std::vector<Attr>& output, cgltf_animation_path_type type, cgltf_interpolation_type interpolation, size_t components, int frames, float mint, int freq)
{
	size_t cursor = 0;

	for (int i = 0; i < frames; ++i)
	{
		float time = mint + float(i) / freq;

		while (cursor + 1 < input.size())
		{
			float next_time = input[cursor + 1];

			if (next_time > time)
				break;

			cursor++;
		}

		if (cursor + 1 < input.size())
		{
			float cursor_time = input[cursor + 0];
			float next_time = input[cursor + 1];

			float range = next_time - cursor_time;
			float inv_range = (range == 0.f) ? 0.f : 1.f / (next_time - cursor_time);
			float t = std::max(0.f, std::min(1.f, (time - cursor_time) * inv_range));

			for (size_t j = 0; j < components; ++j)
			{
				switch (interpolation)
				{
				case cgltf_interpolation_type_linear:
				{
					const Attr& v0 = output[(cursor + 0) * components + j];
					const Attr& v1 = output[(cursor + 1) * components + j];
					data.push_back(interpolateLinear(v0, v1, t, type));
				}
				break;

				case cgltf_interpolation_type_step:
				{
					const Attr& v = output[cursor * components + j];
					data.push_back(v);
				}
				break;

				case cgltf_interpolation_type_cubic_spline:
				{
					const Attr& v0 = output[(cursor * 3 + 1) * components + j];
					const Attr& b0 = output[(cursor * 3 + 2) * components + j];
					const Attr& a1 = output[(cursor * 3 + 3) * components + j];
					const Attr& v1 = output[(cursor * 3 + 4) * components + j];
					data.push_back(interpolateHermite(v0, b0, v1, a1, t, range, type));
				}
				break;

				default:
					assert(!"Unknown interpolation type");
				}
			}
		}
		else
		{
			size_t offset = (interpolation == cgltf_interpolation_type_cubic_spline) ? cursor * 3 + 1 : cursor;

			for (size_t j = 0; j < components; ++j)
			{
				const Attr& v = output[offset * components + j];
				data.push_back(v);
			}
		}
	}
}

static bool isTrackEqual(const std::vector<Attr>& data, cgltf_animation_path_type type, int frames, const Attr* value, size_t components)
{
	assert(data.size() == frames * components);

	float tolerance = getDeltaTolerance(type);

	for (int i = 0; i < frames; ++i)
	{
		for (size_t j = 0; j < components; ++j)
		{
			float delta = getDelta(value[j], data[i * components + j], type);

			if (delta > tolerance)
				return false;
		}
	}

	return true;
}

static void getBaseTransform(Attr* result, size_t components, cgltf_animation_path_type type, cgltf_node* node)
{
	switch (type)
	{
	case cgltf_animation_path_type_translation:
		memcpy(result->f, node->translation, 3 * sizeof(float));
		break;

	case cgltf_animation_path_type_rotation:
		memcpy(result->f, node->rotation, 4 * sizeof(float));
		break;

	case cgltf_animation_path_type_scale:
		memcpy(result->f, node->scale, 3 * sizeof(float));
		break;

	case cgltf_animation_path_type_weights:
		if (node->weights_count)
		{
			assert(node->weights_count == components);
			memcpy(result->f, node->weights, components * sizeof(float));
		}
		else if (node->mesh && node->mesh->weights_count)
		{
			assert(node->mesh->weights_count == components);
			memcpy(result->f, node->mesh->weights, components * sizeof(float));
		}
		break;

	default:
		assert(!"Unknown animation path");
	}
}

void processAnimation(Animation& animation, const Settings& settings)
{
	float mint = FLT_MAX, maxt = 0;

	for (size_t i = 0; i < animation.tracks.size(); ++i)
	{
		const Track& track = animation.tracks[i];
		assert(!track.time.empty());

		mint = std::min(mint, track.time.front());
		maxt = std::max(maxt, track.time.back());
	}

	mint = std::min(mint, maxt);

	// round the number of frames to nearest but favor the "up" direction
	// this means that at 10 Hz resampling, we will try to preserve the last frame <10ms
	// but if the last frame is <2ms we favor just removing this data
	int frames = 1 + int((maxt - mint) * settings.anim_freq + 0.8f);

	animation.start = mint;
	animation.frames = frames;

	std::vector<Attr> base;

	for (size_t i = 0; i < animation.tracks.size(); ++i)
	{
		Track& track = animation.tracks[i];

		std::vector<Attr> result;
		resampleKeyframes(result, track.time, track.data, track.path, track.interpolation, track.components, frames, mint, settings.anim_freq);

		track.time.clear();
		track.data.swap(result);

		if (isTrackEqual(track.data, track.path, frames, &track.data[0], track.components))
		{
			// track is constant (equal to first keyframe), we only need the first keyframe
			track.data.resize(track.components);

			// track.dummy is true iff track redundantly sets up the value to be equal to default node transform
			base.resize(track.components);
			getBaseTransform(&base[0], track.components, track.path, track.node);

			track.dummy = isTrackEqual(track.data, track.path, 1, &base[0], track.components);
		}
	}
}
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2013 Aldebaran Robotics
 */

#include <string>

#include <boost/program_options.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>

#include <qi/log.hpp>
#include <qi/detail/log.hxx>

#include <qi/applicationsession.hpp>
#include <qicore/logmessage.hpp>
#include <qicore/logmanager.hpp>
#include <qicore/loglistener.hpp>

#include "qicli.hpp"

#define foreach BOOST_FOREACH

qiLogCategory("qicli.qilog");

static void onMessage(const qi::LogMessage& msg)
{
  std::stringstream ss;
  ss << qi::log::logLevelToString(static_cast<qi::LogLevel>(msg.level))
      << " " << msg.category
      << " " << msg.source
      << " " << msg.message;
  std::cout << ss.str() << std::endl;
}

static void setFilter(const std::string& rules, qi::LogListenerPtr listener)
{
  std::string cat;
  qi::LogLevel level;
  for (auto &&p: qi::log::detail::parseFilterRules(rules))
  {
    std::tie(cat, level) = std::move(p);
    listener->addFilter(cat, level);
  }
}

int subCmd_logView(int argc, char **argv, qi::ApplicationSession& app)
{
  po::options_description   desc("Usage: qicli log-view");

  desc.add_options()
      ("help,h", "Print this help message and exit")
      ("verbose,v", "Set maximum logs verbosity shown to verbose.")
      ("debug,d", "Set maximum logs verbosity shown to debug.")
      ("level,l", po::value<int>()->default_value(4), "Change the log minimum level: [0-6] (default:4). This option accepts the same arguments' format than --qi-log-level.")
      ("filters,f", po::value<std::string>(), "Set log filtering options. This option accepts the same arguments' format than --qi-log-filters.")
      ;

  po::variables_map vm;
  if (!poDefault(po::command_line_parser(argc, argv).options(desc), vm, desc))
    return 1;

  qiLogVerbose() << "Connecting to service directory";
  app.startSession();
  qi::SessionPtr s = app.session();

  qiLogVerbose() << "Resolving services";

  app.loadModule("qicore");
  qi::LogManagerPtr logger = app.session()->service("LogManager");
  qi::LogListenerPtr listener = logger->createListener();
  listener->clearFilters();

  if (vm.count("level"))
  {
    int level = vm["level"].as<int>();

    if (level > 6)
      level =  6;
    else if (level <= 0)
      level = 0;

    listener->addFilter("*", static_cast<qi::LogLevel>(level));
  }

  if (vm.count("verbose"))
    listener->addFilter("*", qi::LogLevel_Verbose);

  if (vm.count("debug"))
    listener->addFilter("*", qi::LogLevel_Debug);

  if (vm.count("filters"))
  {
    std::string filters = vm["filters"].as<std::string>();
    setFilter(filters, listener);
  }

  listener->onLogMessage.connect(&onMessage);
  app.run();

  return 0;
}

int subCmd_logSend(int argc, char **argv, qi::ApplicationSession& app)
{
  po::options_description   desc("Usage: qicli log-send <message>");

  desc.add_options()
      ("help,h", "Print this help message and exit")
      ("verbose,v", "Set sent message verbosity to verbose.")
      ("debug,d", "Set sent message verbosity to debug.")
      ("level,l", po::value<int>()->default_value(4), "Change the log minimum level: [0-6] (default:4). This option accepts the same arguments' format than --qi-log-level.")
      ("category,c", po::value<std::string>(), "Message's category (default: \"qicli.qilog.logsend\").")
      ("message,m", po::value<std::string>(), "Message to send.")
      ;

  po::positional_options_description positionalOptions;
  positionalOptions.add("message", -1);

  po::variables_map vm;
  if (!poDefault(po::command_line_parser(argc, argv)
                 .options(desc).positional(positionalOptions), vm, desc))
    return 1;

  qiLogVerbose() << "Connecting to service directory";
  app.startSession();
  qi::SessionPtr s = app.session();

  qiLogVerbose() << "Resolving services";

  qi::AnyObject logger = s->service("LogManager");

  qi::os::timeval tv(qi::SystemClock::now());

  std::string source(__FILE__);
  source += ':';
  source += __FUNCTION__;
  source += ':';
  source += boost::lexical_cast<std::string>(__LINE__);

  int level = 4;
  if (vm.count("level"))
  {
    level = vm["level"].as<int>();

    if (level > 6)
      level =  6;
    else if (level <= 0)
      level = 0;
  }
  if (vm.count("verbose"))
    level = 5;

  if (vm.count("debug"))
    level = 6;

  std::string category = "qicli.qilog.logsend";
  if (vm.count("category"))
    category = vm["category"].as<std::string>();

  std::string location = qi::os::getMachineId() + ":" + boost::lexical_cast<std::string>(qi::os::getpid());;
  std::string message = "";
  if (vm.count("message"))
    message = vm["message"].as<std::string>();

  // timestamp
  qi::AnyReferenceVector timeVectRef;
  timeVectRef.push_back(qi::AnyReference::from(tv.tv_sec));
  timeVectRef.push_back(qi::AnyReference::from(tv.tv_usec));
  qi::AnyValue timeVal = qi::AnyValue::makeTuple(timeVectRef);

  qi::AnyReferenceVector msgVectRef;
  msgVectRef.push_back(qi::AnyReference::from(source));
  msgVectRef.push_back(qi::AnyReference::from(level));
  msgVectRef.push_back(timeVal.asReference()); //timestamp
  msgVectRef.push_back(qi::AnyReference::from(category));
  msgVectRef.push_back(qi::AnyReference::from(location));
  msgVectRef.push_back(qi::AnyReference::from(message));
  msgVectRef.push_back(qi::AnyReference::from(0));

  std::vector<qi::AnyValue> msgs;
  msgs.push_back(qi::AnyValue::makeTuple(msgVectRef));
  logger.call<void>("log", qi::AnyValue::from(msgs));

  logger.reset();

  return 0;
}
<commit_msg>Rewrite qicli log-send using qicore type<commit_after>/**
 * Copyright (C) 2013 Aldebaran Robotics
 */

#include <string>

#include <boost/program_options.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>

#include <qi/log.hpp>
#include <qi/detail/log.hxx>

#include <qi/applicationsession.hpp>
#include <qi/anymodule.hpp>
#include <qicore/logmessage.hpp>
#include <qicore/logmanager.hpp>
#include <qicore/loglistener.hpp>

#include "qicli.hpp"

#define foreach BOOST_FOREACH

qiLogCategory("qicli.qilog");

static void onMessage(const qi::LogMessage& msg)
{
  std::stringstream ss;
  ss << qi::log::logLevelToString(static_cast<qi::LogLevel>(msg.level))
     << " " << msg.category
     << " " << msg.source
     << " " << msg.message;
  std::cout << ss.str() << std::endl;
}

static void setFilter(const std::string& rules, qi::LogListenerPtr listener)
{
  std::string cat;
  qi::LogLevel level;
  for (auto &&p: qi::log::detail::parseFilterRules(rules))
  {
    std::tie(cat, level) = std::move(p);
    listener->addFilter(cat, level);
  }
}

int subCmd_logView(int argc, char **argv, qi::ApplicationSession& app)
{
  po::options_description   desc("Usage: qicli log-view");

  desc.add_options()
      ("help,h", "Print this help message and exit")
      ("verbose,v", "Set maximum logs verbosity shown to verbose.")
      ("debug,d", "Set maximum logs verbosity shown to debug.")
      ("level,l", po::value<int>()->default_value(4), "Change the log minimum level: [0-6] (default:4). This option accepts the same arguments' format than --qi-log-level.")
      ("filters,f", po::value<std::string>(), "Set log filtering options. This option accepts the same arguments' format than --qi-log-filters.")
      ;

  po::variables_map vm;
  if (!poDefault(po::command_line_parser(argc, argv).options(desc), vm, desc))
    return 1;

  qiLogVerbose() << "Connecting to service directory";
  app.startSession();
  qi::SessionPtr s = app.session();

  qiLogVerbose() << "Resolving services";

  app.loadModule("qicore");
  qi::LogManagerPtr logger = app.session()->service("LogManager");
  qi::LogListenerPtr listener = logger->createListener();
  listener->clearFilters();

  if (vm.count("level"))
  {
    int level = vm["level"].as<int>();

    if (level > 6)
      level =  6;
    else if (level <= 0)
      level = 0;

    listener->addFilter("*", static_cast<qi::LogLevel>(level));
  }

  if (vm.count("verbose"))
    listener->addFilter("*", qi::LogLevel_Verbose);

  if (vm.count("debug"))
    listener->addFilter("*", qi::LogLevel_Debug);

  if (vm.count("filters"))
  {
    std::string filters = vm["filters"].as<std::string>();
    setFilter(filters, listener);
  }

  listener->onLogMessage.connect(&onMessage);
  app.run();

  return 0;
}

int subCmd_logSend(int argc, char **argv, qi::ApplicationSession& app)
{
  po::options_description   desc("Usage: qicli log-send <message>");

  desc.add_options()
      ("help,h", "Print this help message and exit")
      ("verbose,v", "Set sent message verbosity to verbose.")
      ("debug,d", "Set sent message verbosity to debug.")
      ("level,l", po::value<int>()->default_value(4), "Change the log minimum level: [0-6] (default:4). This option accepts the same arguments' format than --qi-log-level.")
      ("category,c", po::value<std::string>(), "Message's category (default: \"qicli.qilog.logsend\").")
      ("message,m", po::value<std::string>(), "Message to send.")
      ;

  po::positional_options_description positionalOptions;
  positionalOptions.add("message", -1);

  po::variables_map vm;
  if (!poDefault(po::command_line_parser(argc, argv)
                 .options(desc).positional(positionalOptions), vm, desc))
    return 1;

  qiLogVerbose() << "Connecting to service directory";
  app.startSession();
  qi::SessionPtr s = app.session();

  qiLogVerbose() << "Resolving services";

  // import module
  qi::AnyModule mod = qi::import("qicore");

  // get service Logger
  qi::LogManagerPtr logger = app.session()->service("LogManager");

  qi::LogMessage msg;

  msg.source = __FILE__;
  msg.source += ':';
  msg.source += __FUNCTION__;
  msg.source += ':';
  msg.source += boost::lexical_cast<std::string>(__LINE__);

  msg.level = qi::LogLevel_Info;
  if (vm.count("level"))
  {
    int level = vm["level"].as<int>();

    if (level > 6)
      level = qi::LogLevel_Debug;
    else if (level <= 0)
      level = qi::LogLevel_Silent;

    msg.level = static_cast<qi::LogLevel>(level);
  }
  if (vm.count("verbose"))
    msg.level = qi::LogLevel_Verbose;

  if (vm.count("debug"))
    msg.level = qi::LogLevel_Debug;

  msg.category = "qicli.qilog.logsend";
  if (vm.count("category"))
    msg.category = vm["category"].as<std::string>();

  msg.location = qi::os::getMachineId() + ":" + boost::lexical_cast<std::string>(qi::os::getpid());

  if (vm.count("message"))
    msg.message = vm["message"].as<std::string>();

  msg.date = qi::Clock::now();
  msg.systemDate = qi::SystemClock::now();

  std::vector<qi::LogMessage> msgs;
  msgs.push_back(msg);
  logger->log(msgs);

  logger.reset();

  return 0;
}
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
// This source file is part of Hect.
//
// Copyright (c) 2015 Colin Hill
//
// 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 "DebugSystem.h"

#include "Hect/Interface/Widgets/CheckBox.h"
#include "Hect/Interface/Widgets/Grid.h"
#include "Hect/Interface/Widgets/Label.h"
#include "Hect/Logic/Systems/RenderSystem.h"
#include "Hect/Runtime/Engine.h"

using namespace hect;

DebugSystem::DebugSystem(Engine& engine, Scene& scene) :
    System(engine, scene, SystemTickStage::Precedent),
    _renderer(engine.renderer()),
    _window(engine.window()),
    _interfaceSystem(scene.system<InterfaceSystem>())
{
}

void DebugSystem::renderBox(DebugColor color, const Box& box, const Vector3& position, const Quaternion& rotation)
{
    _boxes.emplace_back(box, position, rotation, color);
}

void DebugSystem::addRenderCalls(RenderSystem& renderSystem)
{
    for (DebugBox& box : _boxes)
    {
        renderSystem.addRenderCall(box.transform, *boxMesh, _coloredMaterials[static_cast<size_t>(box.color)]);
    }
}

void DebugSystem::toggleShowInterface()
{
    if (_systemPanel)
    {
        bool visible = _systemPanel->isVisible();
        _systemPanel->setVisible(!visible);
    }
}

void DebugSystem::initialize()
{
    addColoredMaterial(Color(100, 0, 0)); // Primary
    addColoredMaterial(Color(0, 100, 0)); // Secondary
    addColoredMaterial(Color(0, 0, 100)); // Tertiary

    _renderer.uploadShader(*coloredLineShader);
    _renderer.uploadMesh(*boxMesh);

    if (_interfaceSystem)
    {
        createSystemPanel();
    }
}

void DebugSystem::tick(double timeStep)
{
    (void)timeStep;
    _boxes.clear();
}

DebugSystem::DebugBox::DebugBox()
{
}

DebugSystem::DebugBox::DebugBox(const Box& box, const Vector3& position, const Quaternion& rotation, DebugColor color) :
    box(box),
    color(color)
{
    transform.globalPosition = position;
    transform.globalScale = box.scale();
    transform.globalRotation = rotation;
}

void DebugSystem::addColoredMaterial(const Color& color)
{
    // Create a material for this color
    Material material;
    material.setShader(coloredLineShader);
    material.setUniformValue("color", color);

    _coloredMaterials.push_back(material);
}

void DebugSystem::createSystemPanel()
{
    const double checkBoxColumnWidth = 16;
    const double rowHeight = 18;
    const double panelMargin = 10;

    Grid::Handle grid = _interfaceSystem->addWidget<Grid>();
    grid->setHorizontalAlign(HorizontalAlign::Center);
    grid->setVerticalAlign(VerticalAlign::Center);

    Grid::ColumnId checkBoxColumnId = grid->addColumn(checkBoxColumnWidth);
    Grid::ColumnId systemNameColumnId = grid->addColumn();

    double maxSystemNameLength = 0;

    SystemRegistry::SystemTypeIdSequence typeIds = SystemRegistry::typeIds();
    for (SystemTypeId typeId : typeIds)
    {
        Grid::RowId rowId = grid->addRow(rowHeight);

        const std::string& systemName = SystemRegistry::typeNameOf(typeId);

        CheckBox::Handle checkBox = _interfaceSystem->addWidget<CheckBox>();
        checkBox->setHorizontalAlign(HorizontalAlign::Center);
        checkBox->setVerticalAlign(VerticalAlign::Center);
        checkBox->setPressAction([this, typeId, checkBox]
        {
            SystemBase& system = scene().systemOfTypeId(typeId);
            system.setDebugEnabled(checkBox->isChecked());
        });
        grid->setCell(checkBoxColumnId, rowId, checkBox);

        Label::Handle label = _interfaceSystem->addWidget<Label>();
        label->setText(systemName);
        label->setHorizontalAlign(HorizontalAlign::Left);
        label->setVerticalAlign(VerticalAlign::Bottom);
        grid->setCell(systemNameColumnId, rowId, label);

        maxSystemNameLength = std::max(maxSystemNameLength, label->dimensions().x);
    }

    grid->resizeColumn(systemNameColumnId, maxSystemNameLength);

    _systemPanel = _interfaceSystem->addWidget<Panel>();
    _systemPanel->setDimensions(grid->dimensions() + panelMargin);
    _systemPanel->addChild(grid);

    Vector2 windowDimensions(_window.width(), _window.height());
    Vector2 systemPanelPosition = windowDimensions * 0.5 - _systemPanel->dimensions() * 0.5;
    _systemPanel->setPosition(systemPanelPosition);

    _systemPanel->setVisible(false);
}
<commit_msg>Make systems ordered alphabetically in debug system UI<commit_after>///////////////////////////////////////////////////////////////////////////////
// This source file is part of Hect.
//
// Copyright (c) 2015 Colin Hill
//
// 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 "DebugSystem.h"

#include "Hect/Interface/Widgets/CheckBox.h"
#include "Hect/Interface/Widgets/Grid.h"
#include "Hect/Interface/Widgets/Label.h"
#include "Hect/Logic/Systems/RenderSystem.h"
#include "Hect/Runtime/Engine.h"

using namespace hect;

DebugSystem::DebugSystem(Engine& engine, Scene& scene) :
    System(engine, scene, SystemTickStage::Precedent),
    _renderer(engine.renderer()),
    _window(engine.window()),
    _interfaceSystem(scene.system<InterfaceSystem>())
{
}

void DebugSystem::renderBox(DebugColor color, const Box& box, const Vector3& position, const Quaternion& rotation)
{
    _boxes.emplace_back(box, position, rotation, color);
}

void DebugSystem::addRenderCalls(RenderSystem& renderSystem)
{
    for (DebugBox& box : _boxes)
    {
        renderSystem.addRenderCall(box.transform, *boxMesh, _coloredMaterials[static_cast<size_t>(box.color)]);
    }
}

void DebugSystem::toggleShowInterface()
{
    if (_systemPanel)
    {
        bool visible = _systemPanel->isVisible();
        _systemPanel->setVisible(!visible);
    }
}

void DebugSystem::initialize()
{
    addColoredMaterial(Color(100, 0, 0)); // Primary
    addColoredMaterial(Color(0, 100, 0)); // Secondary
    addColoredMaterial(Color(0, 0, 100)); // Tertiary

    _renderer.uploadShader(*coloredLineShader);
    _renderer.uploadMesh(*boxMesh);

    if (_interfaceSystem)
    {
        createSystemPanel();
    }
}

void DebugSystem::tick(double timeStep)
{
    (void)timeStep;
    _boxes.clear();
}

DebugSystem::DebugBox::DebugBox()
{
}

DebugSystem::DebugBox::DebugBox(const Box& box, const Vector3& position, const Quaternion& rotation, DebugColor color) :
    box(box),
    color(color)
{
    transform.globalPosition = position;
    transform.globalScale = box.scale();
    transform.globalRotation = rotation;
}

void DebugSystem::addColoredMaterial(const Color& color)
{
    Material material;
    material.setShader(coloredLineShader);
    material.setUniformValue("color", color);

    _coloredMaterials.push_back(material);
}

void DebugSystem::createSystemPanel()
{
    const double checkBoxColumnWidth = 16;
    const double rowHeight = 18;
    const double panelMargin = 10;

    Grid::Handle grid = _interfaceSystem->addWidget<Grid>();
    grid->setHorizontalAlign(HorizontalAlign::Center);
    grid->setVerticalAlign(VerticalAlign::Center);

    Grid::ColumnId checkBoxColumnId = grid->addColumn(checkBoxColumnWidth);
    Grid::ColumnId systemNameColumnId = grid->addColumn();

    double maxSystemNameLength = 0;

    SystemRegistry::SystemTypeIdSequence typeIds = SystemRegistry::typeIds();
    std::sort(typeIds.begin(), typeIds.end(), [] (SystemTypeId a, SystemTypeId b)
    {
        return SystemRegistry::typeNameOf(a) < SystemRegistry::typeNameOf(b);
    });

    for (SystemTypeId typeId : typeIds)
    {
        Grid::RowId rowId = grid->addRow(rowHeight);

        const std::string& systemName = SystemRegistry::typeNameOf(typeId);

        CheckBox::Handle checkBox = _interfaceSystem->addWidget<CheckBox>();
        checkBox->setHorizontalAlign(HorizontalAlign::Center);
        checkBox->setVerticalAlign(VerticalAlign::Center);
        checkBox->setPressAction([this, typeId, checkBox]
        {
            SystemBase& system = scene().systemOfTypeId(typeId);
            system.setDebugEnabled(checkBox->isChecked());
        });
        grid->setCell(checkBoxColumnId, rowId, checkBox);

        Label::Handle label = _interfaceSystem->addWidget<Label>();
        label->setText(systemName);
        label->setHorizontalAlign(HorizontalAlign::Left);
        label->setVerticalAlign(VerticalAlign::Bottom);
        grid->setCell(systemNameColumnId, rowId, label);

        maxSystemNameLength = std::max(maxSystemNameLength, label->dimensions().x);
    }

    grid->resizeColumn(systemNameColumnId, maxSystemNameLength);

    _systemPanel = _interfaceSystem->addWidget<Panel>();
    _systemPanel->setDimensions(grid->dimensions() + panelMargin);
    _systemPanel->addChild(grid);

    Vector2 windowDimensions(_window.width(), _window.height());
    Vector2 systemPanelPosition = windowDimensions * 0.5 - _systemPanel->dimensions() * 0.5;
    _systemPanel->setPosition(systemPanelPosition);

    _systemPanel->setVisible(false);
}
<|endoftext|>


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

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt 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 "otbVectorImage.h"
#include "otbImageFileReader.h"
#include "otbImageFileWriter.h"
#include "otbStreamingStatisticsVectorImageFilter.h"
#include "otbEigenvalueLikelihoodMaximisation.h"
#include "otbVcaImageFilter.h"
#include "otbUnConstrainedLeastSquareImageFilter.h"

#include "otbVectorImageToMatrixImageFilter.h"

int main(int argc, char * argv[])
{
  if (argc != 3)
    {
    std::cerr << "Usage: " << argv[0];
    std::cerr << "inputFileName outputFileName " << std::endl;
    return EXIT_FAILURE;
    }

  const char *       infname = argv[1];
  const char *       outfname = argv[2];
  const unsigned int Dimension = 2;

  typedef double                                  PixelType;
  typedef otb::VectorImage<PixelType, Dimension>  ImageType;

  typedef otb::ImageFileReader<ImageType>         ReaderType;
  typedef otb::ImageFileWriter<FloatImageType>    WriterType;

  typedef otb::StreamingStatisticsVectorImageFilter<ImageType> StreamingStatisticsVectorImageFilterType;
  typedef otb::EigenvalueLikelihoodMaximisation<double>        ELMType;
  typedef otb::VCAImageFilter<ImageType>                       VCAFilterType;

  typedef otb::UnConstrainedLeastSquareImageFilter<ImageType,ImageType,double> UCLSUnmixingFilterType;

  typedef otb::VectorImageToMatrixImageFilter<ImageType> VectorImageToMatrixImageFilterType;

  typedef vnl_vector<double> VectorType;
  typedef vnl_matrix<double> MatrixType;

/// / Noise filtering
  //  typedef otb::LocalActivityVectorImageFilter< ImageType, ImageType > NoiseFilterType;

  // // Image filtering
  //  typedef otb::MNFImageFilter< ImageType, ImageType,
  //    NoiseFilterType, otb::Transform::FORWARD > FilterType;

  //read image
  ReaderType::Pointer reader = ReaderType::New();
  reader->SetFileName(infname);
  reader->UpdateOutputInformation();

  std::cout << "Computing stats" << std::endl;

  StreamingStatisticsVectorImageFilterType::Pointer stats = StreamingStatisticsVectorImageFilterType::New();

  stats->SetInput(reader->GetOutput());
  otb::StandardWriterWatcher watcher(stats->GetStreamer(), stats->GetFilter(), "Computing image stats");
  stats->Update();

  VectorType mean (stats->GetMean().GetDataPointer(), stats->GetMean().GetSize());
  MatrixType covariance  = stats->GetCovariance().GetVnlMatrix();
  MatrixType correlation = stats->GetCorrelation().GetVnlMatrix();

  /*
         * Estimate Endmembers Numbers
         */
  std::cout << "Estimate Endmembers Numbers by ELM" << std::endl;

  ELMType::Pointer elm = ELMType::New();
  elm->SetCovariance(covariance);
  elm->SetCorrelation(correlation);
  elm->SetNumberOfPixels(reader->GetOutput()->GetLargestPossibleRegion().GetNumberOfPixels());
  elm->Compute();

  const unsigned int nbEndmembers = elm->GetNumberOfEndmembers();

  std::cout << "ELM : " << nbEndmembers << " estimated endmembers" << std::endl;

  std::cout << "ELM likelihood values: " << elm->GetLikelihood() << std::endl;

/*
       * Estimate Endmembers
       */
  std::cout << "Estimate Endmembers by VCA" << std::endl;

  VCAFilterType::Pointer vca = VCAFilterType::New();
  vca->SetNumberOfEndmembers(nbEndmembers);
  vca->SetInput(reader->GetOutput());

  ImageType::Pointer endmembersImage;
  endmembersImage = vca->GetOutput();

  /*
     * Transform Endmembers image to matrix representation
     */
  std::cout << "Endmembers extracted" << std::endl;
  std::cout << "Converting endmembers to matrix" << std::endl;
  VectorImageToMatrixImageFilterType::Pointer endMember2Matrix = VectorImageToMatrixImageFilterType::New();
  endMember2Matrix->SetInput(endmembersImage);
  endMember2Matrix->Update();

  MatrixType endMembersMatrix = endMember2Matrix->GetMatrix();
  std::cout << "Endmembers matrix : " << endMembersMatrix << std::endl;

  /*
     * Unmix
     */
  UCLSUnmixingFilterType::Pointer unmixer =
    UCLSUnmixingFilterType::New();

  unmixer->SetInput(reader->GetOutput());
  unmixer->SetMatrix(endMembersMatrix);
  unmixer->SetNumberOfThreads(1); // FIXME : currently buggy

  ImageType::Pointer abundanceMap;
  abundanceMap = unmixer->GetOutput();

  /*
  * Write abundance map
  */
  std::cout << "Write abundance map " << outfname << std::endl;

  WriterType::Pointer  writer = WriterType::New();
  writer->SetInput(abundanceMap);
  writer->SetFileName(outfname);

  writer->Update();

  return EXIT_SUCCESS;
} // end main
<commit_msg>WPI:change example to display 3 abundance map bands<commit_after>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt 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 "otbVectorImage.h"
#include "otbImageFileReader.h"
#include "otbImageFileWriter.h"
#include "otbStreamingStatisticsVectorImageFilter.h"
#include "otbVcaImageFilter.h"
#include "otbUnConstrainedLeastSquareImageFilter.h"
#include "otbVectorRescaleIntensityImageFilter.h"
#include "otbVectorImageToMatrixImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"
#include "otbVectorRescaleIntensityImageFilter.h"
#include "otbMultiToMonoChannelExtractROI.h"
//  Software Guide : BeginCommandLineArgs
//    INPUTS: {ROI_QB_MUL_1.png}
//    OUTPUTS: {PCAOutput.tif}, {InversePCAOutput.tif}, {InversePCAOutput1.png}, {PCAOutput1.png}, {PCAOutput2.png}, {PCAOutput3.png}
//    3
//  Software Guide : EndCommandLineArgs
int main(int argc, char * argv[])
{
  if (argc != 5)
    {
    std::cerr << "Usage: " << argv[0];
    std::cerr << "inputFileName outputFileName EstimatenumberOfEndmembers outputPrettyFilename1 outputPrettyFilename2 outputPrettyFilename3   " << std::endl;
    return EXIT_FAILURE;
    }

  const char *       infname = argv[1];
  const char *       outfname = argv[2];
  const unsigned int estimateNumberOfEndmembers = atoi(argv[3]);
  const unsigned int Dimension = 2;

  typedef double                                  PixelType;
  typedef otb::VectorImage<PixelType, Dimension>  ImageType;
  typedef otb::VectorImage<unsigned char, 2>      OutputPrettyImageType;

  typedef otb::ImageFileReader<ImageType>         ReaderType;
  typedef otb::ImageFileWriter<ImageType>    WriterType;

  typedef otb::VectorRescaleIntensityImageFilter<ImageType,ImageType>             RescalerType;
  typedef otb::VectorRescaleIntensityImageFilter<ImageType,OutputPrettyImageType> RescalerType2;

  typedef otb::StreamingStatisticsVectorImageFilter<ImageType> StreamingStatisticsVectorImageFilterType;
  typedef otb::VCAImageFilter<ImageType>                       VCAFilterType;

  typedef otb::UnConstrainedLeastSquareImageFilter<ImageType,ImageType,double> UCLSUnmixingFilterType;

  typedef otb::VectorImageToMatrixImageFilter<ImageType> VectorImageToMatrixImageFilterType;

  typedef vnl_vector<double> VectorType;
  typedef vnl_matrix<double> MatrixType;

  /// / Noise filtering
  //  typedef otb::LocalActivityVectorImageFilter< ImageType, ImageType > NoiseFilterType;

  // // Image filtering
  //  typedef otb::MNFImageFilter< ImageType, ImageType,
  //    NoiseFilterType, otb::Transform::FORWARD > FilterType;

  //read image
  ReaderType::Pointer reader = ReaderType::New();
  reader->SetFileName(infname);
  reader->UpdateOutputInformation();

  //rescale input image between 0 and 1
  RescalerType::Pointer         rescaler = RescalerType::New();
  rescaler->SetInput(reader->GetOutput());
  rescaler->SetOutputMinimum(0);
  rescaler->SetOutputMaximum(1.);
  /*
  * Estimate Endmembers
  */
  std::cout << "Estimate Endmembers by VCA" << std::endl;

  VCAFilterType::Pointer vca = VCAFilterType::New();
  vca->SetNumberOfEndmembers(estimateNumberOfEndmembers);
  vca->SetInput(rescaler->GetOutput());

  ImageType::Pointer endmembersImage;
  endmembersImage = vca->GetOutput();

  /*
     * Transform Endmembers image to matrix representation
     */
  std::cout << "Endmembers extracted" << std::endl;
  std::cout << "Converting endmembers to matrix" << std::endl;
  VectorImageToMatrixImageFilterType::Pointer endMember2Matrix = VectorImageToMatrixImageFilterType::New();
  endMember2Matrix->SetInput(endmembersImage);
  endMember2Matrix->Update();

  MatrixType endMembersMatrix = endMember2Matrix->GetMatrix();
  std::cout << "Endmembers matrix : " << endMembersMatrix << std::endl;

  /*
  * Unmix
  */
  UCLSUnmixingFilterType::Pointer unmixer =
    UCLSUnmixingFilterType::New();

  unmixer->SetInput(rescaler->GetOutput());
  unmixer->SetMatrix(endMembersMatrix);
  unmixer->SetNumberOfThreads(1); // FIXME : currently buggy

  ImageType::Pointer abundanceMap;
  abundanceMap = unmixer->GetOutput();

  /*
  * Write abundance map
  */
  std::cout << "Write abundance map " << outfname << std::endl;

  WriterType::Pointer  writer = WriterType::New();
  writer->SetInput(abundanceMap);
  writer->SetFileName(outfname);

  writer->Update();

  typedef otb::Image<PixelType, Dimension>                        MonoImageType;
  typedef otb::MultiToMonoChannelExtractROI<PixelType, PixelType> ExtractROIFilterType;
  typedef otb::Image<unsigned char, 2>                            OutputImageType;
  typedef itk::RescaleIntensityImageFilter<MonoImageType,
                                           OutputImageType>       PrettyRescalerType;
  typedef otb::ImageFileWriter<OutputImageType>                   WriterType2;

  for (unsigned int cpt = 0; cpt < 3; ++cpt)
    {
    ExtractROIFilterType::Pointer extractROIFilter = ExtractROIFilterType::New();
    PrettyRescalerType::Pointer   prettyRescaler = PrettyRescalerType::New();
    WriterType2::Pointer          writer2 = WriterType2::New();

    extractROIFilter->SetInput(abundanceMap);
    extractROIFilter->SetChannel(cpt + 1);

    prettyRescaler->SetInput(extractROIFilter->GetOutput());
    prettyRescaler->SetOutputMinimum(0);
    prettyRescaler->SetOutputMaximum(255);

    writer2->SetInput(prettyRescaler->GetOutput());
    writer2->SetFileName(argv[cpt + 4]);
    writer2->Update();
    }

  return EXIT_SUCCESS;
} // end main
<|endoftext|>