<commit_before>// $Id$

//**************************************************************************
//* This file is property of and copyright by the ALICE HLT Project        *
//* ALICE Experiment at CERN, All rights reserved.                         *
//*                                                                        *
//* Primary Authors: Matthias Richter <Matthias.Richter@ift.uib.no>        *
//*                  for The ALICE HLT Project.                            *
//*                                                                        *
//* Permission to use, copy, modify and distribute this software and its   *
//* documentation strictly for non-commercial purposes is hereby granted   *
//* without fee, provided that the above copyright notice appears in all   *
//* copies and that both the copyright notice and this permission notice   *
//* appear in the supporting documentation. The authors make no claims     *
//* about the suitability of this software for any purpose. It is          *
//* provided "as is" without express or implied warranty.                  *
//**************************************************************************

/** @file   AliHLTGlobalAgent.cxx
    @author Matthias Richter
    @date
    @brief  Agent of the libAliHLTGlobal library
*/

#include <cassert>
#include "AliHLTGlobalAgent.h"
#include "AliHLTConfigurationHandler.h"
#include "TObjString.h"
#include "TObjArray.h"
#include "AliHLTGlobalFlatEsdTestComponent.h"

// header files of library components
#include "AliHLTGlobalTrackMergerComponent.h"
#include "AliHLTGlobalCompareFlatComponent.h"
#include "AliHLTGlobalEsdToFlatConverterComponent.h"
#include "AliHLTGlobalFlatEsdConverterComponent.h"
#include "AliHLTGlobalEsdConverterComponent.h"
#include "AliHLTGlobalVertexerComponent.h"
#include "AliHLTGlobalOfflineVertexerComponent.h"
#include "AliHLTV0HistoComponent.h"
#include "AliHLTdNdPtAnalysisComponent.h"
#include "AliHLTdNdPtAnalysisMergerComponent.h"
#include "AliHLTGlobalVertexerHistoComponent.h"
#include "AliHLTGlobalHistoComponent.h"
#include "AliHLTGlobalHistoCollector.h"
#include "AliHLTGlobalDCSPublisherComponent.h"
#include "AliHLTMultiplicityCorrelationsComponent.h"
#include "AliHLTPrimaryVertexFinderComponent.h"
#include "AliHLTV0FinderComponent.h"
#include "AliHLTAnalysisManagerComponent.h"
#include "AliHLTAsyncTestComponent.h"
#include "AliHLTLumiRegComponent.h"
#include "AliHLTGlobalPromptRecoQAComponent.h"
#include "AliHLTRootObjectMergerComponent.h"
#include "AliHLTAsyncCalibrationComponent.h"
#include "AliHLTZeroComponent.h"
#ifdef ZMQ
#include "AliHLTZMQsink.h"
#include "AliHLTZMQsource.h"
#endif

// header file for preprocessor plugin
#include "AliHLTGlobalPreprocessor.h"

#define NOCOMPRESS

/** global instance for agent registration */
AliHLTGlobalAgent gAliHLTGlobalAgent;

/** ROOT macro for the implementation of ROOT specific class methods */
ClassImp(AliHLTGlobalAgent)

AliHLTGlobalAgent::AliHLTGlobalAgent()
  :
  AliHLTModuleAgent("Global")
{
  // see header file for class documentation
  // or
  // refer to README to build package
  // or
  // visit http://web.ift.uib.no/~kjeks/doc/alice-hlt
}

AliHLTGlobalAgent::~AliHLTGlobalAgent()
{
  // see header file for class documentation
}

int AliHLTGlobalAgent::RegisterComponents(AliHLTComponentHandler* pHandler) const
{
  // see header file for class documentation
  assert(pHandler);
  if (!pHandler) return -EINVAL;
  pHandler->AddComponent(new AliHLTGlobalEsdToFlatConverterComponent);
  pHandler->AddComponent(new AliHLTGlobalCompareFlatComponent);
  pHandler->AddComponent(new AliHLTGlobalFlatEsdTestComponent);
  pHandler->AddComponent(new AliHLTGlobalTrackMergerComponent);
  pHandler->AddComponent(new AliHLTGlobalFlatEsdConverterComponent);
  pHandler->AddComponent(new AliHLTGlobalEsdConverterComponent);
  pHandler->AddComponent(new AliHLTGlobalVertexerComponent);
  pHandler->AddComponent(new AliHLTGlobalOfflineVertexerComponent);
  pHandler->AddComponent(new AliHLTGlobalVertexerHistoComponent);
  pHandler->AddComponent(new AliHLTGlobalHistoComponent);
  pHandler->AddComponent(new AliHLTV0HistoComponent );
  //pHandler->AddComponent(new AliHLTdNdPtAnalysisComponent );
  //pHandler->AddComponent(new AliHLTdNdPtAnalysisMergerComponent );
  pHandler->AddComponent(new AliHLTMultiplicityCorrelationsComponent );
  pHandler->AddComponent(new AliHLTPrimaryVertexFinderComponent);
  pHandler->AddComponent(new AliHLTV0FinderComponent);
  pHandler->AddComponent(new AliHLTGlobalHistoCollector );
  pHandler->AddComponent(new AliHLTGlobalDCSPublisherComponent );
  pHandler->AddComponent(new AliHLTAnalysisManagerComponent);
  pHandler->AddComponent(new AliHLTAsyncTestComponent);
  pHandler->AddComponent(new AliHLTAsyncCalibrationComponent);
  pHandler->AddComponent(new AliHLTZeroComponent);
  pHandler->AddComponent(new AliHLTLumiRegComponent);
  pHandler->AddComponent(new AliHLTGlobalPromptRecoQAComponent);
  pHandler->AddComponent(new AliHLTRootObjectMergerComponent);
#ifdef ZMQ
  pHandler->AddComponent(new AliHLTZMQsink);
  pHandler->AddComponent(new AliHLTZMQsource);
#endif
  return 0;
}

int AliHLTGlobalAgent::CreateConfigurations(AliHLTConfigurationHandler* pHandler,
					    AliRawReader* /*rawReader*/,
					    AliRunLoader* /*runloader*/) const
{
  // see header file for class documentation
  if (!pHandler) return -EINVAL;
  TObjArray* pTokens=NULL;

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  //
  // global vertexer component
  //
  // define the inputs
  TString vertexerInputs="TPC-globalmerger ITS-tracker";

  // check for the availibility
  pTokens=vertexerInputs.Tokenize(" ");
  vertexerInputs="";
  if (pTokens) {
    for (int n=0; n<pTokens->GetEntriesFast(); n++) {
      TString module=((TObjString*)pTokens->At(n))->GetString();
      if (pHandler->FindConfiguration(module.Data())) {
	vertexerInputs+=module;
	vertexerInputs+=" ";
      }
    }
    delete pTokens;
    pTokens=NULL;
  }
  if (!vertexerInputs.IsNull()) {
    HLTInfo("\n\nConfiguring inputs to global HLT Vertexer: %s\n\n", vertexerInputs.Data());
    pHandler->CreateConfiguration("GLOBAL-vertexer","GlobalVertexer",vertexerInputs,"");
  } else {
    HLTWarning("\n\nNo inputs to global HLT Vertexer found\n\n");
  }

  /////////////////////////////////////////////////////////////////////////////////////
  //
  // assembly of the global ESD

  // define the inputs to the global ESD
  //TString esdInputs="TPC-globalmerger TPC-mcTrackMarker ITS-tracker TPC-HWCFDecoder TPC-ClusterTransformation GLOBAL-vertexer ITS-SPD-vertexer TPC-dEdx VZERO-RECO ITS-SAPtracker"; //DISABLE GLOBAL VERTEXER TEMPORARILY
  TString esdInputs="TPC-globalmerger TPC-mcTrackMarker ITS-tracker TPC-HWCFDecoder TPC-ClusterTransformation ITS-SPD-vertexer TPC-dEdx VZERO-RECO ITS-SAPtracker";

  // check for the availibility
  pTokens=esdInputs.Tokenize(" ");
  esdInputs="";
  if (pTokens) {
    for (int n=0; n<pTokens->GetEntriesFast(); n++) {
      TString module=((TObjString*)pTokens->At(n))->GetString();
      if (pHandler->FindConfiguration(module.Data())) {
	esdInputs+=module;
	esdInputs+=" ";
      }
    }
    delete pTokens;
    pTokens=NULL;
  }

  if (esdInputs.Length()>0) {
    esdInputs+=" TPC-HWCFDecoder TPC-ClusterTransformation TPC-compressed-cluster-ids";
    HLTInfo("Configuring inputs to global HLT ESD: %s", esdInputs.Data());
  } else {
    HLTWarning("No inputs to global HLT ESD found");
  }
#ifdef NOCOMPRESS
  pHandler->CreateConfiguration("GLOBAL-flat-esd-converter", "GlobalFlatEsdConverter", esdInputs.Data(), "-object-compression=0");
  pHandler->CreateConfiguration("GLOBAL-esd-converter", "GlobalEsdConverter", esdInputs.Data(), "-object-compression=0");
  pHandler->CreateConfiguration("GLOBAL-esd-converter-friends", "GlobalEsdConverter", esdInputs.Data(), "-object-compression=0 -make-friends");
  pHandler->CreateConfiguration("GLOBAL-esd-converter-schema", "GlobalEsdConverter", esdInputs.Data(), "-object-compression=0 -schema");
#endif

#ifndef NOCOMPRESS
  pHandler->CreateConfiguration("GLOBAL-flat-esd-converter", "GlobalFlatEsdConverter", esdInputs.Data(), "");
  pHandler->CreateConfiguration("GLOBAL-esd-converter", "GlobalEsdConverter", esdInputs.Data(), "");
  pHandler->CreateConfiguration("GLOBAL-esd-converter-friends", "GlobalEsdConverter", esdInputs.Data(), "-make-friends");
#endif
  pHandler->CreateConfiguration("GLOBAL-flat-esd-test", "GlobalFlatEsdTest", "GLOBAL-esd-converter GLOBAL-flat-esd-converter", "");
  pHandler->CreateConfiguration("esd-to-flat-conversion", "GlobalEsdToFlatConverter", "GLOBAL-esd-converter-friends", "");
  pHandler->CreateConfiguration("compare-flat", "GlobalCompareFlat", "GLOBAL-flat-esd-converter esd-to-flat-conversion", "");

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  //
  // global histograms
  //
  TString vertexhistoInput="GLOBAL-vertexer ITS-SPD-vertexer";

  // check for the availibility
  pTokens=vertexhistoInput.Tokenize(" ");
  vertexhistoInput="";
  if (pTokens) {
    for (int n=0; n<pTokens->GetEntriesFast(); n++) {
      TString module=((TObjString*)pTokens->At(n))->GetString();
      if (pHandler->FindConfiguration(module.Data())) {
	vertexhistoInput+=module;
	vertexhistoInput+=" ";
      }
    }
    delete pTokens;
    pTokens=NULL;
  }

  if (!vertexhistoInput.IsNull()) {
    HLTInfo("Configuring inputs to global HLT Vertex histogram component: %s", vertexhistoInput.Data());
    pHandler->CreateConfiguration("GLOBAL-vertexhisto","GlobalVertexerHisto", vertexhistoInput.Data(),"");
  } else {
    HLTWarning("No inputs to global HLT Vertex histogram component found");
  }

  //pHandler->CreateConfiguration("GLOBAL-dNdPtAnalysis", "dNdPtAnalysis", "GLOBAL-esd-converter", "");

  pHandler->CreateConfiguration("GLOBAL-MultCorr", "MultiplicityCorrelations", "GLOBAL-esd-converter", "");

  return 0;
}

const char* AliHLTGlobalAgent::GetReconstructionChains(AliRawReader* /*rawReader*/,
						    AliRunLoader* runloader) const
{
  // see header file for class documentation
  if (runloader) {
    // reconstruction chains for AliRoot simulation
    // Note: run loader is only available while running embedded into
    // AliRoot simulation
    return "GLOBAL-esd-converter";
  }
  return NULL;
}

const char* AliHLTGlobalAgent::GetRequiredComponentLibraries() const
{
  // see header file for class documentation

  return "";
}

int AliHLTGlobalAgent::GetHandlerDescription(AliHLTComponentDataType /*dt*/,
					     AliHLTUInt32_t /*spec*/,
					     AliHLTOUTHandlerDesc& /*desc*/) const
{
  // see header file for class documentation

  return 0;
}

AliHLTOUTHandler* AliHLTGlobalAgent::GetOutputHandler(AliHLTComponentDataType /*dt*/,
						      AliHLTUInt32_t /*spec*/)
{
  // see header file for class documentation

  return NULL;
}

int AliHLTGlobalAgent::DeleteOutputHandler(AliHLTOUTHandler* pInstance)
{
  // see header file for class documentation
  if (pInstance==NULL) return -EINVAL;

  return 0;
}

AliHLTModulePreprocessor* AliHLTGlobalAgent::GetPreprocessor()
{
  // create the preprocessor plugin
  return new AliHLTGlobalPreprocessor;
}
<commit_msg>no need to send compressed cluster ids to esd converter now that we decided to pass the HLT track association via the compression<commit_after>// $Id$

//**************************************************************************
//* This file is property of and copyright by the ALICE HLT Project        *
//* ALICE Experiment at CERN, All rights reserved.                         *
//*                                                                        *
//* Primary Authors: Matthias Richter <Matthias.Richter@ift.uib.no>        *
//*                  for The ALICE HLT Project.                            *
//*                                                                        *
//* Permission to use, copy, modify and distribute this software and its   *
//* documentation strictly for non-commercial purposes is hereby granted   *
//* without fee, provided that the above copyright notice appears in all   *
//* copies and that both the copyright notice and this permission notice   *
//* appear in the supporting documentation. The authors make no claims     *
//* about the suitability of this software for any purpose. It is          *
//* provided "as is" without express or implied warranty.                  *
//**************************************************************************

/** @file   AliHLTGlobalAgent.cxx
    @author Matthias Richter
    @date
    @brief  Agent of the libAliHLTGlobal library
*/

#include <cassert>
#include "AliHLTGlobalAgent.h"
#include "AliHLTConfigurationHandler.h"
#include "TObjString.h"
#include "TObjArray.h"
#include "AliHLTGlobalFlatEsdTestComponent.h"

// header files of library components
#include "AliHLTGlobalTrackMergerComponent.h"
#include "AliHLTGlobalCompareFlatComponent.h"
#include "AliHLTGlobalEsdToFlatConverterComponent.h"
#include "AliHLTGlobalFlatEsdConverterComponent.h"
#include "AliHLTGlobalEsdConverterComponent.h"
#include "AliHLTGlobalVertexerComponent.h"
#include "AliHLTGlobalOfflineVertexerComponent.h"
#include "AliHLTV0HistoComponent.h"
#include "AliHLTdNdPtAnalysisComponent.h"
#include "AliHLTdNdPtAnalysisMergerComponent.h"
#include "AliHLTGlobalVertexerHistoComponent.h"
#include "AliHLTGlobalHistoComponent.h"
#include "AliHLTGlobalHistoCollector.h"
#include "AliHLTGlobalDCSPublisherComponent.h"
#include "AliHLTMultiplicityCorrelationsComponent.h"
#include "AliHLTPrimaryVertexFinderComponent.h"
#include "AliHLTV0FinderComponent.h"
#include "AliHLTAnalysisManagerComponent.h"
#include "AliHLTAsyncTestComponent.h"
#include "AliHLTLumiRegComponent.h"
#include "AliHLTGlobalPromptRecoQAComponent.h"
#include "AliHLTRootObjectMergerComponent.h"
#include "AliHLTAsyncCalibrationComponent.h"
#include "AliHLTZeroComponent.h"
#ifdef ZMQ
#include "AliHLTZMQsink.h"
#include "AliHLTZMQsource.h"
#endif

// header file for preprocessor plugin
#include "AliHLTGlobalPreprocessor.h"

#define NOCOMPRESS

/** global instance for agent registration */
AliHLTGlobalAgent gAliHLTGlobalAgent;

/** ROOT macro for the implementation of ROOT specific class methods */
ClassImp(AliHLTGlobalAgent)

AliHLTGlobalAgent::AliHLTGlobalAgent()
  :
  AliHLTModuleAgent("Global")
{
  // see header file for class documentation
  // or
  // refer to README to build package
  // or
  // visit http://web.ift.uib.no/~kjeks/doc/alice-hlt
}

AliHLTGlobalAgent::~AliHLTGlobalAgent()
{
  // see header file for class documentation
}

int AliHLTGlobalAgent::RegisterComponents(AliHLTComponentHandler* pHandler) const
{
  // see header file for class documentation
  assert(pHandler);
  if (!pHandler) return -EINVAL;
  pHandler->AddComponent(new AliHLTGlobalEsdToFlatConverterComponent);
  pHandler->AddComponent(new AliHLTGlobalCompareFlatComponent);
  pHandler->AddComponent(new AliHLTGlobalFlatEsdTestComponent);
  pHandler->AddComponent(new AliHLTGlobalTrackMergerComponent);
  pHandler->AddComponent(new AliHLTGlobalFlatEsdConverterComponent);
  pHandler->AddComponent(new AliHLTGlobalEsdConverterComponent);
  pHandler->AddComponent(new AliHLTGlobalVertexerComponent);
  pHandler->AddComponent(new AliHLTGlobalOfflineVertexerComponent);
  pHandler->AddComponent(new AliHLTGlobalVertexerHistoComponent);
  pHandler->AddComponent(new AliHLTGlobalHistoComponent);
  pHandler->AddComponent(new AliHLTV0HistoComponent );
  //pHandler->AddComponent(new AliHLTdNdPtAnalysisComponent );
  //pHandler->AddComponent(new AliHLTdNdPtAnalysisMergerComponent );
  pHandler->AddComponent(new AliHLTMultiplicityCorrelationsComponent );
  pHandler->AddComponent(new AliHLTPrimaryVertexFinderComponent);
  pHandler->AddComponent(new AliHLTV0FinderComponent);
  pHandler->AddComponent(new AliHLTGlobalHistoCollector );
  pHandler->AddComponent(new AliHLTGlobalDCSPublisherComponent );
  pHandler->AddComponent(new AliHLTAnalysisManagerComponent);
  pHandler->AddComponent(new AliHLTAsyncTestComponent);
  pHandler->AddComponent(new AliHLTAsyncCalibrationComponent);
  pHandler->AddComponent(new AliHLTZeroComponent);
  pHandler->AddComponent(new AliHLTLumiRegComponent);
  pHandler->AddComponent(new AliHLTGlobalPromptRecoQAComponent);
  pHandler->AddComponent(new AliHLTRootObjectMergerComponent);
#ifdef ZMQ
  pHandler->AddComponent(new AliHLTZMQsink);
  pHandler->AddComponent(new AliHLTZMQsource);
#endif
  return 0;
}

int AliHLTGlobalAgent::CreateConfigurations(AliHLTConfigurationHandler* pHandler,
					    AliRawReader* /*rawReader*/,
					    AliRunLoader* /*runloader*/) const
{
  // see header file for class documentation
  if (!pHandler) return -EINVAL;
  TObjArray* pTokens=NULL;

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  //
  // global vertexer component
  //
  // define the inputs
  TString vertexerInputs="TPC-globalmerger ITS-tracker";

  // check for the availibility
  pTokens=vertexerInputs.Tokenize(" ");
  vertexerInputs="";
  if (pTokens) {
    for (int n=0; n<pTokens->GetEntriesFast(); n++) {
      TString module=((TObjString*)pTokens->At(n))->GetString();
      if (pHandler->FindConfiguration(module.Data())) {
	vertexerInputs+=module;
	vertexerInputs+=" ";
      }
    }
    delete pTokens;
    pTokens=NULL;
  }
  if (!vertexerInputs.IsNull()) {
    HLTInfo("\n\nConfiguring inputs to global HLT Vertexer: %s\n\n", vertexerInputs.Data());
    pHandler->CreateConfiguration("GLOBAL-vertexer","GlobalVertexer",vertexerInputs,"");
  } else {
    HLTWarning("\n\nNo inputs to global HLT Vertexer found\n\n");
  }

  /////////////////////////////////////////////////////////////////////////////////////
  //
  // assembly of the global ESD

  // define the inputs to the global ESD
  //TString esdInputs="TPC-globalmerger TPC-mcTrackMarker ITS-tracker TPC-HWCFDecoder TPC-ClusterTransformation GLOBAL-vertexer ITS-SPD-vertexer TPC-dEdx VZERO-RECO ITS-SAPtracker"; //DISABLE GLOBAL VERTEXER TEMPORARILY
  TString esdInputs="TPC-globalmerger TPC-mcTrackMarker ITS-tracker TPC-HWCFDecoder TPC-ClusterTransformation ITS-SPD-vertexer TPC-dEdx VZERO-RECO ITS-SAPtracker";

  // check for the availibility
  pTokens=esdInputs.Tokenize(" ");
  esdInputs="";
  if (pTokens) {
    for (int n=0; n<pTokens->GetEntriesFast(); n++) {
      TString module=((TObjString*)pTokens->At(n))->GetString();
      if (pHandler->FindConfiguration(module.Data())) {
	esdInputs+=module;
	esdInputs+=" ";
      }
    }
    delete pTokens;
    pTokens=NULL;
  }

  if (esdInputs.Length()>0) {
    esdInputs+=" TPC-HWCFDecoder TPC-ClusterTransformation";
    HLTInfo("Configuring inputs to global HLT ESD: %s", esdInputs.Data());
  } else {
    HLTWarning("No inputs to global HLT ESD found");
  }
#ifdef NOCOMPRESS
  pHandler->CreateConfiguration("GLOBAL-flat-esd-converter", "GlobalFlatEsdConverter", esdInputs.Data(), "-object-compression=0");
  pHandler->CreateConfiguration("GLOBAL-esd-converter", "GlobalEsdConverter", esdInputs.Data(), "-object-compression=0");
  pHandler->CreateConfiguration("GLOBAL-esd-converter-friends", "GlobalEsdConverter", esdInputs.Data(), "-object-compression=0 -make-friends");
  pHandler->CreateConfiguration("GLOBAL-esd-converter-schema", "GlobalEsdConverter", esdInputs.Data(), "-object-compression=0 -schema");
#endif

#ifndef NOCOMPRESS
  pHandler->CreateConfiguration("GLOBAL-flat-esd-converter", "GlobalFlatEsdConverter", esdInputs.Data(), "");
  pHandler->CreateConfiguration("GLOBAL-esd-converter", "GlobalEsdConverter", esdInputs.Data(), "");
  pHandler->CreateConfiguration("GLOBAL-esd-converter-friends", "GlobalEsdConverter", esdInputs.Data(), "-make-friends");
#endif
  pHandler->CreateConfiguration("GLOBAL-flat-esd-test", "GlobalFlatEsdTest", "GLOBAL-esd-converter GLOBAL-flat-esd-converter", "");
  pHandler->CreateConfiguration("esd-to-flat-conversion", "GlobalEsdToFlatConverter", "GLOBAL-esd-converter-friends", "");
  pHandler->CreateConfiguration("compare-flat", "GlobalCompareFlat", "GLOBAL-flat-esd-converter esd-to-flat-conversion", "");

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  //
  // global histograms
  //
  TString vertexhistoInput="GLOBAL-vertexer ITS-SPD-vertexer";

  // check for the availibility
  pTokens=vertexhistoInput.Tokenize(" ");
  vertexhistoInput="";
  if (pTokens) {
    for (int n=0; n<pTokens->GetEntriesFast(); n++) {
      TString module=((TObjString*)pTokens->At(n))->GetString();
      if (pHandler->FindConfiguration(module.Data())) {
	vertexhistoInput+=module;
	vertexhistoInput+=" ";
      }
    }
    delete pTokens;
    pTokens=NULL;
  }

  if (!vertexhistoInput.IsNull()) {
    HLTInfo("Configuring inputs to global HLT Vertex histogram component: %s", vertexhistoInput.Data());
    pHandler->CreateConfiguration("GLOBAL-vertexhisto","GlobalVertexerHisto", vertexhistoInput.Data(),"");
  } else {
    HLTWarning("No inputs to global HLT Vertex histogram component found");
  }

  //pHandler->CreateConfiguration("GLOBAL-dNdPtAnalysis", "dNdPtAnalysis", "GLOBAL-esd-converter", "");

  pHandler->CreateConfiguration("GLOBAL-MultCorr", "MultiplicityCorrelations", "GLOBAL-esd-converter", "");

  return 0;
}

const char* AliHLTGlobalAgent::GetReconstructionChains(AliRawReader* /*rawReader*/,
						    AliRunLoader* runloader) const
{
  // see header file for class documentation
  if (runloader) {
    // reconstruction chains for AliRoot simulation
    // Note: run loader is only available while running embedded into
    // AliRoot simulation
    return "GLOBAL-esd-converter";
  }
  return NULL;
}

const char* AliHLTGlobalAgent::GetRequiredComponentLibraries() const
{
  // see header file for class documentation

  return "";
}

int AliHLTGlobalAgent::GetHandlerDescription(AliHLTComponentDataType /*dt*/,
					     AliHLTUInt32_t /*spec*/,
					     AliHLTOUTHandlerDesc& /*desc*/) const
{
  // see header file for class documentation

  return 0;
}

AliHLTOUTHandler* AliHLTGlobalAgent::GetOutputHandler(AliHLTComponentDataType /*dt*/,
						      AliHLTUInt32_t /*spec*/)
{
  // see header file for class documentation

  return NULL;
}

int AliHLTGlobalAgent::DeleteOutputHandler(AliHLTOUTHandler* pInstance)
{
  // see header file for class documentation
  if (pInstance==NULL) return -EINVAL;

  return 0;
}

AliHLTModulePreprocessor* AliHLTGlobalAgent::GetPreprocessor()
{
  // create the preprocessor plugin
  return new AliHLTGlobalPreprocessor;
}
<|endoftext|>


<commit_before>//===- InlineFunction.cpp - Code to perform function inlining -------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements inlining of a function into a call site, resolving
// parameters and the return value as appropriate.
//
// FIXME: This pass should transform alloca instructions in the called function
//        into malloc/free pairs!  Or perhaps it should refuse to inline them!
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Constant.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Module.h"
#include "llvm/Instructions.h"
#include "llvm/Intrinsics.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Transforms/Utils/Local.h"
using namespace llvm;

bool llvm::InlineFunction(CallInst *CI) { return InlineFunction(CallSite(CI)); }
bool llvm::InlineFunction(InvokeInst *II) {return InlineFunction(CallSite(II));}

// InlineFunction - This function inlines the called function into the basic
// block of the caller.  This returns false if it is not possible to inline this
// call.  The program is still in a well defined state if this occurs though.
//
// Note that this only does one level of inlining.  For example, if the
// instruction 'call B' is inlined, and 'B' calls 'C', then the call to 'C' now
// exists in the instruction stream.  Similiarly this will inline a recursive
// function by one level.
//
bool llvm::InlineFunction(CallSite CS) {
  Instruction *TheCall = CS.getInstruction();
  assert(TheCall->getParent() && TheCall->getParent()->getParent() &&
         "Instruction not in function!");

  const Function *CalledFunc = CS.getCalledFunction();
  if (CalledFunc == 0 ||          // Can't inline external function or indirect
      CalledFunc->isExternal() || // call, or call to a vararg function!
      CalledFunc->getFunctionType()->isVarArg()) return false;

  BasicBlock *OrigBB = TheCall->getParent();
  Function *Caller = OrigBB->getParent();

  // We want to clone the entire callee function into the whole between the
  // "starter" and "ender" blocks.  How we accomplish this depends on whether
  // this is an invoke instruction or a call instruction.

  BasicBlock *InvokeDest = 0;     // Exception handling destination
  std::vector<Value*> InvokeDestPHIValues; // Values for PHI nodes in InvokeDest
  BasicBlock *AfterCallBB;

  if (InvokeInst *II = dyn_cast<InvokeInst>(TheCall)) {
    InvokeDest = II->getExceptionalDest();

    // If there are PHI nodes in the exceptional destination block, we need to
    // keep track of which values came into them from this invoke, then remove
    // the entry for this block.
    for (BasicBlock::iterator I = InvokeDest->begin();
         PHINode *PN = dyn_cast<PHINode>(I); ++I) {
      // Save the value to use for this edge...
      InvokeDestPHIValues.push_back(PN->getIncomingValueForBlock(OrigBB));
    }

    // Add an unconditional branch to make this look like the CallInst case...
    BranchInst *NewBr = new BranchInst(II->getNormalDest(), TheCall);

    // Split the basic block.  This guarantees that no PHI nodes will have to be
    // updated due to new incoming edges, and make the invoke case more
    // symmetric to the call case.
    AfterCallBB = OrigBB->splitBasicBlock(NewBr,
                                          CalledFunc->getName()+".entry");

    // Remove (unlink) the InvokeInst from the function...
    OrigBB->getInstList().remove(TheCall);

  } else {  // It's a call
    // If this is a call instruction, we need to split the basic block that the
    // call lives in.
    //
    AfterCallBB = OrigBB->splitBasicBlock(TheCall,
                                          CalledFunc->getName()+".entry");
    // Remove (unlink) the CallInst from the function...
    AfterCallBB->getInstList().remove(TheCall);
  }

  // If we have a return value generated by this call, convert it into a PHI
  // node that gets values from each of the old RET instructions in the original
  // function.
  //
  PHINode *PHI = 0;
  if (!TheCall->use_empty()) {
    // The PHI node should go at the front of the new basic block to merge all
    // possible incoming values.
    //
    PHI = new PHINode(CalledFunc->getReturnType(), TheCall->getName(),
                      AfterCallBB->begin());

    // Anything that used the result of the function call should now use the PHI
    // node as their operand.
    //
    TheCall->replaceAllUsesWith(PHI);
  }

  // Get an iterator to the last basic block in the function, which will have
  // the new function inlined after it.
  //
  Function::iterator LastBlock = &Caller->back();

  // Calculate the vector of arguments to pass into the function cloner...
  std::map<const Value*, Value*> ValueMap;
  assert(std::distance(CalledFunc->abegin(), CalledFunc->aend()) ==
         std::distance(CS.arg_begin(), CS.arg_end()) &&
         "No varargs calls can be inlined!");

  CallSite::arg_iterator AI = CS.arg_begin();
  for (Function::const_aiterator I = CalledFunc->abegin(), E=CalledFunc->aend();
       I != E; ++I, ++AI)
    ValueMap[I] = *AI;

  // Since we are now done with the Call/Invoke, we can delete it.
  delete TheCall;

  // Make a vector to capture the return instructions in the cloned function...
  std::vector<ReturnInst*> Returns;

  // Do all of the hard part of cloning the callee into the caller...
  CloneFunctionInto(Caller, CalledFunc, ValueMap, Returns, ".i");

  // Loop over all of the return instructions, turning them into unconditional
  // branches to the merge point now...
  for (unsigned i = 0, e = Returns.size(); i != e; ++i) {
    ReturnInst *RI = Returns[i];
    BasicBlock *BB = RI->getParent();

    // Add a branch to the merge point where the PHI node lives if it exists.
    new BranchInst(AfterCallBB, RI);

    if (PHI) {   // The PHI node should include this value!
      assert(RI->getReturnValue() && "Ret should have value!");
      assert(RI->getReturnValue()->getType() == PHI->getType() &&
             "Ret value not consistent in function!");
      PHI->addIncoming(RI->getReturnValue(), BB);
    }

    // Delete the return instruction now
    BB->getInstList().erase(RI);
  }

  // Check to see if the PHI node only has one argument.  This is a common
  // case resulting from there only being a single return instruction in the
  // function call.  Because this is so common, eliminate the PHI node.
  //
  if (PHI && PHI->getNumIncomingValues() == 1) {
    PHI->replaceAllUsesWith(PHI->getIncomingValue(0));
    PHI->getParent()->getInstList().erase(PHI);
  }

  // Change the branch that used to go to AfterCallBB to branch to the first
  // basic block of the inlined function.
  //
  TerminatorInst *Br = OrigBB->getTerminator();
  assert(Br && Br->getOpcode() == Instruction::Br &&
	 "splitBasicBlock broken!");
  Br->setOperand(0, ++LastBlock);

  // If there are any alloca instructions in the block that used to be the entry
  // block for the callee, move them to the entry block of the caller.  First
  // calculate which instruction they should be inserted before.  We insert the
  // instructions at the end of the current alloca list.
  //
  if (isa<AllocaInst>(LastBlock->begin())) {
    BasicBlock::iterator InsertPoint = Caller->begin()->begin();
    while (isa<AllocaInst>(InsertPoint)) ++InsertPoint;

    for (BasicBlock::iterator I = LastBlock->begin(), E = LastBlock->end();
         I != E; )
      if (AllocaInst *AI = dyn_cast<AllocaInst>(I++))
        if (isa<Constant>(AI->getArraySize())) {
          LastBlock->getInstList().remove(AI);
          Caller->front().getInstList().insert(InsertPoint, AI);
        }
  }

  // If we just inlined a call due to an invoke instruction, scan the inlined
  // function checking for function calls that should now be made into invoke
  // instructions, and for unwind's which should be turned into branches.
  if (InvokeDest) {
    for (Function::iterator BB = LastBlock, E = Caller->end(); BB != E; ++BB) {
      for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) {
        // We only need to check for function calls: inlined invoke instructions
        // require no special handling...
        if (CallInst *CI = dyn_cast<CallInst>(I)) {
          // Convert this function call into an invoke instruction...

          // First, split the basic block...
          BasicBlock *Split = BB->splitBasicBlock(CI, CI->getName()+".noexc");

          // Next, create the new invoke instruction, inserting it at the end
          // of the old basic block.
          InvokeInst *II =
            new InvokeInst(CI->getCalledValue(), Split, InvokeDest,
                           std::vector<Value*>(CI->op_begin()+1, CI->op_end()),
                           CI->getName(), BB->getTerminator());

          // Make sure that anything using the call now uses the invoke!
          CI->replaceAllUsesWith(II);

          // Delete the unconditional branch inserted by splitBasicBlock
          BB->getInstList().pop_back();
          Split->getInstList().pop_front();  // Delete the original call

          // Update any PHI nodes in the exceptional block to indicate that
          // there is now a new entry in them.
          unsigned i = 0;
          for (BasicBlock::iterator I = InvokeDest->begin();
               PHINode *PN = dyn_cast<PHINode>(I); ++I, ++i)
            PN->addIncoming(InvokeDestPHIValues[i], BB);

          // This basic block is now complete, start scanning the next one.
          break;
        } else {
          ++I;
        }
      }

      if (UnwindInst *UI = dyn_cast<UnwindInst>(BB->getTerminator())) {
        // An UnwindInst requires special handling when it gets inlined into an
        // invoke site.  Once this happens, we know that the unwind would cause
        // a control transfer to the invoke exception destination, so we can
        // transform it into a direct branch to the exception destination.
        new BranchInst(InvokeDest, UI);

        // Delete the unwind instruction!
        UI->getParent()->getInstList().pop_back();

        // Update any PHI nodes in the exceptional block to indicate that
        // there is now a new entry in them.
        unsigned i = 0;
        for (BasicBlock::iterator I = InvokeDest->begin();
             PHINode *PN = dyn_cast<PHINode>(I); ++I, ++i)
          PN->addIncoming(InvokeDestPHIValues[i], BB);
      }
    }

    // Now that everything is happy, we have one final detail.  The PHI nodes in
    // the exception destination block still have entries due to the original
    // invoke instruction.  Eliminate these entries (which might even delete the
    // PHI node) now.
    for (BasicBlock::iterator I = InvokeDest->begin();
         PHINode *PN = dyn_cast<PHINode>(I); ++I)
      PN->removeIncomingValue(AfterCallBB);
  }
  // Now that the function is correct, make it a little bit nicer.  In
  // particular, move the basic blocks inserted from the end of the function
  // into the space made by splitting the source basic block.
  //
  Caller->getBasicBlockList().splice(AfterCallBB, Caller->getBasicBlockList(),
                                     LastBlock, Caller->end());

  // We should always be able to fold the entry block of the function into the
  // single predecessor of the block...
  assert(cast<BranchInst>(Br)->isUnconditional() && "splitBasicBlock broken!");
  BasicBlock *CalleeEntry = cast<BranchInst>(Br)->getSuccessor(0);
  SimplifyCFG(CalleeEntry);

  // Okay, continue the CFG cleanup.  It's often the case that there is only a
  // single return instruction in the callee function.  If this is the case,
  // then we have an unconditional branch from the return block to the
  // 'AfterCallBB'.  Check for this case, and eliminate the branch is possible.
  SimplifyCFG(AfterCallBB);
  return true;
}
<commit_msg>Move the cloning of the function body much earlier in the inlinefunction process.  The only optimization we did so far is to avoid creating a PHI node, then immediately destroying it in the common case where the callee has one return statement.  Instead, we just don't create the return value.  This has no noticable performance impact, but paves the way for future improvements.<commit_after>//===- InlineFunction.cpp - Code to perform function inlining -------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements inlining of a function into a call site, resolving
// parameters and the return value as appropriate.
//
// FIXME: This pass should transform alloca instructions in the called function
// into alloca/dealloca pairs!  Or perhaps it should refuse to inline them!
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Constant.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Module.h"
#include "llvm/Instructions.h"
#include "llvm/Intrinsics.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Transforms/Utils/Local.h"
using namespace llvm;

bool llvm::InlineFunction(CallInst *CI) { return InlineFunction(CallSite(CI)); }
bool llvm::InlineFunction(InvokeInst *II) {return InlineFunction(CallSite(II));}

// InlineFunction - This function inlines the called function into the basic
// block of the caller.  This returns false if it is not possible to inline this
// call.  The program is still in a well defined state if this occurs though.
//
// Note that this only does one level of inlining.  For example, if the
// instruction 'call B' is inlined, and 'B' calls 'C', then the call to 'C' now
// exists in the instruction stream.  Similiarly this will inline a recursive
// function by one level.
//
bool llvm::InlineFunction(CallSite CS) {
  Instruction *TheCall = CS.getInstruction();
  assert(TheCall->getParent() && TheCall->getParent()->getParent() &&
         "Instruction not in function!");

  const Function *CalledFunc = CS.getCalledFunction();
  if (CalledFunc == 0 ||          // Can't inline external function or indirect
      CalledFunc->isExternal() || // call, or call to a vararg function!
      CalledFunc->getFunctionType()->isVarArg()) return false;

  BasicBlock *OrigBB = TheCall->getParent();
  Function *Caller = OrigBB->getParent();

  // Calculate the vector of arguments to pass into the function cloner...
  std::map<const Value*, Value*> ValueMap;
  assert(std::distance(CalledFunc->abegin(), CalledFunc->aend()) ==
         std::distance(CS.arg_begin(), CS.arg_end()) &&
         "No varargs calls can be inlined!");

  CallSite::arg_iterator AI = CS.arg_begin();
  for (Function::const_aiterator I = CalledFunc->abegin(), E=CalledFunc->aend();
       I != E; ++I, ++AI)
    ValueMap[I] = *AI;

  // Get an iterator to the last basic block in the function, which will have
  // the new function inlined after it.
  //
  Function::iterator LastBlock = &Caller->back();

  // Clone the entire body of the callee into the caller.  Make sure to capture
  // all of the return instructions from the cloned function.
  std::vector<ReturnInst*> Returns;
  CloneFunctionInto(Caller, CalledFunc, ValueMap, Returns, ".i");

  // We want to clone the entire callee function into the hole between the
  // "starter" and "ender" blocks.  How we accomplish this depends on whether
  // this is an invoke instruction or a call instruction.

  BasicBlock *InvokeDest = 0;     // Exception handling destination
  std::vector<Value*> InvokeDestPHIValues; // Values for PHI nodes in InvokeDest
  BasicBlock *AfterCallBB;

  if (InvokeInst *II = dyn_cast<InvokeInst>(TheCall)) {
    InvokeDest = II->getExceptionalDest();

    // If there are PHI nodes in the exceptional destination block, we need to
    // keep track of which values came into them from this invoke, then remove
    // the entry for this block.
    for (BasicBlock::iterator I = InvokeDest->begin();
         PHINode *PN = dyn_cast<PHINode>(I); ++I) {
      // Save the value to use for this edge...
      InvokeDestPHIValues.push_back(PN->getIncomingValueForBlock(OrigBB));
    }

    // Add an unconditional branch to make this look like the CallInst case...
    BranchInst *NewBr = new BranchInst(II->getNormalDest(), TheCall);

    // Split the basic block.  This guarantees that no PHI nodes will have to be
    // updated due to new incoming edges, and make the invoke case more
    // symmetric to the call case.
    AfterCallBB = OrigBB->splitBasicBlock(NewBr,
                                          CalledFunc->getName()+".entry");

    // Remove (unlink) the InvokeInst from the function...
    OrigBB->getInstList().remove(TheCall);

  } else {  // It's a call
    // If this is a call instruction, we need to split the basic block that the
    // call lives in.
    //
    AfterCallBB = OrigBB->splitBasicBlock(TheCall,
                                          CalledFunc->getName()+".entry");
    // Remove (unlink) the CallInst from the function...
    AfterCallBB->getInstList().remove(TheCall);
  }

  // If we have a return value generated by this call, convert it into a PHI
  // node that gets values from each of the old RET instructions in the original
  // function.
  //
  if (!TheCall->use_empty()) {
    // We only need to make the PHI if there is more than one return instruction
    if (Returns.size() > 1) {
      // The PHI node should go at the front of the new basic block to merge all
      // possible incoming values.
      //
      PHINode *PHI = new PHINode(CalledFunc->getReturnType(),
                                 TheCall->getName(), AfterCallBB->begin());

      // Anything that used the result of the function call should now use the
      // PHI node as their operand.
      //
      TheCall->replaceAllUsesWith(PHI);

      // Add all of the return instructions as entries in the PHI node.
      for (unsigned i = 0, e = Returns.size(); i != e; ++i) {
        ReturnInst *RI = Returns[i];

        assert(RI->getReturnValue() && "Ret should have value!");
        assert(RI->getReturnValue()->getType() == PHI->getType() &&
               "Ret value not consistent in function!");
        PHI->addIncoming(RI->getReturnValue(), RI->getParent());
      }

    } else if (!Returns.empty()) {
      // Otherwise, if there is exactly one return value, just replace anything
      // using the return value of the call with the computed value.
      TheCall->replaceAllUsesWith(Returns[0]->getReturnValue());
    }
  }

  // Since we are now done with the Call/Invoke, we can delete it.
  delete TheCall;

  // Loop over all of the return instructions, turning them into unconditional
  // branches to the merge point now...
  for (unsigned i = 0, e = Returns.size(); i != e; ++i) {
    ReturnInst *RI = Returns[i];

    // Add a branch to the merge point where the PHI node lives if it exists.
    new BranchInst(AfterCallBB, RI);

    // Delete the return instruction now
    RI->getParent()->getInstList().erase(RI);
  }

  // Change the branch that used to go to AfterCallBB to branch to the first
  // basic block of the inlined function.
  //
  TerminatorInst *Br = OrigBB->getTerminator();
  assert(Br && Br->getOpcode() == Instruction::Br &&
	 "splitBasicBlock broken!");
  Br->setOperand(0, ++LastBlock);

  // If there are any alloca instructions in the block that used to be the entry
  // block for the callee, move them to the entry block of the caller.  First
  // calculate which instruction they should be inserted before.  We insert the
  // instructions at the end of the current alloca list.
  //
  if (isa<AllocaInst>(LastBlock->begin())) {
    BasicBlock::iterator InsertPoint = Caller->begin()->begin();
    while (isa<AllocaInst>(InsertPoint)) ++InsertPoint;

    for (BasicBlock::iterator I = LastBlock->begin(), E = LastBlock->end();
         I != E; )
      if (AllocaInst *AI = dyn_cast<AllocaInst>(I++))
        if (isa<Constant>(AI->getArraySize())) {
          LastBlock->getInstList().remove(AI);
          Caller->front().getInstList().insert(InsertPoint, AI);
        }
  }

  // If we just inlined a call due to an invoke instruction, scan the inlined
  // function checking for function calls that should now be made into invoke
  // instructions, and for unwind's which should be turned into branches.
  if (InvokeDest) {
    for (Function::iterator BB = LastBlock, E = Caller->end(); BB != E; ++BB) {
      for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) {
        // We only need to check for function calls: inlined invoke instructions
        // require no special handling...
        if (CallInst *CI = dyn_cast<CallInst>(I)) {
          // Convert this function call into an invoke instruction...

          // First, split the basic block...
          BasicBlock *Split = BB->splitBasicBlock(CI, CI->getName()+".noexc");

          // Next, create the new invoke instruction, inserting it at the end
          // of the old basic block.
          InvokeInst *II =
            new InvokeInst(CI->getCalledValue(), Split, InvokeDest,
                           std::vector<Value*>(CI->op_begin()+1, CI->op_end()),
                           CI->getName(), BB->getTerminator());

          // Make sure that anything using the call now uses the invoke!
          CI->replaceAllUsesWith(II);

          // Delete the unconditional branch inserted by splitBasicBlock
          BB->getInstList().pop_back();
          Split->getInstList().pop_front();  // Delete the original call

          // Update any PHI nodes in the exceptional block to indicate that
          // there is now a new entry in them.
          unsigned i = 0;
          for (BasicBlock::iterator I = InvokeDest->begin();
               PHINode *PN = dyn_cast<PHINode>(I); ++I, ++i)
            PN->addIncoming(InvokeDestPHIValues[i], BB);

          // This basic block is now complete, start scanning the next one.
          break;
        } else {
          ++I;
        }
      }

      if (UnwindInst *UI = dyn_cast<UnwindInst>(BB->getTerminator())) {
        // An UnwindInst requires special handling when it gets inlined into an
        // invoke site.  Once this happens, we know that the unwind would cause
        // a control transfer to the invoke exception destination, so we can
        // transform it into a direct branch to the exception destination.
        new BranchInst(InvokeDest, UI);

        // Delete the unwind instruction!
        UI->getParent()->getInstList().pop_back();

        // Update any PHI nodes in the exceptional block to indicate that
        // there is now a new entry in them.
        unsigned i = 0;
        for (BasicBlock::iterator I = InvokeDest->begin();
             PHINode *PN = dyn_cast<PHINode>(I); ++I, ++i)
          PN->addIncoming(InvokeDestPHIValues[i], BB);
      }
    }

    // Now that everything is happy, we have one final detail.  The PHI nodes in
    // the exception destination block still have entries due to the original
    // invoke instruction.  Eliminate these entries (which might even delete the
    // PHI node) now.
    for (BasicBlock::iterator I = InvokeDest->begin();
         PHINode *PN = dyn_cast<PHINode>(I); ++I)
      PN->removeIncomingValue(AfterCallBB);
  }
  // Now that the function is correct, make it a little bit nicer.  In
  // particular, move the basic blocks inserted from the end of the function
  // into the space made by splitting the source basic block.
  //
  Caller->getBasicBlockList().splice(AfterCallBB, Caller->getBasicBlockList(),
                                     LastBlock, Caller->end());

  // We should always be able to fold the entry block of the function into the
  // single predecessor of the block...
  assert(cast<BranchInst>(Br)->isUnconditional() && "splitBasicBlock broken!");
  BasicBlock *CalleeEntry = cast<BranchInst>(Br)->getSuccessor(0);
  SimplifyCFG(CalleeEntry);

  // Okay, continue the CFG cleanup.  It's often the case that there is only a
  // single return instruction in the callee function.  If this is the case,
  // then we have an unconditional branch from the return block to the
  // 'AfterCallBB'.  Check for this case, and eliminate the branch is possible.
  SimplifyCFG(AfterCallBB);
  return true;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <vector>
#include <array>
#include <valarray>

#include "gal.hpp"

#include "test_construction.hpp"
#include "test_construction_from_std.hpp"
#include "test_arithmetic.hpp"

#include "test_geometry.hpp"

/*
* Lgg till transpose-funktion.

Frgor
* Konstruktr utan initializer list fr arrayer med rank=1.
* copy-construction frn samma typ. Hanteras det av de templatiserade konstruktrerna?
* Single-dimensional construction from:
  - Eigen::Matrix

* Implicit konvertering? Fr gande arrayer: Nej. Fr pekare ?

* Hur ska nullptr hanteras? Ska man krva att size()==0 d?

* sliced_array, med stride, lgg till. Finns det nn kort bokstav fr det?

* make_pdarray({1,2,4}, data); // S man slipper redundanta template argument.
  make_darray({1,2}, 0.f);
  make_sarray({1, 2}, 1.f);

* Hur ska multiplikation och division hanteras? Vill kunna hantera fallen:
  - Elementvis
  - Matriser
  - Komplexa tal
  - Kvaternioner
  - Expression templates
* Ha mnga elementvisa operationer i annan modul: * / % < <= > >= && ! ||.

* storage order fr matriser.
- Skulle kunna ha allmn default-templatiserad flagga i class-signature.
-  Eller kan lta den bara pverka operator() !.
D kan man stta den typ:
using matrix23f = sarray<float,2,3,ROW_MAJOR>
- Skapa matris-klass som rver frn sarray? Vore ju bttre om man inte behvde rva frn den.
- Skapa tv fria funktioner fr operator(), som kan vlja mellan att plocka ut i row-major eller column major.
at_cm(matrix, 3, 2)
at_rm(matrix, 2, 3)
at_cm<3,2>(matrix)
at_rm<2,3>(matrix)

* Hur ska konstruktorerna vara fr att dom ska vara konsekventa fr alla arrayer?
  - typ, dimension, storlek, vrden.

auto a = sarray<int, 2, 3>(1, 2, 3, 4, 5, 6);   // <- funkar nog inte det bra fr matriser i den column/row-order som vi har?
auto a = sarray<int, 2, 3>({1, 2, 3, 4, 5, 6}); // <-
auto a = sarray<int, 2, 3>({{1, 2, 3}, {4, 5, 6}}); // <-
auto a = sarray<int, 2, 3>(data_iterator);
auto a = sarray<int, 2, 3>(data_value);
auto a = sarray<int, 2, 3>(data_array);

auto a = darray<int, 2>({2, 3}, data_iterator); // Tillt iterator istllet fr pekare?
auto a = darray<int, 2>({2, 3}, data_value);
auto a = darray<int, 2>({2, 3});
auto a = darray<int, 2>(data_array);

auto a = pdarray<int, 2>({2, 3}, data_iterator);
auto a = pdarray<int, 2>(data_array);

auto a = psarray<int, 2, 3>(data_iterator);
auto a = psarray<int, 2, 3>(data_array);

auto a = std::array<int, 2>({1, 2})
auto a = std::vector<int>(size)
auto a = std::vector<int>(size, data_value)
auto a = std::valarray<int>(size)
auto a = std::valarray<int>(data_value, size) <- exception

assert(extents(array1) == extents(array2))
*/

void f(pdarray<int> a)
{
	for (auto& x : a)
	{
		++x;
	}
}

void h(pdarray<const int> a)
{
	using namespace std;
	for (auto& x : a)
	{
		cout << x << " ";
	}
	cout << endl;
}

void g(pdarray<int, 2> a)
{
	for (auto& x : a)
	{
		++x;
	}

	for (size_t y = 0; y < a.extent1(); ++y)
	{
		for (size_t x = 0; x < a.extent0(); ++x)
		{
			std::cout << a(x, y) << " ";
		}
		std::cout << std::endl;
	}
}

int main()
{
	using namespace std;

	cout << "total_size: " << total_size<1, 2, 3>::value << endl;

	cout << "get_extent0: " << get_size<0, 8, 9, 6, 5>::value << endl;
	cout << "get_extent1: " << get_size<1, 8, 9, 6, 5>::value << endl;
	cout << "get_extent2: " << get_size<2, 8, 9, 6, 5>::value << endl;
	cout << "get_extent3: " << get_size<3, 8, 9, 6, 5>::value << endl;

	const auto W = size_t(4);
	const auto H = size_t(3);
	const auto N = W * H;

	auto a = vector<int>(N);
	//auto b = valarray<int>(N);
	auto c = array<int, N>();

	f(pdarray<int>(a));
	//f(b);
	f(pdarray<int>(c));

	h(pdarray<const int>(a));
	h(pdarray<const int>(c));

	g(pdarray<int, 2>({W, H}, a.data()));
	g(pdarray<int, 2>({W, H}, c.data()));

	auto d = darray<int>();
	auto e = sarray<int, 5>();
	auto M = sarray<int, 2, 3>();

	std::array<size_t, 2> sizes = {2, 3};
	auto f = darray<float, 2>(sizes);
	auto g = f;

	begin(e);
	end(e);
	data(e);
	size(e);

	begin(f);
	end(f);
	data(f);
	size(f);

	for (auto x : f)
	{
		std::ignore = x;
	}

	for (size_t y = 0; y < f.extent1(); ++y)
		for (size_t x = 0; x < f.extent0(); ++x)
			f(x, y);

	M.size();
	M.rank();
	M.extent0();
	M.extent1();

	// Row-major order for images.
	for (size_t y = 0; y < M.extent1(); ++y)
		for (size_t x = 0; x < M.extent0(); ++x)
			M(x, y);

	// Column-major order for matrices.
	for (size_t col = 0; col < M.extent1(); ++col)
		for (size_t row = 0; row < M.extent0(); ++row)
			M(row, col);

	testVectors();
	/*
	testArithmeticStaticArrays();
	testArithmeticDynamicArrays();
	testArithmeticPointerArrays();
	testArithmeticMixedArrays();
	*/
	cout << "Press enter to quit." << endl;
	cin.get();

	//sarray<int, 3, 3, 3, 3, 6> magnus;
	//magnus.get_element(magnus, 1, 1, 1, 1, 1);
}
<commit_msg>Add todo comments<commit_after>#include <iostream>
#include <vector>
#include <array>
#include <valarray>

#include "gal.hpp"

#include "test_construction.hpp"
#include "test_construction_from_std.hpp"
#include "test_arithmetic.hpp"

#include "test_geometry.hpp"

/*
* Lgg till transpose-funktion.

* sliced_array, med stride, lgg till. Finns det nn kort bokstav fr det?
  Vill man bara ha strided fr pointer arrays? Kan heta spsarray och spdarray.

Frgor
* copy-construction frn samma typ. Hanteras det av de templatiserade konstruktrerna?
* Single-dimensional construction from:
  - Eigen::Matrix

* Implicit konvertering? Fr gande arrayer: Nej. Fr pekare ?

* Hur ska nullptr hanteras? Ska man krva att size()==0 d?

* make_pdarray({1,2,4}, data); // S man slipper redundanta template argument.
  make_darray({1,2}, 0.f);
  make_sarray({1, 2}, 1.f);

* Hur ska multiplikation och division hanteras? Vill kunna hantera fallen:
  - Elementvis
  - Matriser
  - Komplexa tal
  - Kvaternioner
  - Expression templates
* Ha mnga elementvisa operationer i annan modul: * / % < <= > >= && ! ||.
* implementera som allmnna funktioner: add, subtract, multiply, divide.

* storage order fr matriser.
- Skulle kunna ha allmn default-templatiserad flagga i class-signature.
-  Eller kan lta den bara pverka operator() !.
D kan man stta den typ:
using matrix23f = sarray<float,2,3,ROW_MAJOR>
- Skapa matris-klass som rver frn sarray? Vore ju bttre om man inte behvde rva frn den.
- Skapa tv fria funktioner fr operator(), som kan vlja mellan att plocka ut i row-major eller column major.
at_cm(matrix, 3, 2)
at_rm(matrix, 2, 3)
at_cm<3,2>(matrix)
at_rm<2,3>(matrix)

* Hur ska konstruktorerna vara fr att dom ska vara konsekventa fr alla arrayer?
  - typ, dimension, storlek, vrden.

auto a = sarray<int, 2, 3>(1, 2, 3, 4, 5, 6);   // <- funkar nog inte det bra fr matriser i den column/row-order som vi har?
auto a = sarray<int, 2, 3>({1, 2, 3, 4, 5, 6}); // <-
auto a = sarray<int, 2, 3>({{1, 2, 3}, {4, 5, 6}}); // <-
auto a = sarray<int, 2, 3>(data_iterator);
auto a = sarray<int, 2, 3>(data_value);
auto a = sarray<int, 2, 3>(data_array);

auto a = darray<int, 2>({2, 3}, data_iterator); // Tillt iterator istllet fr pekare?
auto a = darray<int, 2>({2, 3}, data_value);
auto a = darray<int, 2>({2, 3});
auto a = darray<int, 2>(data_array);

auto a = pdarray<int, 2>({2, 3}, data_iterator);
auto a = pdarray<int, 2>(data_array);

auto a = psarray<int, 2, 3>(data_iterator);
auto a = psarray<int, 2, 3>(data_array);

auto a = std::array<int, 2>({1, 2})
auto a = std::vector<int>(size)
auto a = std::vector<int>(size, data_value)
auto a = std::valarray<int>(size)
auto a = std::valarray<int>(data_value, size) <- exception

assert(extents(array1) == extents(array2))
*/

void f(pdarray<int> a)
{
	for (auto& x : a)
	{
		++x;
	}
}

void h(pdarray<const int> a)
{
	using namespace std;
	for (auto& x : a)
	{
		cout << x << " ";
	}
	cout << endl;
}

void g(pdarray<int, 2> a)
{
	for (auto& x : a)
	{
		++x;
	}

	for (size_t y = 0; y < a.extent1(); ++y)
	{
		for (size_t x = 0; x < a.extent0(); ++x)
		{
			std::cout << a(x, y) << " ";
		}
		std::cout << std::endl;
	}
}

int main()
{
	using namespace std;

	cout << "total_size: " << total_size<1, 2, 3>::value << endl;

	cout << "get_extent0: " << get_size<0, 8, 9, 6, 5>::value << endl;
	cout << "get_extent1: " << get_size<1, 8, 9, 6, 5>::value << endl;
	cout << "get_extent2: " << get_size<2, 8, 9, 6, 5>::value << endl;
	cout << "get_extent3: " << get_size<3, 8, 9, 6, 5>::value << endl;

	const auto W = size_t(4);
	const auto H = size_t(3);
	const auto N = W * H;

	auto a = vector<int>(N);
	//auto b = valarray<int>(N);
	auto c = array<int, N>();

	f(pdarray<int>(a));
	//f(b);
	f(pdarray<int>(c));

	h(pdarray<const int>(a));
	h(pdarray<const int>(c));

	g(pdarray<int, 2>({W, H}, a.data()));
	g(pdarray<int, 2>({W, H}, c.data()));

	auto d = darray<int>();
	auto e = sarray<int, 5>();
	auto M = sarray<int, 2, 3>();

	std::array<size_t, 2> sizes = {2, 3};
	auto f = darray<float, 2>(sizes);
	auto g = f;

	begin(e);
	end(e);
	data(e);
	size(e);

	begin(f);
	end(f);
	data(f);
	size(f);

	for (auto x : f)
	{
		std::ignore = x;
	}

	for (size_t y = 0; y < f.extent1(); ++y)
		for (size_t x = 0; x < f.extent0(); ++x)
			f(x, y);

	M.size();
	M.rank();
	M.extent0();
	M.extent1();

	// Row-major order for images.
	for (size_t y = 0; y < M.extent1(); ++y)
		for (size_t x = 0; x < M.extent0(); ++x)
			M(x, y);

	// Column-major order for matrices.
	for (size_t col = 0; col < M.extent1(); ++col)
		for (size_t row = 0; row < M.extent0(); ++row)
			M(row, col);

	testVectors();
	/*
	testArithmeticStaticArrays();
	testArithmeticDynamicArrays();
	testArithmeticPointerArrays();
	testArithmeticMixedArrays();
	*/
	cout << "Press enter to quit." << endl;
	cin.get();

	//sarray<int, 3, 3, 3, 3, 6> magnus;
	//magnus.get_element(magnus, 1, 1, 1, 1, 1);
}
<|endoftext|>


<commit_before>#include "cinder/app/RendererGl.h"
#include "cinder/app/App.h"
#include "cinder/gl/gl.h"
#include "cinder/gl/scoped.h"
#include "cinder/params/Params.h"
#include "cinder/Log.h"
#include "cinder/PolyLine.h"

#include "cinder/osc/Osc.h"
#include "CinderOpenCV.h"
#include "BlobTracker.h"

#include "DepthSensor.h"
#include "Cinder-VNM/include/MiniConfig.h"
#include "Cinder-VNM/include/TextureHelper.h"

using namespace ci;
using namespace ci::app;

class KinServerApp : public App
{
public:
    void setup() override
    {
        const auto& args = getCommandLineArgs();
        readConfig();
        log::makeLogger<log::LoggerFile>();

        {
            mParams = createConfigUI({ 400, 600 });
            std::vector<string> smoothNames = { "Off", "Light", "Middle", "High" };
            ADD_ENUM_TO_INT(mParams, TRACKING_SMOOTH, smoothNames);

            mParams->addParam("FPS", &mFps, true);
            mParams->addButton("Set Bg", std::bind(&KinServerApp::updateBack, this));
            mParams->addButton("Reset In/Out", [] {
                INPUT_X1 = INPUT_Y1 = OUTPUT_X1 = OUTPUT_Y1 = 0;
                INPUT_X2 = INPUT_Y2 = OUTPUT_X2 = OUTPUT_Y2 = 1;
            });
        }

        ds::DeviceType type = ds::DeviceType(_SENSOR_TYPE);
        ds::Option option;
        option.enableDepth = !_INFRARED_MODE;
        option.enableInfrared = _INFRARED_MODE;
        mDevice = ds::Device::create(type, option);
        if (!mDevice->isValid())
        {
            quit();
            return;
        }

        if (_INFRARED_MODE)
        {
            mDirtyConnection = mDevice->signalInfraredDirty.connect(std::bind(&KinServerApp::updateDepthRelated, this));
        }
        else
        {
            mDirtyConnection = mDevice->signalDepthDirty.connect(std::bind(&KinServerApp::updateDepthRelated, this));
        }

        mOscSender = std::make_shared<osc::SenderUdp>(10000, _ADDRESS, _TUIO_PORT);
        mOscSender->bind();

        getWindow()->setSize(1024, 768);

        try
        {
            mLogo = gl::Texture::create(loadImage(loadAsset("logo.png")));
        }
        catch (std::exception& e)
        {
            console() << e.what() << std::endl;
        }

        try
        {
            mShader = gl::GlslProg::create(loadAsset("depthMap.vs"), loadAsset("depthMap.fs"));
            mShader->uniform("image", 0);
        }
        catch (std::exception& e)
        {
            console() << e.what() << std::endl;
        }
    }

    void resize() override
    {
        mLayout.width = getWindowWidth();
        mLayout.height = getWindowHeight();
        mLayout.halfW = mLayout.width / 2;
        mLayout.halfH = mLayout.height / 2;
        mLayout.spc = mLayout.width * 0.04;

        for (int x = 0; x < 2; x++)
        {
            for (int y = 0; y < 2; y++)
            {
                mLayout.canvases[y * 2 + x] = Rectf(
                    mLayout.spc + mLayout.halfW * x,
                    mLayout.spc + mLayout.halfH * y,
                    mLayout.halfW * (1 + x) - mLayout.spc,
                    mLayout.halfH * (1 + y) - mLayout.spc
                    );
            }
        }
        if (mLogo)
        {
            mLayout.logoRect = Rectf(
                mLayout.halfW + mLayout.spc,
                mLayout.spc,
                mLayout.width - mLayout.spc,
                mLayout.spc + (mLayout.halfW - mLayout.spc * 2) / mLogo->getAspectRatio()
                );
        }

        mParams->setPosition(mLayout.canvases[1].getUpperLeft());
    }

    void draw() override
    {
        gl::clear(ColorA::gray(0.5f));

        if (mLogo)
        {
            gl::enableAlphaBlending();
            gl::draw(mLogo, mLayout.logoRect);
            gl::disableAlphaBlending();
        }

        if (mDepthW == 0) return;

        if (mDepthTexture)
        {
            gl::ScopedGlslProg prog(mShader);
            gl::ScopedTextureBind tex0(mDepthTexture);
            gl::drawSolidRect(mLayout.canvases[0]);
            gl::ScopedTextureBind tex1(mBackTexture);
            gl::drawSolidRect(mLayout.canvases[3]);
            gl::ScopedTextureBind tex2(mDiffTexture);
            gl::drawSolidRect(mLayout.canvases[2]);
        }
        visualizeBlobs(mBlobTracker);
    }

    void keyUp(KeyEvent event) override
    {
        int code = event.getCode();
        if (code == KeyEvent::KEY_ESCAPE)
        {
            quit();
        }
    }

    void update() override
    {
        if (mDepthW == 0) return;

        mFps = getAverageFps();

        mInputRoi.set(
            INPUT_X1 * mDepthW,
            INPUT_Y1 * mDepthH,
            INPUT_X2 * mDepthW,
            INPUT_Y2 * mDepthH
            );
        mOutputMap.set(
            OUTPUT_X1 * mDepthW,
            OUTPUT_Y1 * mDepthH,
            OUTPUT_X2 * mDepthW,
            OUTPUT_Y2 * mDepthH
            );
    }

private:

    void updateDepthRelated()
    {
        if (mDepthW == 0)
        {
            mDepthW = mDevice->getDepthSize().x;
            mDepthH = mDevice->getDepthSize().y;

            mDiffMat = cv::Mat1b(mDepthH, mDepthW);
            mDiffChannel = Channel(mDepthW, mDepthH, mDiffMat.step, 1,
                mDiffMat.ptr());
        }

        mTargetChannel = _INFRARED_MODE ? &mDevice->infraredChannel : &mDevice->depthChannel;

        updateTexture(mDepthTexture, *mTargetChannel);

        if (!mBackTexture)
        {
            updateBack();
        }

        mDiffMat.setTo(cv::Scalar::all(0));

        int cx = CENTER_X * mDepthW;
        int cy = CENTER_Y * mDepthH;
        int radius = RADIUS * mDepthH;
        int radius_sq = radius * radius;

        float depthToMmScale = _INFRARED_MODE ? 0.01 : mDevice->getDepthToMmScale();
        float minThresholdInDepthUnit = MIN_THRESHOLD_MM / depthToMmScale;
        float maxThresholdInDepthUnit = MAX_THRESHOLD_MM / depthToMmScale;

        for (int yy = mInputRoi.y1; yy < mInputRoi.y2; yy++)
        {
            // TODO: cache row pointer
            int y = yy;
            for (int xx = mInputRoi.x1; xx < mInputRoi.x2; xx++)
            {
                int x = LEFT_RIGHT_FLIPPED ? (mDepthW - xx) : xx;
                auto bg = *mBackChannel.getData(x, y);
                auto dep = *mTargetChannel->getData(x, y);
                auto diff = dep - bg;
                if (dep > 0 && diff > minThresholdInDepthUnit && diff < maxThresholdInDepthUnit)
                {
                    // TODO: optimize
                    if (!CIRCLE_MASK_ENABLED || (cx - x) * (cx - x) + (cy - y) * (cy - y) < radius_sq)
                    {
                        mDiffMat(yy, xx) = 255;
                    }
                }
            }
        }

        if (TRACKING_SMOOTH > 0)
        {
            cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(TRACKING_SMOOTH * 2 + 1, TRACKING_SMOOTH * 2 + 1),
                cv::Point(TRACKING_SMOOTH, TRACKING_SMOOTH));
            cv::morphologyEx(mDiffMat, mDiffMat, cv::MORPH_OPEN, element);
        }

        updateTexture(mDiffTexture, mDiffChannel);
        std::vector<Blob> blobs;
        BlobFinder::Option option;
        option.minArea = MIN_AREA;
        option.handOnlyMode = FINGER_MODE_ENABLED;
        option.handDistance = FINGER_SIZE;
        BlobFinder::execute(mDiffMat, blobs, option);
        mBlobTracker.trackBlobs(blobs);
        sendTuioMessage(*mOscSender, mBlobTracker);
    }

    void visualizeBlobs(const BlobTracker &blobTracker)
    {
        static uint8_t sPalette[][3] =
        {
            { 255, 0, 0 },
            { 122, 0, 0 },
            { 255, 255, 0 },
            { 122, 122, 0 },
            { 255, 0, 255 },
            { 122, 0, 122 },
            { 0, 0, 255 },
            { 0, 0, 122 },
            { 0, 255, 255 },
            { 0, 122, 122 },
        };
        const size_t sPaletteCount = _countof(sPalette);

        vec2 scale;
        scale.x = (mLayout.halfW - mLayout.spc * 2) / mDepthW;
        scale.y = (mLayout.halfH - mLayout.spc * 2) / mDepthH;
        gl::pushModelMatrix();
        gl::translate(mLayout.canvases[2].getUpperLeft());
        gl::scale(scale);

        if (CIRCLE_MASK_ENABLED)
        {
            float cx = CENTER_X * mDepthW;
            if (LEFT_RIGHT_FLIPPED) cx = mDepthW - cx;
            float cy = CENTER_Y * mDepthH;
            float radius = RADIUS * mDepthH;
            gl::drawStrokedCircle(vec2(cx, cy), radius, 40);
        }
        {
            gl::ScopedColor scope(ColorAf(1, 0, 0, 0.5f));
            gl::drawStrokedRect(mInputRoi);
        }
        {
            gl::ScopedColor scope(ColorAf(0, 1, 0, 0.5f));
            gl::drawStrokedRect(mOutputMap);
        }

        char idName[10];
        for (const auto &blob : blobTracker.trackedBlobs)
        {
            int idx = blob.id % sPaletteCount;
            gl::color(Color8u(sPalette[idx][0], sPalette[idx][1], sPalette[idx][2]));
            PolyLine2 line;
            for (const auto &pt : blob.pts)
            {
                line.push_back(vec2(pt.x, pt.y));
            }
            line.setClosed();
            gl::drawSolid(line);
            sprintf(idName, "#%d", blob.id);
            gl::drawStringCentered(idName, vec2(blob.center.x, blob.center.y));
        }
        gl::color(Color::white());
        gl::popModelMatrix();
    }

    int remapTuioId(int srcId)
    {
#define kMagicNumber 100
        return (srcId % kMagicNumber) + kMagicNumber * SERVER_ID;
    }

    void sendTuioMessage(osc::SenderUdp &sender, const BlobTracker &blobTracker)
    {
        osc::Bundle bundle;

        osc::Message alive;
        {
            alive.setAddress("/tuio/2Dcur");
            alive.append("alive");
        }

        osc::Message fseq;
        {
            fseq.setAddress("/tuio/2Dcur");
            fseq.append("fseq");
            fseq.append((int32_t)getElapsedFrames());
        }

        SERVER_COUNT = math<int>::max(1, SERVER_COUNT);
        SERVER_ID = math<int>::clamp(SERVER_ID, 0, SERVER_COUNT - 1);
        float newRegion = 1 / (float)SERVER_COUNT;
        for (const auto &blob : blobTracker.trackedBlobs)
        {
            //Point2f center(blob.center.x + depthOrigin.x, blob.center.y + depthOrigin.y);
            vec2 center(blob.center.x, blob.center.y);

            if (!mInputRoi.contains(center)) continue;

            int blobId = remapTuioId(blob.id);
            osc::Message set;
            set.setAddress("/tuio/2Dcur");
            set.append("set");
            set.append(blobId);             // id
            float mappedX = lmap(center.x / mDepthW, INPUT_X1, INPUT_X2, OUTPUT_X1, OUTPUT_X2);
            mappedX = (SERVER_ID + mappedX) * newRegion;
            float mappedY = lmap(center.y / mDepthH, INPUT_Y1, INPUT_Y2, OUTPUT_Y1, OUTPUT_Y2);
            set.append(mappedX);
            set.append(mappedY);
            set.append(blob.velocity.x / mOutputMap.getWidth());
            set.append(blob.velocity.y / mOutputMap.getHeight());
            set.append(0);     // m
            bundle.append(set);                         // add message to bundle

            alive.append(blobId);               // add blob to list of ALL active IDs
        }

        bundle.append(alive);    //add message to bundle
        bundle.append(fseq);     //add message to bundle

        sender.send(bundle); //send bundle
    }

    void updateBack()
    {
        mBackChannel = mTargetChannel->clone();
        updateTexture(mBackTexture, mBackChannel);
    }

    float mFps = 0;

    struct Layout
    {
        float width;
        float height;
        float halfW;
        float halfH;
        float spc;

        Rectf canvases[4];
        Rectf logoRect;
    } mLayout;

    ci::Channel16u* mTargetChannel = nullptr;
    ds::DeviceRef mDevice;
    signals::Connection mDirtyConnection;
    params::InterfaceGlRef mParams;
    std::shared_ptr<osc::SenderUdp> mOscSender;
    int mDepthW = 0;
    int mDepthH = 0;

    gl::TextureRef mDepthTexture;

    // vision
    Channel16u mBackChannel;
    gl::TextureRef mBackTexture;
    BlobTracker mBlobTracker;

    Channel mDiffChannel;
    cv::Mat1b mDiffMat;
    gl::TextureRef mDiffTexture;

    Rectf mInputRoi;
    Rectf mOutputMap;

    gl::TextureRef mLogo;

    gl::GlslProgRef	mShader;
};

CINDER_APP(KinServerApp, RendererGl)
<commit_msg>Fix bug when _INFRARED_MODE is false.<commit_after>#include "cinder/app/RendererGl.h"
#include "cinder/app/App.h"
#include "cinder/gl/gl.h"
#include "cinder/gl/scoped.h"
#include "cinder/params/Params.h"
#include "cinder/Log.h"
#include "cinder/PolyLine.h"

#include "cinder/osc/Osc.h"
#include "CinderOpenCV.h"
#include "BlobTracker.h"

#include "DepthSensor.h"
#include "Cinder-VNM/include/MiniConfig.h"
#include "Cinder-VNM/include/TextureHelper.h"

using namespace ci;
using namespace ci::app;

class KinServerApp : public App
{
public:
    void setup() override
    {
        const auto& args = getCommandLineArgs();
        readConfig();
        log::makeLogger<log::LoggerFile>();

        {
            mParams = createConfigUI({ 400, 600 });
            std::vector<string> smoothNames = { "Off", "Light", "Middle", "High" };
            ADD_ENUM_TO_INT(mParams, TRACKING_SMOOTH, smoothNames);

            mParams->addParam("FPS", &mFps, true);
            mParams->addButton("Set Bg", std::bind(&KinServerApp::updateBack, this));
            mParams->addButton("Reset In/Out", [] {
                INPUT_X1 = INPUT_Y1 = OUTPUT_X1 = OUTPUT_Y1 = 0;
                INPUT_X2 = INPUT_Y2 = OUTPUT_X2 = OUTPUT_Y2 = 1;
            });
        }

        ds::DeviceType type = ds::DeviceType(_SENSOR_TYPE);
        ds::Option option;
        option.enableDepth = !_INFRARED_MODE;
        option.enableInfrared = _INFRARED_MODE;
        mDevice = ds::Device::create(type, option);
        if (!mDevice->isValid())
        {
            quit();
            return;
        }

        if (_INFRARED_MODE)
        {
            mDirtyConnection = mDevice->signalInfraredDirty.connect(std::bind(&KinServerApp::updateDepthRelated, this));
        }
        else
        {
            mDirtyConnection = mDevice->signalDepthDirty.connect(std::bind(&KinServerApp::updateDepthRelated, this));
        }

        mOscSender = std::make_shared<osc::SenderUdp>(10000, _ADDRESS, _TUIO_PORT);
        mOscSender->bind();

        getWindow()->setSize(1024, 768);

        try
        {
            mLogo = gl::Texture::create(loadImage(loadAsset("logo.png")));
        }
        catch (std::exception& e)
        {
            console() << e.what() << std::endl;
        }

        try
        {
            mShader = gl::GlslProg::create(loadAsset("depthMap.vs"), loadAsset("depthMap.fs"));
            mShader->uniform("image", 0);
        }
        catch (std::exception& e)
        {
            console() << e.what() << std::endl;
        }
    }

    void resize() override
    {
        mLayout.width = getWindowWidth();
        mLayout.height = getWindowHeight();
        mLayout.halfW = mLayout.width / 2;
        mLayout.halfH = mLayout.height / 2;
        mLayout.spc = mLayout.width * 0.04;

        for (int x = 0; x < 2; x++)
        {
            for (int y = 0; y < 2; y++)
            {
                mLayout.canvases[y * 2 + x] = Rectf(
                    mLayout.spc + mLayout.halfW * x,
                    mLayout.spc + mLayout.halfH * y,
                    mLayout.halfW * (1 + x) - mLayout.spc,
                    mLayout.halfH * (1 + y) - mLayout.spc
                    );
            }
        }
        if (mLogo)
        {
            mLayout.logoRect = Rectf(
                mLayout.halfW + mLayout.spc,
                mLayout.spc,
                mLayout.width - mLayout.spc,
                mLayout.spc + (mLayout.halfW - mLayout.spc * 2) / mLogo->getAspectRatio()
                );
        }

        mParams->setPosition(mLayout.canvases[1].getUpperLeft());
    }

    void draw() override
    {
        gl::clear(ColorA::gray(0.5f));

        if (mLogo)
        {
            gl::enableAlphaBlending();
            gl::draw(mLogo, mLayout.logoRect);
            gl::disableAlphaBlending();
        }

        if (mDepthW == 0) return;

        if (mDepthTexture)
        {
            gl::ScopedGlslProg prog(mShader);
            gl::ScopedTextureBind tex0(mDepthTexture);
            gl::drawSolidRect(mLayout.canvases[0]);
            gl::ScopedTextureBind tex1(mBackTexture);
            gl::drawSolidRect(mLayout.canvases[3]);
            gl::ScopedTextureBind tex2(mDiffTexture);
            gl::drawSolidRect(mLayout.canvases[2]);
        }
        visualizeBlobs(mBlobTracker);
    }

    void keyUp(KeyEvent event) override
    {
        int code = event.getCode();
        if (code == KeyEvent::KEY_ESCAPE)
        {
            quit();
        }
    }

    void update() override
    {
        if (mDepthW == 0) return;

        mFps = getAverageFps();

        mInputRoi.set(
            INPUT_X1 * mDepthW,
            INPUT_Y1 * mDepthH,
            INPUT_X2 * mDepthW,
            INPUT_Y2 * mDepthH
            );
        mOutputMap.set(
            OUTPUT_X1 * mDepthW,
            OUTPUT_Y1 * mDepthH,
            OUTPUT_X2 * mDepthW,
            OUTPUT_Y2 * mDepthH
            );
    }

private:

    void updateDepthRelated()
    {
        if (mDepthW == 0)
        {
            mDepthW = mDevice->getDepthSize().x;
            mDepthH = mDevice->getDepthSize().y;

            mDiffMat = cv::Mat1b(mDepthH, mDepthW);
            mDiffChannel = Channel(mDepthW, mDepthH, mDiffMat.step, 1,
                mDiffMat.ptr());
        }

        mTargetChannel = _INFRARED_MODE ? &mDevice->infraredChannel : &mDevice->depthChannel;

        updateTexture(mDepthTexture, *mTargetChannel);

        if (!mBackTexture)
        {
            updateBack();
        }

        mDiffMat.setTo(cv::Scalar::all(0));

        int cx = CENTER_X * mDepthW;
        int cy = CENTER_Y * mDepthH;
        int radius = RADIUS * mDepthH;
        int radius_sq = radius * radius;

        float depthToMmScale = _INFRARED_MODE ? 0.01 : mDevice->getDepthToMmScale();
        float minThresholdInDepthUnit = MIN_THRESHOLD_MM / depthToMmScale;
        float maxThresholdInDepthUnit = MAX_THRESHOLD_MM / depthToMmScale;

        uint16_t diff;

        for (int yy = mInputRoi.y1; yy < mInputRoi.y2; yy++)
        {
            // TODO: cache row pointer
            int y = yy;
            for (int xx = mInputRoi.x1; xx < mInputRoi.x2; xx++)
            {
                int x = LEFT_RIGHT_FLIPPED ? (mDepthW - xx) : xx;
                auto dep = *mTargetChannel->getData(x, y);
                if (dep > 0)
                {
                    auto bg = *mBackChannel.getData(x, y);
                    if (_INFRARED_MODE) diff = dep - bg;
                    else diff = bg - dep;
                    if (diff <= minThresholdInDepthUnit || diff >= maxThresholdInDepthUnit) continue;
                    // TODO: optimize
                    if (!CIRCLE_MASK_ENABLED || (cx - x) * (cx - x) + (cy - y) * (cy - y) < radius_sq)
                    {
                        mDiffMat(yy, xx) = 255;
                    }
                }
            }
        }

        if (TRACKING_SMOOTH > 0)
        {
            cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(TRACKING_SMOOTH * 2 + 1, TRACKING_SMOOTH * 2 + 1),
                cv::Point(TRACKING_SMOOTH, TRACKING_SMOOTH));
            cv::morphologyEx(mDiffMat, mDiffMat, cv::MORPH_OPEN, element);
        }

        updateTexture(mDiffTexture, mDiffChannel);
        std::vector<Blob> blobs;
        BlobFinder::Option option;
        option.minArea = MIN_AREA;
        option.handOnlyMode = FINGER_MODE_ENABLED;
        option.handDistance = FINGER_SIZE;
        BlobFinder::execute(mDiffMat, blobs, option);
        mBlobTracker.trackBlobs(blobs);
        sendTuioMessage(*mOscSender, mBlobTracker);
    }

    void visualizeBlobs(const BlobTracker &blobTracker)
    {
        static uint8_t sPalette[][3] =
        {
            { 255, 0, 0 },
            { 122, 0, 0 },
            { 255, 255, 0 },
            { 122, 122, 0 },
            { 255, 0, 255 },
            { 122, 0, 122 },
            { 0, 0, 255 },
            { 0, 0, 122 },
            { 0, 255, 255 },
            { 0, 122, 122 },
        };
        const size_t sPaletteCount = _countof(sPalette);

        vec2 scale;
        scale.x = (mLayout.halfW - mLayout.spc * 2) / mDepthW;
        scale.y = (mLayout.halfH - mLayout.spc * 2) / mDepthH;
        gl::pushModelMatrix();
        gl::translate(mLayout.canvases[2].getUpperLeft());
        gl::scale(scale);

        if (CIRCLE_MASK_ENABLED)
        {
            float cx = CENTER_X * mDepthW;
            if (LEFT_RIGHT_FLIPPED) cx = mDepthW - cx;
            float cy = CENTER_Y * mDepthH;
            float radius = RADIUS * mDepthH;
            gl::drawStrokedCircle(vec2(cx, cy), radius, 40);
        }
        {
            gl::ScopedColor scope(ColorAf(1, 0, 0, 0.5f));
            gl::drawStrokedRect(mInputRoi);
        }
        {
            gl::ScopedColor scope(ColorAf(0, 1, 0, 0.5f));
            gl::drawStrokedRect(mOutputMap);
        }

        char idName[10];
        for (const auto &blob : blobTracker.trackedBlobs)
        {
            int idx = blob.id % sPaletteCount;
            gl::color(Color8u(sPalette[idx][0], sPalette[idx][1], sPalette[idx][2]));
            PolyLine2 line;
            for (const auto &pt : blob.pts)
            {
                line.push_back(vec2(pt.x, pt.y));
            }
            line.setClosed();
            gl::drawSolid(line);
            sprintf(idName, "#%d", blob.id);
            gl::drawStringCentered(idName, vec2(blob.center.x, blob.center.y));
        }
        gl::color(Color::white());
        gl::popModelMatrix();
    }

    int remapTuioId(int srcId)
    {
#define kMagicNumber 100
        return (srcId % kMagicNumber) + kMagicNumber * SERVER_ID;
    }

    void sendTuioMessage(osc::SenderUdp &sender, const BlobTracker &blobTracker)
    {
        osc::Bundle bundle;

        osc::Message alive;
        {
            alive.setAddress("/tuio/2Dcur");
            alive.append("alive");
        }

        osc::Message fseq;
        {
            fseq.setAddress("/tuio/2Dcur");
            fseq.append("fseq");
            fseq.append((int32_t)getElapsedFrames());
        }

        SERVER_COUNT = math<int>::max(1, SERVER_COUNT);
        SERVER_ID = math<int>::clamp(SERVER_ID, 0, SERVER_COUNT - 1);
        float newRegion = 1 / (float)SERVER_COUNT;
        for (const auto &blob : blobTracker.trackedBlobs)
        {
            //Point2f center(blob.center.x + depthOrigin.x, blob.center.y + depthOrigin.y);
            vec2 center(blob.center.x, blob.center.y);

            if (!mInputRoi.contains(center)) continue;

            int blobId = remapTuioId(blob.id);
            osc::Message set;
            set.setAddress("/tuio/2Dcur");
            set.append("set");
            set.append(blobId);             // id
            float mappedX = lmap(center.x / mDepthW, INPUT_X1, INPUT_X2, OUTPUT_X1, OUTPUT_X2);
            mappedX = (SERVER_ID + mappedX) * newRegion;
            float mappedY = lmap(center.y / mDepthH, INPUT_Y1, INPUT_Y2, OUTPUT_Y1, OUTPUT_Y2);
            set.append(mappedX);
            set.append(mappedY);
            set.append(blob.velocity.x / mOutputMap.getWidth());
            set.append(blob.velocity.y / mOutputMap.getHeight());
            set.append(0);     // m
            bundle.append(set);                         // add message to bundle

            alive.append(blobId);               // add blob to list of ALL active IDs
        }

        bundle.append(alive);    //add message to bundle
        bundle.append(fseq);     //add message to bundle

        sender.send(bundle); //send bundle
    }

    void updateBack()
    {
        mBackChannel = mTargetChannel->clone();
        updateTexture(mBackTexture, mBackChannel);
    }

    float mFps = 0;

    struct Layout
    {
        float width;
        float height;
        float halfW;
        float halfH;
        float spc;

        Rectf canvases[4];
        Rectf logoRect;
    } mLayout;

    ci::Channel16u* mTargetChannel = nullptr;
    ds::DeviceRef mDevice;
    signals::Connection mDirtyConnection;
    params::InterfaceGlRef mParams;
    std::shared_ptr<osc::SenderUdp> mOscSender;
    int mDepthW = 0;
    int mDepthH = 0;

    gl::TextureRef mDepthTexture;

    // vision
    Channel16u mBackChannel;
    gl::TextureRef mBackTexture;
    BlobTracker mBlobTracker;

    Channel mDiffChannel;
    cv::Mat1b mDiffMat;
    gl::TextureRef mDiffTexture;

    Rectf mInputRoi;
    Rectf mOutputMap;

    gl::TextureRef mLogo;

    gl::GlslProgRef	mShader;
};

CINDER_APP(KinServerApp, RendererGl)
<|endoftext|>


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

#ifndef MAPNIK_SPAN_IMAGE_FILTER_INCLUDED
#define MAPNIK_SPAN_IMAGE_FILTER_INCLUDED

#include "agg_span_image_filter_gray.h"
#include "agg_span_image_filter_rgba.h"

#include <boost/optional.hpp>

#include <limits>

namespace mapnik
{

template<class Source>
class span_image_resample_gray_affine : public agg::span_image_resample_affine<Source>
{
public:
    using source_type = Source;
    using color_type = typename source_type::color_type;
    using base_type = agg::span_image_resample_affine<source_type>;
    using interpolator_type = typename base_type::interpolator_type;
    using value_type = typename color_type::value_type;
    using long_type = typename color_type::long_type;

    enum base_scale_e
    {
        downscale_shift = agg::image_filter_shift,
        base_mask       = color_type::base_mask
    };

    span_image_resample_gray_affine(source_type & src,
                                    interpolator_type & inter,
                                    agg::image_filter_lut const & filter,
                                    boost::optional<value_type> const & nodata_value) :
        base_type(src, inter, filter),
        nodata_value_(nodata_value)
    { }

    void generate(color_type* span, int x, int y, unsigned len)
    {
        base_type::interpolator().begin(x + base_type::filter_dx_dbl(),
                                        y + base_type::filter_dy_dbl(), len);

        long_type fg;

        int diameter     = base_type::filter().diameter();
        int filter_scale = diameter << agg::image_subpixel_shift;
        int radius_x     = (diameter * base_type::m_rx) >> 1;
        int radius_y     = (diameter * base_type::m_ry) >> 1;
        int len_x_lr     =
            (diameter * base_type::m_rx + agg::image_subpixel_mask) >>
                agg::image_subpixel_shift;

        const agg::int16* weight_array = base_type::filter().weight_array();

        do
        {
            base_type::interpolator().coordinates(&x, &y);

            if (nodata_value_)
            {
                int src_x = x >> agg::image_subpixel_shift;
                int src_y = y >> agg::image_subpixel_shift;
                const value_type* pix = reinterpret_cast<const value_type*>(base_type::source().span(src_x, src_y, 1));
                if (*nodata_value_ == *pix)
                {
                    span->v = *nodata_value_;
                    span->a = base_mask;
                    ++span;
                    ++base_type::interpolator();
                    continue;
                }
            }

            x += base_type::filter_dx_int() - radius_x;
            y += base_type::filter_dy_int() - radius_y;

            fg = 0;

            int y_lr = y >> agg::image_subpixel_shift;
            int y_hr = ((agg::image_subpixel_mask - (y & agg::image_subpixel_mask)) *
                            base_type::m_ry_inv) >>
                                agg::image_subpixel_shift;
            int total_weight = 0;
            int x_lr = x >> agg::image_subpixel_shift;
            int x_hr = ((agg::image_subpixel_mask - (x & agg::image_subpixel_mask)) *
                            base_type::m_rx_inv) >>
                                agg::image_subpixel_shift;

            int x_hr2 = x_hr;
            const value_type* fg_ptr = reinterpret_cast<const value_type*>(base_type::source().span(x_lr, y_lr, len_x_lr));
            for(;;)
            {
                int weight_y = weight_array[y_hr];
                x_hr = x_hr2;
                for(;;)
                {
                    int weight = (weight_y * weight_array[x_hr] +
                                 agg::image_filter_scale) >>
                                 downscale_shift;
                    if (!nodata_value_ || *nodata_value_ != *fg_ptr)
                    {
                        fg += *fg_ptr * weight;
                        total_weight += weight;
                    }
                    x_hr  += base_type::m_rx_inv;
                    if (x_hr >= filter_scale) break;
                    fg_ptr = reinterpret_cast<const value_type*>(base_type::source().next_x());
                }
                y_hr += base_type::m_ry_inv;
                if (y_hr >= filter_scale) break;
                fg_ptr = reinterpret_cast<const value_type*>(base_type::source().next_y());
            }

            if (total_weight == 0)
            {
                span->v = *nodata_value_;
            }
            else
            {
                fg /= total_weight;
                if (fg < std::numeric_limits<value_type>::min())
                {
                    span->v = std::numeric_limits<value_type>::min();
                }
                else if (fg > std::numeric_limits<value_type>::max())
                {
                    span->v = std::numeric_limits<value_type>::max();
                }
                else
                {
                    span->v = static_cast<value_type>(fg);
                }
            }

            span->a = base_mask;

            ++span;
            ++base_type::interpolator();
        } while(--len);
    }

private:
    boost::optional<value_type> nodata_value_;
};

template<class Source>
class span_image_resample_rgba_affine : public agg::span_image_resample_rgba_affine<Source>
{
public:
    using source_type = Source;
    using color_type = typename source_type::color_type;
    using order_type = typename source_type::order_type;
    using base_type = agg::span_image_resample_rgba_affine<source_type>;
    using interpolator_type = typename base_type::interpolator_type;
    using value_type = typename color_type::value_type;
    using long_type = typename color_type::long_type;

    span_image_resample_rgba_affine(source_type & src,
                                    interpolator_type & inter,
                                    agg::image_filter_lut const & filter,
                                    boost::optional<value_type> const & nodata_value) :
        agg::span_image_resample_rgba_affine<Source>(src, inter, filter)
    { }
};

}

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

#ifndef MAPNIK_SPAN_IMAGE_FILTER_INCLUDED
#define MAPNIK_SPAN_IMAGE_FILTER_INCLUDED

#include "agg_span_image_filter_gray.h"
#include "agg_span_image_filter_rgba.h"

#include <boost/optional.hpp>

#include <limits>

#include <mapnik/safe_cast.hpp>

namespace mapnik
{

template<class Source>
class span_image_resample_gray_affine : public agg::span_image_resample_affine<Source>
{
public:
    using source_type = Source;
    using color_type = typename source_type::color_type;
    using base_type = agg::span_image_resample_affine<source_type>;
    using interpolator_type = typename base_type::interpolator_type;
    using value_type = typename color_type::value_type;
    using long_type = typename color_type::long_type;

    enum base_scale_e
    {
        downscale_shift = agg::image_filter_shift,
        base_mask       = color_type::base_mask
    };

    span_image_resample_gray_affine(source_type & src,
                                    interpolator_type & inter,
                                    agg::image_filter_lut const & filter,
                                    boost::optional<value_type> const & nodata_value) :
        base_type(src, inter, filter),
        nodata_value_(nodata_value)
    { }

    void generate(color_type* span, int x, int y, unsigned len)
    {
        base_type::interpolator().begin(x + base_type::filter_dx_dbl(),
                                        y + base_type::filter_dy_dbl(), len);

        long_type fg;

        int diameter     = base_type::filter().diameter();
        int filter_scale = diameter << agg::image_subpixel_shift;
        int radius_x     = (diameter * base_type::m_rx) >> 1;
        int radius_y     = (diameter * base_type::m_ry) >> 1;
        int len_x_lr     =
            (diameter * base_type::m_rx + agg::image_subpixel_mask) >>
                agg::image_subpixel_shift;

        const agg::int16* weight_array = base_type::filter().weight_array();

        do
        {
            base_type::interpolator().coordinates(&x, &y);

            if (nodata_value_)
            {
                int src_x = x >> agg::image_subpixel_shift;
                int src_y = y >> agg::image_subpixel_shift;
                const value_type* pix = reinterpret_cast<const value_type*>(base_type::source().span(src_x, src_y, 1));
                if (*nodata_value_ == *pix)
                {
                    span->v = *nodata_value_;
                    span->a = base_mask;
                    ++span;
                    ++base_type::interpolator();
                    continue;
                }
            }

            x += base_type::filter_dx_int() - radius_x;
            y += base_type::filter_dy_int() - radius_y;

            fg = 0;

            int y_lr = y >> agg::image_subpixel_shift;
            int y_hr = ((agg::image_subpixel_mask - (y & agg::image_subpixel_mask)) *
                            base_type::m_ry_inv) >>
                                agg::image_subpixel_shift;
            int total_weight = 0;
            int x_lr = x >> agg::image_subpixel_shift;
            int x_hr = ((agg::image_subpixel_mask - (x & agg::image_subpixel_mask)) *
                            base_type::m_rx_inv) >>
                                agg::image_subpixel_shift;

            int x_hr2 = x_hr;
            const value_type* fg_ptr = reinterpret_cast<const value_type*>(base_type::source().span(x_lr, y_lr, len_x_lr));
            for(;;)
            {
                int weight_y = weight_array[y_hr];
                x_hr = x_hr2;
                for(;;)
                {
                    int weight = (weight_y * weight_array[x_hr] +
                                 agg::image_filter_scale) >>
                                 downscale_shift;
                    if (!nodata_value_ || *nodata_value_ != *fg_ptr)
                    {
                        fg += *fg_ptr * weight;
                        total_weight += weight;
                    }
                    x_hr  += base_type::m_rx_inv;
                    if (x_hr >= filter_scale) break;
                    fg_ptr = reinterpret_cast<const value_type*>(base_type::source().next_x());
                }
                y_hr += base_type::m_ry_inv;
                if (y_hr >= filter_scale) break;
                fg_ptr = reinterpret_cast<const value_type*>(base_type::source().next_y());
            }

            if (total_weight == 0)
            {
                span->v = *nodata_value_;
            }
            else
            {
                span->v = safe_cast<value_type>(fg / total_weight);
            }

            span->a = base_mask;

            ++span;
            ++base_type::interpolator();
        } while(--len);
    }

private:
    boost::optional<value_type> nodata_value_;
};

template<class Source>
class span_image_resample_rgba_affine : public agg::span_image_resample_rgba_affine<Source>
{
public:
    using source_type = Source;
    using color_type = typename source_type::color_type;
    using order_type = typename source_type::order_type;
    using base_type = agg::span_image_resample_rgba_affine<source_type>;
    using interpolator_type = typename base_type::interpolator_type;
    using value_type = typename color_type::value_type;
    using long_type = typename color_type::long_type;

    span_image_resample_rgba_affine(source_type & src,
                                    interpolator_type & inter,
                                    agg::image_filter_lut const & filter,
                                    boost::optional<value_type> const & nodata_value) :
        agg::span_image_resample_rgba_affine<Source>(src, inter, filter)
    { }
};

}

#endif
<|endoftext|>