<commit_before>//===- lib/Driver/WinLinkDriver.cpp ---------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// Concrete instance of the Driver for Windows link.exe.
///
//===----------------------------------------------------------------------===//

#include <cstdlib>

#include "llvm/ADT/StringSwitch.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Path.h"

#include "lld/Driver/Driver.h"
#include "lld/ReaderWriter/PECOFFTargetInfo.h"

namespace lld {

namespace {

// Create enum with OPT_xxx values for each option in WinLinkOptions.td
enum WinLinkOpt {
  OPT_INVALID = 0,
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, HELP, META) \
          OPT_##ID,
#include "WinLinkOptions.inc"
  LastOption
#undef OPTION
};

// Create prefix string literals used in WinLinkOptions.td
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
#include "WinLinkOptions.inc"
#undef PREFIX

// Create table mapping all options defined in WinLinkOptions.td
static const llvm::opt::OptTable::Info infoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
               HELPTEXT, METAVAR)   \
  { PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
    PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS },
#include "WinLinkOptions.inc"
#undef OPTION
};

// Create OptTable class for parsing actual command line arguments
class WinLinkOptTable : public llvm::opt::OptTable {
public:
  WinLinkOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){}
};

// Returns the index of "--" or -1 if not found.
int findDoubleDash(int argc, const char *argv[]) {
  for (int i = 0; i < argc; ++i)
    if (std::strcmp(argv[i], "--") == 0)
      return i;
  return -1;
}

// Displays error message if the given version does not match with
// /^\d+$/.
bool checkNumber(StringRef version, const char *errorMessage,
                 raw_ostream &diagnostics) {
  if (version.str().find_first_not_of("0123456789") != std::string::npos
      || version.empty()) {
    diagnostics << "error: " << errorMessage << version << "\n";
    return false;
  }
  return true;
}

// Parse an argument for -stack or -heap. The expected string is
// "reserveSize[,stackCommitSize]".
bool parseMemoryOption(const StringRef &arg, raw_ostream &diagnostics,
                       uint64_t &reserve, uint64_t &commit) {
  StringRef reserveStr, commitStr;
  llvm::tie(reserveStr, commitStr) = arg.split(',');
  if (!checkNumber(reserveStr, "invalid stack size: ", diagnostics))
    return false;
  reserve = atoi(reserveStr.str().c_str());
  if (!commitStr.empty()) {
    if (!checkNumber(commitStr, "invalid stack size: ", diagnostics))
      return false;
    commit = atoi(commitStr.str().c_str());
  }
  return true;
}

// Parse -stack command line option
bool parseStackOption(PECOFFTargetInfo &info, const StringRef &arg,
                      raw_ostream &diagnostics) {
  uint64_t reserve;
  uint64_t commit = info.getStackCommit();
  if (!parseMemoryOption(arg, diagnostics, reserve, commit))
    return false;
  info.setStackReserve(reserve);
  info.setStackCommit(commit);
  return true;
}

// Parse -heap command line option.
bool parseHeapOption(PECOFFTargetInfo &info, const StringRef &arg,
                     raw_ostream &diagnostics) {
  uint64_t reserve;
  uint64_t commit = info.getHeapCommit();
  if (!parseMemoryOption(arg, diagnostics, reserve, commit))
    return false;
  info.setHeapReserve(reserve);
  info.setHeapCommit(commit);
  return true;
}

// Returns subsystem type for the given string.
llvm::COFF::WindowsSubsystem stringToWinSubsystem(StringRef str) {
  std::string arg(str.lower());
  return llvm::StringSwitch<llvm::COFF::WindowsSubsystem>(arg)
      .Case("windows", llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI)
      .Case("console", llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI)
      .Default(llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN);
}

bool parseMinOSVersion(PECOFFTargetInfo &info, const StringRef &osVersion,
                       raw_ostream &diagnostics) {
  StringRef majorVersion, minorVersion;
  llvm::tie(majorVersion, minorVersion) = osVersion.split('.');
  if (minorVersion.empty())
    minorVersion = "0";
  if (!checkNumber(majorVersion, "invalid OS major version: ", diagnostics))
    return false;
  if (!checkNumber(minorVersion, "invalid OS minor version: ", diagnostics))
    return false;
  PECOFFTargetInfo::OSVersion minOSVersion(atoi(majorVersion.str().c_str()),
                                           atoi(minorVersion.str().c_str()));
  info.setMinOSVersion(minOSVersion);
  return true;
}

// Parse -subsystem command line option. The form of -subsystem is
// "subsystem_name[,majorOSVersion[.minorOSVersion]]".
bool parseSubsystemOption(PECOFFTargetInfo &info, std::string arg,
                          raw_ostream &diagnostics) {
  StringRef subsystemStr, osVersionStr;
  llvm::tie(subsystemStr, osVersionStr) = StringRef(arg).split(',');

  // Parse optional OS version if exists.
  if (!osVersionStr.empty())
    if (!parseMinOSVersion(info, osVersionStr, diagnostics))
      return false;

  // Parse subsystem name.
  llvm::COFF::WindowsSubsystem subsystem = stringToWinSubsystem(subsystemStr);
  if (subsystem == llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN) {
    diagnostics << "error: unknown subsystem name: " << subsystemStr << "\n";
    return false;
  }
  info.setSubsystem(subsystem);
  return true;
}

// Add ".obj" extension if the given path name has no file extension.
StringRef canonicalizeInputFileName(PECOFFTargetInfo &info, std::string path) {
  if (llvm::sys::path::extension(path).empty())
    path.append(".obj");
  return info.allocateString(path);
}

// Replace a file extension with ".exe". If the given file has no
// extension, just add ".exe".
StringRef getDefaultOutputFileName(PECOFFTargetInfo &info, std::string path) {
  StringRef ext = llvm::sys::path::extension(path);
  if (!ext.empty())
    path.erase(path.size() - ext.size());
  return info.allocateString(path.append(".exe"));
}

} // namespace

bool WinLinkDriver::linkPECOFF(int argc, const char *argv[],
                               raw_ostream &diagnostics) {
  PECOFFTargetInfo info;
  if (parse(argc, argv, info, diagnostics))
    return true;
  return link(info, diagnostics);
}

bool WinLinkDriver::parse(int argc, const char *argv[],
                          PECOFFTargetInfo &info, raw_ostream &diagnostics) {
  // Arguments after "--" are interpreted as filenames even if they start with
  // a hyphen or a slash. This is not compatible with link.exe but useful for
  // us to test lld on Unix.
  int doubleDashPosition = findDoubleDash(argc, argv);
  int argEnd = (doubleDashPosition > 0) ? doubleDashPosition : argc;

  // Parse command line options using WinLinkOptions.td
  std::unique_ptr<llvm::opt::InputArgList> parsedArgs;
  WinLinkOptTable table;
  unsigned missingIndex;
  unsigned missingCount;
  parsedArgs.reset(
      table.ParseArgs(&argv[1], &argv[argEnd], missingIndex, missingCount));
  if (missingCount) {
    diagnostics << "error: missing arg value for '"
                << parsedArgs->getArgString(missingIndex) << "' expected "
                << missingCount << " argument(s).\n";
    return true;
  }

  // Handle -help
  if (parsedArgs->getLastArg(OPT_help)) {
    table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false);
    return true;
  }

  // Show warning for unknown arguments
  for (auto it = parsedArgs->filtered_begin(OPT_UNKNOWN),
            ie = parsedArgs->filtered_end(); it != ie; ++it) {
    diagnostics << "warning: ignoring unknown argument: "
                << (*it)->getAsString(*parsedArgs) << "\n";
  }

  // Copy -mllvm
  for (llvm::opt::arg_iterator it = parsedArgs->filtered_begin(OPT_mllvm),
                               ie = parsedArgs->filtered_end();
       it != ie; ++it) {
    info.appendLLVMOption((*it)->getValue());
  }

  // Handle -stack
  if (llvm::opt::Arg *arg = parsedArgs->getLastArg(OPT_stack))
    if (!parseStackOption(info, arg->getValue(), diagnostics))
      return true;

  // Handle -heap
  if (llvm::opt::Arg *arg = parsedArgs->getLastArg(OPT_heap))
    if (!parseHeapOption(info, arg->getValue(), diagnostics))
      return true;

  // Handle -subsystem
  if (llvm::opt::Arg *arg = parsedArgs->getLastArg(OPT_subsystem))
    if (!parseSubsystemOption(info, arg->getValue(), diagnostics))
      return true;

  // Handle -entry
  if (llvm::opt::Arg *arg = parsedArgs->getLastArg(OPT_entry))
    info.setEntrySymbolName(arg->getValue());

  // Handle -force
  if (parsedArgs->getLastArg(OPT_force))
    info.setAllowRemainingUndefines(true);

  // Hanlde -nxcompat:no
  if (parsedArgs->getLastArg(OPT_no_nxcompat))
    info.setNxCompat(false);

  // Hanlde -largeaddressaware
  if (parsedArgs->getLastArg(OPT_largeaddressaware))
    info.setLargeAddressAware(true);

  // Hanlde -out
  if (llvm::opt::Arg *outpath = parsedArgs->getLastArg(OPT_out))
    info.setOutputPath(outpath->getValue());

  // Add input files
  std::vector<StringRef> inputPaths;
  for (llvm::opt::arg_iterator it = parsedArgs->filtered_begin(OPT_INPUT),
                               ie = parsedArgs->filtered_end();
       it != ie; ++it) {
    inputPaths.push_back((*it)->getValue());
  }

  // Arguments after "--" are also input files
  if (doubleDashPosition > 0)
    for (int i = doubleDashPosition + 1; i < argc; ++i)
      inputPaths.push_back(argv[i]);

  // Add ".obj" extension for those who have no file extension.
  for (const StringRef &path : inputPaths)
    info.appendInputFile(canonicalizeInputFileName(info, path));

  // If -out option was not specified, the default output file name is
  // constructed by replacing an extension with ".exe".
  if (info.outputPath().empty() && !inputPaths.empty())
    info.setOutputPath(getDefaultOutputFileName(info, inputPaths[0]));

  // Validate the combination of options used.
  return info.validate(diagnostics);
}

} // namespace lld
<commit_msg>[PECOFF] Use replace_extension() instead of doing it myself.<commit_after>//===- lib/Driver/WinLinkDriver.cpp ---------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// Concrete instance of the Driver for Windows link.exe.
///
//===----------------------------------------------------------------------===//

#include <cstdlib>

#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Path.h"

#include "lld/Driver/Driver.h"
#include "lld/ReaderWriter/PECOFFTargetInfo.h"

namespace lld {

namespace {

// Create enum with OPT_xxx values for each option in WinLinkOptions.td
enum WinLinkOpt {
  OPT_INVALID = 0,
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, HELP, META) \
          OPT_##ID,
#include "WinLinkOptions.inc"
  LastOption
#undef OPTION
};

// Create prefix string literals used in WinLinkOptions.td
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
#include "WinLinkOptions.inc"
#undef PREFIX

// Create table mapping all options defined in WinLinkOptions.td
static const llvm::opt::OptTable::Info infoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
               HELPTEXT, METAVAR)   \
  { PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
    PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS },
#include "WinLinkOptions.inc"
#undef OPTION
};

// Create OptTable class for parsing actual command line arguments
class WinLinkOptTable : public llvm::opt::OptTable {
public:
  WinLinkOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){}
};

// Returns the index of "--" or -1 if not found.
int findDoubleDash(int argc, const char *argv[]) {
  for (int i = 0; i < argc; ++i)
    if (std::strcmp(argv[i], "--") == 0)
      return i;
  return -1;
}

// Displays error message if the given version does not match with
// /^\d+$/.
bool checkNumber(StringRef version, const char *errorMessage,
                 raw_ostream &diagnostics) {
  if (version.str().find_first_not_of("0123456789") != std::string::npos
      || version.empty()) {
    diagnostics << "error: " << errorMessage << version << "\n";
    return false;
  }
  return true;
}

// Parse an argument for -stack or -heap. The expected string is
// "reserveSize[,stackCommitSize]".
bool parseMemoryOption(const StringRef &arg, raw_ostream &diagnostics,
                       uint64_t &reserve, uint64_t &commit) {
  StringRef reserveStr, commitStr;
  llvm::tie(reserveStr, commitStr) = arg.split(',');
  if (!checkNumber(reserveStr, "invalid stack size: ", diagnostics))
    return false;
  reserve = atoi(reserveStr.str().c_str());
  if (!commitStr.empty()) {
    if (!checkNumber(commitStr, "invalid stack size: ", diagnostics))
      return false;
    commit = atoi(commitStr.str().c_str());
  }
  return true;
}

// Parse -stack command line option
bool parseStackOption(PECOFFTargetInfo &info, const StringRef &arg,
                      raw_ostream &diagnostics) {
  uint64_t reserve;
  uint64_t commit = info.getStackCommit();
  if (!parseMemoryOption(arg, diagnostics, reserve, commit))
    return false;
  info.setStackReserve(reserve);
  info.setStackCommit(commit);
  return true;
}

// Parse -heap command line option.
bool parseHeapOption(PECOFFTargetInfo &info, const StringRef &arg,
                     raw_ostream &diagnostics) {
  uint64_t reserve;
  uint64_t commit = info.getHeapCommit();
  if (!parseMemoryOption(arg, diagnostics, reserve, commit))
    return false;
  info.setHeapReserve(reserve);
  info.setHeapCommit(commit);
  return true;
}

// Returns subsystem type for the given string.
llvm::COFF::WindowsSubsystem stringToWinSubsystem(StringRef str) {
  std::string arg(str.lower());
  return llvm::StringSwitch<llvm::COFF::WindowsSubsystem>(arg)
      .Case("windows", llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI)
      .Case("console", llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI)
      .Default(llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN);
}

bool parseMinOSVersion(PECOFFTargetInfo &info, const StringRef &osVersion,
                       raw_ostream &diagnostics) {
  StringRef majorVersion, minorVersion;
  llvm::tie(majorVersion, minorVersion) = osVersion.split('.');
  if (minorVersion.empty())
    minorVersion = "0";
  if (!checkNumber(majorVersion, "invalid OS major version: ", diagnostics))
    return false;
  if (!checkNumber(minorVersion, "invalid OS minor version: ", diagnostics))
    return false;
  PECOFFTargetInfo::OSVersion minOSVersion(atoi(majorVersion.str().c_str()),
                                           atoi(minorVersion.str().c_str()));
  info.setMinOSVersion(minOSVersion);
  return true;
}

// Parse -subsystem command line option. The form of -subsystem is
// "subsystem_name[,majorOSVersion[.minorOSVersion]]".
bool parseSubsystemOption(PECOFFTargetInfo &info, std::string arg,
                          raw_ostream &diagnostics) {
  StringRef subsystemStr, osVersionStr;
  llvm::tie(subsystemStr, osVersionStr) = StringRef(arg).split(',');

  // Parse optional OS version if exists.
  if (!osVersionStr.empty())
    if (!parseMinOSVersion(info, osVersionStr, diagnostics))
      return false;

  // Parse subsystem name.
  llvm::COFF::WindowsSubsystem subsystem = stringToWinSubsystem(subsystemStr);
  if (subsystem == llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN) {
    diagnostics << "error: unknown subsystem name: " << subsystemStr << "\n";
    return false;
  }
  info.setSubsystem(subsystem);
  return true;
}

// Add ".obj" extension if the given path name has no file extension.
StringRef canonicalizeInputFileName(PECOFFTargetInfo &info, std::string path) {
  if (llvm::sys::path::extension(path).empty())
    path.append(".obj");
  return info.allocateString(path);
}

// Replace a file extension with ".exe". If the given file has no
// extension, just add ".exe".
StringRef getDefaultOutputFileName(PECOFFTargetInfo &info, StringRef path) {
  SmallString<128> smallStr = path;
  llvm::sys::path::replace_extension(smallStr, ".exe");
  return info.allocateString(smallStr.str());
}

} // namespace

bool WinLinkDriver::linkPECOFF(int argc, const char *argv[],
                               raw_ostream &diagnostics) {
  PECOFFTargetInfo info;
  if (parse(argc, argv, info, diagnostics))
    return true;
  return link(info, diagnostics);
}

bool WinLinkDriver::parse(int argc, const char *argv[],
                          PECOFFTargetInfo &info, raw_ostream &diagnostics) {
  // Arguments after "--" are interpreted as filenames even if they start with
  // a hyphen or a slash. This is not compatible with link.exe but useful for
  // us to test lld on Unix.
  int doubleDashPosition = findDoubleDash(argc, argv);
  int argEnd = (doubleDashPosition > 0) ? doubleDashPosition : argc;

  // Parse command line options using WinLinkOptions.td
  std::unique_ptr<llvm::opt::InputArgList> parsedArgs;
  WinLinkOptTable table;
  unsigned missingIndex;
  unsigned missingCount;
  parsedArgs.reset(
      table.ParseArgs(&argv[1], &argv[argEnd], missingIndex, missingCount));
  if (missingCount) {
    diagnostics << "error: missing arg value for '"
                << parsedArgs->getArgString(missingIndex) << "' expected "
                << missingCount << " argument(s).\n";
    return true;
  }

  // Handle -help
  if (parsedArgs->getLastArg(OPT_help)) {
    table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false);
    return true;
  }

  // Show warning for unknown arguments
  for (auto it = parsedArgs->filtered_begin(OPT_UNKNOWN),
            ie = parsedArgs->filtered_end(); it != ie; ++it) {
    diagnostics << "warning: ignoring unknown argument: "
                << (*it)->getAsString(*parsedArgs) << "\n";
  }

  // Copy -mllvm
  for (llvm::opt::arg_iterator it = parsedArgs->filtered_begin(OPT_mllvm),
                               ie = parsedArgs->filtered_end();
       it != ie; ++it) {
    info.appendLLVMOption((*it)->getValue());
  }

  // Handle -stack
  if (llvm::opt::Arg *arg = parsedArgs->getLastArg(OPT_stack))
    if (!parseStackOption(info, arg->getValue(), diagnostics))
      return true;

  // Handle -heap
  if (llvm::opt::Arg *arg = parsedArgs->getLastArg(OPT_heap))
    if (!parseHeapOption(info, arg->getValue(), diagnostics))
      return true;

  // Handle -subsystem
  if (llvm::opt::Arg *arg = parsedArgs->getLastArg(OPT_subsystem))
    if (!parseSubsystemOption(info, arg->getValue(), diagnostics))
      return true;

  // Handle -entry
  if (llvm::opt::Arg *arg = parsedArgs->getLastArg(OPT_entry))
    info.setEntrySymbolName(arg->getValue());

  // Handle -force
  if (parsedArgs->getLastArg(OPT_force))
    info.setAllowRemainingUndefines(true);

  // Hanlde -nxcompat:no
  if (parsedArgs->getLastArg(OPT_no_nxcompat))
    info.setNxCompat(false);

  // Hanlde -largeaddressaware
  if (parsedArgs->getLastArg(OPT_largeaddressaware))
    info.setLargeAddressAware(true);

  // Hanlde -out
  if (llvm::opt::Arg *outpath = parsedArgs->getLastArg(OPT_out))
    info.setOutputPath(outpath->getValue());

  // Add input files
  std::vector<StringRef> inputPaths;
  for (llvm::opt::arg_iterator it = parsedArgs->filtered_begin(OPT_INPUT),
                               ie = parsedArgs->filtered_end();
       it != ie; ++it) {
    inputPaths.push_back((*it)->getValue());
  }

  // Arguments after "--" are also input files
  if (doubleDashPosition > 0)
    for (int i = doubleDashPosition + 1; i < argc; ++i)
      inputPaths.push_back(argv[i]);

  // Add ".obj" extension for those who have no file extension.
  for (const StringRef &path : inputPaths)
    info.appendInputFile(canonicalizeInputFileName(info, path));

  // If -out option was not specified, the default output file name is
  // constructed by replacing an extension with ".exe".
  if (info.outputPath().empty() && !inputPaths.empty())
    info.setOutputPath(getDefaultOutputFileName(info, inputPaths[0]));

  // Validate the combination of options used.
  return info.validate(diagnostics);
}

} // namespace lld
<|endoftext|>


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

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

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

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

=========================================================================*/
#include "otbRasterization.h"

#include <iostream>
#include "otbCommandLineArgumentParser.h"

//Image
#include "otbImage.h"
#include "otbImageFileReader.h"
#include "otbStreamingImageFileWriter.h"
#include "itkRGBAPixel.h"

//VectorData
#include "otbVectorData.h"
#include "otbVectorDataExtractROI.h"
#include "otbVectorDataProjectionFilter.h"
#include "otbVectorDataFileReader.h"
#include "otbVectorDataFileWriter.h"
#include "otbVectorDataProperties.h"

//Rasterization
#include "otbVectorDataToImageFilter.h"

//Misc
#include "otbRemoteSensingRegion.h"
#include "otbStandardWriterWatcher.h"
#include "otbPipelineMemoryPrintCalculator.h"

namespace otb
{

int Rasterization::Describe(ApplicationDescriptor* descriptor)
{
  descriptor->SetName("Rasterization");
  descriptor->SetDescription("Reproject and Rasterize a Vector Data.");
  descriptor->AddOption("InputVData", "The input vector data to be rasterized",
                        "in",  1, true, ApplicationDescriptor::FileName);
  descriptor->AddOption("OutputImage", "An output image containing the rasterized vector data",
                        "out", 1, true, ApplicationDescriptor::OutputImage);
  descriptor->AddOption("SizeX", "OutputSize[0]",
                        "szx", 1, true, ApplicationDescriptor::Real);
  descriptor->AddOption("SizeY", "OutputSize[1]",
                        "szy", 1, true, ApplicationDescriptor::Real);

  // Optional
  descriptor->AddOption("OriginX", "OutputOrigin[0] (optional)",
                        "orx", 1, false, ApplicationDescriptor::Real);
  descriptor->AddOption("OriginY", "OutputOrigin[1] (optional)",
                        "ory", 1, false, ApplicationDescriptor::Real);
  descriptor->AddOption("SpacingX", "OutputSpacing[0] (optional)",
                        "spx", 1, false, ApplicationDescriptor::Real);
  descriptor->AddOption("SpacingY", "OutputSpacing[1] (optional)",
                        "spy", 1, false, ApplicationDescriptor::Real);
  descriptor->AddOption("ProjRef", "Projection (optional)",
                        "pr",  1, false, ApplicationDescriptor::String);
  descriptor->AddOption("AvailableMemory", "Set the maximum of available memory for the pipeline execution in mega bytes (optional, 256 by default)",
                        "ram",1,false, otb::ApplicationDescriptor::Integer);

  return EXIT_SUCCESS;
}

int Rasterization::Execute(otb::ApplicationOptionsResult* parseResult)
{
  // Images
  //typedef itk::RGBAPixel<unsigned char>                   PixelType;
  typedef unsigned char                                   PixelType;
  typedef otb::Image<PixelType, 2>                        ImageType;
  typedef ImageType::PointType                            PointType;
  typedef ImageType::SizeType                             SizeType;
  typedef ImageType::SpacingType                          SpacingType;
  typedef ImageType::IndexType                            IndexType;
  typedef otb::StreamingImageFileWriter<ImageType>        WriterType;

  // VectorData
  typedef otb::VectorData<>                               VectorDataType;
  typedef VectorDataType::DataNodeType                    DataNodeType;
  typedef VectorDataType::DataTreeType                    DataTreeType;
  typedef DataNodeType::PointType                         PointType;
  typedef otb::VectorDataFileReader<VectorDataType>       VectorDataReaderType;
  typedef otb::VectorDataFileWriter<VectorDataType>       VectorDataWriterType;
  typedef VectorDataProjectionFilter<
    VectorDataType,VectorDataType>                        VectorDataProjectionFilterType;
  typedef VectorDataExtractROI<VectorDataType>            VectorDataExtractROIType;
  typedef VectorDataProperties<VectorDataType>            VectorDataPropertiesType;

  // Rasterization
  typedef otb::VectorDataToImageFilter<VectorDataType,
    ImageType>                                      VectorDataToImageFilterType;

  // Misc
  typedef otb::RemoteSensingRegion<double>                RemoteSensingRegionType;
  typedef RemoteSensingRegionType::SizeType               SizePhyType;
  typedef otb::PipelineMemoryPrintCalculator              MemoryCalculatorType;

  // Reading the VectorData
  std::string vdFilename = parseResult->GetParameterString("InputVData");
  std::cout<<"Processing vector data : "<<vdFilename<<std::endl;
  VectorDataReaderType::Pointer vdReader = VectorDataReaderType::New();
  vdReader->SetFileName(vdFilename);
  vdReader->Update();

  // Reprojecting the VectorData
  std::string projectionRef;
  VectorDataProjectionFilterType::Pointer vproj = VectorDataProjectionFilterType::New();
  vproj->SetInput(vdReader->GetOutput());
  if(parseResult->IsOptionPresent("ProjRef"))
    {
    projectionRef = parseResult->GetParameterString("ProjRef");
    }
  else
    {
    projectionRef = vdReader->GetOutput()->GetProjectionRef();
    }
  vproj->SetOutputProjectionRef(projectionRef);

  // Converting the VectorData
  VectorDataPropertiesType::Pointer vdProperties = VectorDataPropertiesType::New();
  vdProperties->SetVectorDataObject(vdReader->GetOutput());
  vdProperties->ComputeBoundingRegion();

  SizeType size;
  size[0] = parseResult->GetParameterDouble("SizeX");
  size[1] = parseResult->GetParameterDouble("SizeY");

  PointType origin;
  if(parseResult->IsOptionPresent("OriginX") && parseResult->IsOptionPresent("OriginY"))
    {
    origin[0] = parseResult->GetParameterDouble("OriginX");
    origin[1] = parseResult->GetParameterDouble("OriginY");
    }
  else
    {
    origin = vdProperties->GetBoundingRegion().GetIndex();
    }

  SpacingType spacing;
  if(parseResult->IsOptionPresent("SpacingX") && parseResult->IsOptionPresent("SpacingY"))
    {
    spacing[0] = parseResult->GetParameterDouble("SpacingX");
    spacing[1] = parseResult->GetParameterDouble("SpacingY");
    }
  else
    {
    spacing[0] = vdProperties->GetBoundingRegion().GetSize()[0]/size[0];
    spacing[1] = vdProperties->GetBoundingRegion().GetSize()[1]/size[1];
    }

  SizePhyType               sizePhy;
  sizePhy[0] = size[0] * spacing[0];
  sizePhy[1] = size[1] * spacing[1];

  RemoteSensingRegionType   region;
  region.SetSize(sizePhy);
  region.SetOrigin(origin);
  region.SetRegionProjection(projectionRef);

  VectorDataExtractROIType::Pointer vdextract = VectorDataExtractROIType::New();
  vdextract->SetRegion(region);
  vdextract->SetInput(vproj->GetOutput());

  VectorDataToImageFilterType::Pointer vectorDataRendering = VectorDataToImageFilterType::New();
  vectorDataRendering->SetInput(vdextract->GetOutput());
  vectorDataRendering->SetSize(size);
  vectorDataRendering->SetOrigin(origin);
  vectorDataRendering->SetSpacing(spacing);
  vectorDataRendering->SetVectorDataProjectionWKT(projectionRef);
  vectorDataRendering->SetRenderingStyleType(VectorDataToImageFilterType::Binary);

  // Instantiate the writer
  std::string oFilename = parseResult->GetParameterString("OutputImage");

  WriterType::Pointer oWriter = WriterType::New();
  oWriter->SetFileName(oFilename);
  oWriter->SetInput(vectorDataRendering->GetOutput());

  //Instantiate the pipeline memory print estimator
  MemoryCalculatorType::Pointer calculator = MemoryCalculatorType::New();
  const double byteToMegabyte = 1./vcl_pow(2.0, 20);

  if (parseResult->IsOptionPresent("AvailableMemory"))
    {
    long long int memory = static_cast <long long int> (parseResult->GetParameterUInt("AvailableMemory"));
    calculator->SetAvailableMemory(memory / byteToMegabyte);
    }
  else
    {
    calculator->SetAvailableMemory(256 * byteToMegabyte);
    }

  calculator->SetDataToWrite(vectorDataRendering->GetOutput());
  calculator->Compute();

  oWriter->SetTilingStreamDivisions(calculator->GetOptimalNumberOfStreamDivisions());

  otbMsgDevMacro(<< "Guess the pipeline memory print " << calculator->GetMemoryPrint()*byteToMegabyte << " Mo");
  otbMsgDevMacro(<< "Number of stream divisions : " << calculator->GetOptimalNumberOfStreamDivisions());

  otb::StandardWriterWatcher watcher(oWriter,"Rasterization");

  oWriter->Update();

  return EXIT_SUCCESS;
}
}
<commit_msg>STYLE<commit_after>/*=========================================================================

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

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

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

=========================================================================*/
#include "otbRasterization.h"

#include <iostream>
#include "otbCommandLineArgumentParser.h"

//Image
#include "otbImage.h"
#include "otbImageFileReader.h"
#include "otbStreamingImageFileWriter.h"
#include "itkRGBAPixel.h"

//VectorData
#include "otbVectorData.h"
#include "otbVectorDataExtractROI.h"
#include "otbVectorDataProjectionFilter.h"
#include "otbVectorDataFileReader.h"
#include "otbVectorDataFileWriter.h"
#include "otbVectorDataProperties.h"

//Rasterization
#include "otbVectorDataToImageFilter.h"

//Misc
#include "otbRemoteSensingRegion.h"
#include "otbStandardWriterWatcher.h"
#include "otbPipelineMemoryPrintCalculator.h"

namespace otb
{

int Rasterization::Describe(ApplicationDescriptor* descriptor)
{
  descriptor->SetName("Rasterization");
  descriptor->SetDescription("Reproject and Rasterize a Vector Data.");
  descriptor->AddOption("InputVData", "The input vector data to be rasterized",
                        "in",  1, true, ApplicationDescriptor::FileName);
  descriptor->AddOption("OutputImage", "An output image containing the rasterized vector data",
                        "out", 1, true, ApplicationDescriptor::OutputImage);
  descriptor->AddOption("SizeX", "OutputSize[0]",
                        "szx", 1, true, ApplicationDescriptor::Real);
  descriptor->AddOption("SizeY", "OutputSize[1]",
                        "szy", 1, true, ApplicationDescriptor::Real);

  // Optional
  descriptor->AddOption("OriginX", "OutputOrigin[0] (optional)",
                        "orx", 1, false, ApplicationDescriptor::Real);
  descriptor->AddOption("OriginY", "OutputOrigin[1] (optional)",
                        "ory", 1, false, ApplicationDescriptor::Real);
  descriptor->AddOption("SpacingX", "OutputSpacing[0] (optional)",
                        "spx", 1, false, ApplicationDescriptor::Real);
  descriptor->AddOption("SpacingY", "OutputSpacing[1] (optional)",
                        "spy", 1, false, ApplicationDescriptor::Real);
  descriptor->AddOption("ProjRef", "Projection (optional)",
                        "pr",  1, false, ApplicationDescriptor::String);
  descriptor->AddOption("AvailableMemory", "Set the maximum of available memory for the pipeline execution in mega bytes (optional, 256 by default)",
                        "ram", 1, false, otb::ApplicationDescriptor::Integer);

  return EXIT_SUCCESS;
}

int Rasterization::Execute(otb::ApplicationOptionsResult* parseResult)
{
  // Images
  //typedef itk::RGBAPixel<unsigned char>                   PixelType;
  typedef unsigned char                                   PixelType;
  typedef otb::Image<PixelType, 2>                        ImageType;
  typedef ImageType::PointType                            PointType;
  typedef ImageType::SizeType                             SizeType;
  typedef ImageType::SpacingType                          SpacingType;
  typedef ImageType::IndexType                            IndexType;
  typedef otb::StreamingImageFileWriter<ImageType>        WriterType;

  // VectorData
  typedef otb::VectorData<>                               VectorDataType;
  typedef VectorDataType::DataNodeType                    DataNodeType;
  typedef VectorDataType::DataTreeType                    DataTreeType;
  typedef DataNodeType::PointType                         PointType;
  typedef otb::VectorDataFileReader<VectorDataType>       VectorDataReaderType;
  typedef otb::VectorDataFileWriter<VectorDataType>       VectorDataWriterType;
  typedef VectorDataProjectionFilter<
    VectorDataType, VectorDataType>                        VectorDataProjectionFilterType;
  typedef VectorDataExtractROI<VectorDataType>            VectorDataExtractROIType;
  typedef VectorDataProperties<VectorDataType>            VectorDataPropertiesType;

  // Rasterization
  typedef otb::VectorDataToImageFilter<VectorDataType,
    ImageType>                                      VectorDataToImageFilterType;

  // Misc
  typedef otb::RemoteSensingRegion<double>                RemoteSensingRegionType;
  typedef RemoteSensingRegionType::SizeType               SizePhyType;
  typedef otb::PipelineMemoryPrintCalculator              MemoryCalculatorType;

  // Reading the VectorData
  std::string vdFilename = parseResult->GetParameterString("InputVData");
  std::cout<<"Processing vector data : "<<vdFilename<<std::endl;
  VectorDataReaderType::Pointer vdReader = VectorDataReaderType::New();
  vdReader->SetFileName(vdFilename);
  vdReader->Update();

  // Reprojecting the VectorData
  std::string projectionRef;
  VectorDataProjectionFilterType::Pointer vproj = VectorDataProjectionFilterType::New();
  vproj->SetInput(vdReader->GetOutput());
  if(parseResult->IsOptionPresent("ProjRef"))
    {
    projectionRef = parseResult->GetParameterString("ProjRef");
    }
  else
    {
    projectionRef = vdReader->GetOutput()->GetProjectionRef();
    }
  vproj->SetOutputProjectionRef(projectionRef);

  // Converting the VectorData
  VectorDataPropertiesType::Pointer vdProperties = VectorDataPropertiesType::New();
  vdProperties->SetVectorDataObject(vdReader->GetOutput());
  vdProperties->ComputeBoundingRegion();

  SizeType size;
  size[0] = parseResult->GetParameterDouble("SizeX");
  size[1] = parseResult->GetParameterDouble("SizeY");

  PointType origin;
  if(parseResult->IsOptionPresent("OriginX") && parseResult->IsOptionPresent("OriginY"))
    {
    origin[0] = parseResult->GetParameterDouble("OriginX");
    origin[1] = parseResult->GetParameterDouble("OriginY");
    }
  else
    {
    origin = vdProperties->GetBoundingRegion().GetIndex();
    }

  SpacingType spacing;
  if(parseResult->IsOptionPresent("SpacingX") && parseResult->IsOptionPresent("SpacingY"))
    {
    spacing[0] = parseResult->GetParameterDouble("SpacingX");
    spacing[1] = parseResult->GetParameterDouble("SpacingY");
    }
  else
    {
    spacing[0] = vdProperties->GetBoundingRegion().GetSize()[0]/size[0];
    spacing[1] = vdProperties->GetBoundingRegion().GetSize()[1]/size[1];
    }

  SizePhyType               sizePhy;
  sizePhy[0] = size[0] * spacing[0];
  sizePhy[1] = size[1] * spacing[1];

  RemoteSensingRegionType   region;
  region.SetSize(sizePhy);
  region.SetOrigin(origin);
  region.SetRegionProjection(projectionRef);

  VectorDataExtractROIType::Pointer vdextract = VectorDataExtractROIType::New();
  vdextract->SetRegion(region);
  vdextract->SetInput(vproj->GetOutput());

  VectorDataToImageFilterType::Pointer vectorDataRendering = VectorDataToImageFilterType::New();
  vectorDataRendering->SetInput(vdextract->GetOutput());
  vectorDataRendering->SetSize(size);
  vectorDataRendering->SetOrigin(origin);
  vectorDataRendering->SetSpacing(spacing);
  vectorDataRendering->SetVectorDataProjectionWKT(projectionRef);
  vectorDataRendering->SetRenderingStyleType(VectorDataToImageFilterType::Binary);

  // Instantiate the writer
  std::string oFilename = parseResult->GetParameterString("OutputImage");

  WriterType::Pointer oWriter = WriterType::New();
  oWriter->SetFileName(oFilename);
  oWriter->SetInput(vectorDataRendering->GetOutput());

  //Instantiate the pipeline memory print estimator
  MemoryCalculatorType::Pointer calculator = MemoryCalculatorType::New();
  const double byteToMegabyte = 1./vcl_pow(2.0, 20);

  if (parseResult->IsOptionPresent("AvailableMemory"))
    {
    long long int memory = static_cast <long long int> (parseResult->GetParameterUInt("AvailableMemory"));
    calculator->SetAvailableMemory(memory / byteToMegabyte);
    }
  else
    {
    calculator->SetAvailableMemory(256 * byteToMegabyte);
    }

  calculator->SetDataToWrite(vectorDataRendering->GetOutput());
  calculator->Compute();

  oWriter->SetTilingStreamDivisions(calculator->GetOptimalNumberOfStreamDivisions());

  otbMsgDevMacro(<< "Guess the pipeline memory print " << calculator->GetMemoryPrint()*byteToMegabyte << " Mo");
  otbMsgDevMacro(<< "Number of stream divisions : " << calculator->GetOptimalNumberOfStreamDivisions());

  otb::StandardWriterWatcher watcher(oWriter,"Rasterization");

  oWriter->Update();

  return EXIT_SUCCESS;
}
}
<|endoftext|>


<commit_before>#include "hexmap_widget.h"

#include <QMouseEvent>

#include <asdf_multiplat/main/asdf_multiplat.h>
#include <asdf_multiplat/data/content_manager.h>

#include "mainwindow.h"
#include "ui_mainwindow.h"

/// https://doc-snapshots.qt.io/qt5-dev/qopenglwidget.html

using namespace std;
//using namespace glm;  //causes namespace collison with uint

using vec2 = glm::vec2;

namespace
{
    constexpr float zoom_per_scroll_tick = 0.5f;

    constexpr float min_zoom = -16.0f;
    constexpr float max_zoom = 128.0f;
}

using tool_type_e = asdf::hexmap::editor::editor_t::tool_type_e;

hexmap_widget_t::hexmap_widget_t(QWidget* _parent)
: QOpenGLWidget(_parent)
{
    setFocusPolicy(Qt::StrongFocus);
    setFocus();
}

void hexmap_widget_t::initializeGL()
{
    using namespace asdf;

    void* qt_gl_context = reinterpret_cast<void*>(context());  //this technically isnt the native context pointer, but I don't think I care so long as it's unique
    app.renderer = make_unique<asdf::asdf_renderer_t>(qt_gl_context);  //do this before content init. Content needs GL_State to exist
    GL_State.set_current_state_machine(app.renderer->gl_state);

    app.renderer->init(); //loads screen shader, among other things
    Content.init(); //needed for Content::shader_path

    defaultFramebufferObject();

    auto shader = Content.create_shader_highest_supported("hexmap");
    Content.shaders.add_resource(shader);

    editor = std::make_unique<hexmap::editor::editor_t>();
    editor->init();
    // hex_map = &(editor.rendered_map);

    editor->map_changed_callback = [this](){
        emit map_data_changed(editor->map_data);
    };

    emit hex_map_initialized();
}

void hexmap_widget_t::resizeGL(int w, int h)
{
    using namespace asdf;

    app.surface_width = w;
    app.surface_height = h;

    editor->resize(w, h);
    main_window->set_scrollbar_stuff(editor->rendered_map.camera);
    //emit camera_changed(hex_map->camera);
}

void hexmap_widget_t::paintGL()
{
    using namespace asdf;

    //set global GL_State proxy object to point to this context
    GL_State.set_current_state_machine(app.renderer->gl_state);

    glDisable(GL_DEPTH_TEST);

    auto& gl_clear_color = asdf::app.renderer->gl_clear_color;
    glClearColor(gl_clear_color.r
                       , gl_clear_color.g
                       , gl_clear_color.b
                       , gl_clear_color.a);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glDisable(GL_CULL_FACE);

    editor->render();
}

glm::uvec2 hexmap_widget_t::map_size_cells() const
{
    return editor->map_data.hex_grid.size_cells();
}

glm::vec2  hexmap_widget_t::map_size_units() const
{
    return editor->map_data.hex_grid.size_units();
}

glm::vec2 hexmap_widget_t::camera_pos() const
{
    return vec2(editor->rendered_map.camera.position.x, editor->rendered_map.camera.position.y);
}

void hexmap_widget_t::camera_pos(glm::vec2 const& p, bool emit_signal)
{
    editor->rendered_map.camera.position.x = p.x;
    editor->rendered_map.camera.position.y = p.y;

    //if(emit_signal)
        //emit camera_changed(hex_map->camera);
}

//final zoom is equal to 2^zoom_exponent
void hexmap_widget_t::camera_zoom_exponent(float zoom_exponent)
{
    editor->rendered_map.camera.position.z = zoom_exponent;
}

// there may be a better solution than a switch statement
asdf::mouse_button_e asdf_button_from_qt(Qt::MouseButton button)
{
    switch(button)
    {
        case Qt::NoButton:
            return asdf::mouse_no_button;
        case Qt::LeftButton:
            return asdf::mouse_left;
        case Qt::RightButton:
            return asdf::mouse_right;
        case Qt::MiddleButton:
            return asdf::mouse_middle;
        case Qt::ExtraButton1:
            return asdf::mouse_4;
        case Qt::ExtraButton2:
            return asdf::mouse_5;

        default:
            return asdf::mouse_no_button;
    }
}

void hexmap_widget_t::mousePressEvent(QMouseEvent* event)
{
    auto& mouse = asdf::app.mouse_state;

    asdf::mouse_button_event_t asdf_event {
      mouse
    , asdf_button_from_qt(event->button())
    , (event->flags() & Qt::MouseEventCreatedDoubleClick) > 0
    };

    mouse.mouse_down(asdf_event, adjusted_screen_coords(event->x(), event->y()));
    update();
}

void hexmap_widget_t::mouseReleaseEvent(QMouseEvent* event)
{
    auto& mouse = asdf::app.mouse_state;

    asdf::mouse_button_event_t asdf_event {
      mouse
    , asdf_button_from_qt(event->button())
    , (event->flags() & Qt::MouseEventCreatedDoubleClick) > 0
    };

    auto obj_sel = editor->object_selection;
    mouse.mouse_up(asdf_event, adjusted_screen_coords(event->x(), event->y()));

    if(obj_sel != editor->object_selection)
    {
        emit object_selection_changed(*editor);
    }

    update();
}

void hexmap_widget_t::mouseMoveEvent(QMouseEvent* event)
{
    auto& mouse = asdf::app.mouse_state;

    asdf::mouse_motion_event_t asdf_event {
        mouse
    };

    auto coords = adjusted_screen_coords(event->x(), event->y());

    if(mouse.is_dragging())
    {
        mouse.mouse_drag(asdf_event, coords);
    }
    else
    {
        mouse.mouse_move(asdf_event, coords);
    }

    update(); //lazy
}

void hexmap_widget_t::wheelEvent(QWheelEvent* event)
{
    if(keyboard_mods == Qt::NoModifier)
    {
        main_window->ui->hexmap_vscroll->event(event); ///FIXME can this be solved without holding onto main_window?
    }
    else if(keyboard_mods == Qt::ShiftModifier)
    {
        // because shift button is held, the hscroll will act as if I'm shift-scrolling the scrollbar
        // which uses large page-sized jumps instead of regular jumps
        Qt::KeyboardModifiers mods = event->modifiers();
        mods.setFlag(Qt::ShiftModifier, false);
        event->setModifiers(mods);
        main_window->ui->hexmap_hscroll->event(event);
    }
    else if(keyboard_mods == Qt::ControlModifier)
    {
        float num_steps = event->angleDelta().y() / 8.0f / 15.0f;

        auto& zoom = editor->rendered_map.camera.position.z;

        zoom += num_steps * zoom_per_scroll_tick;
        zoom = glm::clamp(zoom, min_zoom, max_zoom);

        main_window->set_scrollbar_stuff(editor->rendered_map.camera);
        update();
        emit camera_changed(editor->rendered_map.camera);
    }
}

void hexmap_widget_t::keyPressEvent(QKeyEvent* event)
{
    keyboard_mods = event->modifiers();

    SDL_Keysym sdl_key_event; //being lazy and reusing sdl event for now
    sdl_key_event.sym = event->nativeVirtualKey(); //supposedly virtual keys are a standard

    sdl_key_event.mod = 0;
    sdl_key_event.mod |= KMOD_SHIFT * (event->modifiers() & Qt::ShiftModifier) > 0;
    sdl_key_event.mod |= KMOD_CTRL  * (event->modifiers() & Qt::ControlModifier) > 0;
    sdl_key_event.mod |= KMOD_ALT   * (event->modifiers() & Qt::AltModifier) > 0;
    sdl_key_event.mod |= KMOD_GUI   * (event->modifiers() & Qt::MetaModifier) > 0;

    auto prev_tool = editor->current_tool;

    editor->input->on_key_down(sdl_key_event);

    if(prev_tool != editor->current_tool)
        emit editor_tool_changed(editor->current_tool);

    /// TODO only call this when editor does not handle key
    QWidget::keyPressEvent(event);
}

void hexmap_widget_t::keyReleaseEvent(QKeyEvent *event)
{
    keyboard_mods = event->modifiers();
}

// QT mouse coords have 0,0 at the top-left of the widget
// adjust such that 0,0 is the center
glm::ivec2 hexmap_widget_t::adjusted_screen_coords(int x, int y) const
{
    return glm::ivec2(x - width()/2, height()/2 - y);
}

void hexmap_widget_t::set_editor_tool(tool_type_e new_tool)
{
    editor->set_tool(new_tool);
    emit editor_tool_changed(new_tool);
}

void hexmap_widget_t::set_palette_item(QModelIndex const& index)
{
    switch(editor->current_tool)
    {
        case tool_type_e::terrain_paint:
            editor->current_tile_id = index.row();
            break;
        case tool_type_e::place_objects:
            editor->current_object_id = index.row();
            break;

    default:
        break;
    }
}

void hexmap_widget_t::add_terrain(QStringList const& terrain_filepaths)
{
    if(terrain_filepaths.size() > 0)
    {
        //ensure this is the current gl context before the terrain bank
        //does any rendering in add_texture
        makeCurrent();

        for(auto const& filepath : terrain_filepaths)
        {
            std::string filepath_str{filepath.toUtf8().constData()};

            editor->map_data.terrain_bank.add_texture(filepath_str);
        }

        emit terrain_added(editor->map_data.terrain_bank);
    }
}

void hexmap_widget_t::save_terrain(QString const& filepath)
{
    auto path = std::experimental::filesystem::path(filepath.toUtf8().constData());
    editor->map_data.terrain_bank.save_to_file(path);
}

void hexmap_widget_t::load_terrain(QString const& filepath)
{
    makeCurrent();
    auto path = std::experimental::filesystem::path(filepath.toUtf8().constData());

    editor->map_data.terrain_bank.clear();
    editor->map_data.terrain_bank.load_from_file(path);

    emit terrain_added(editor->map_data.terrain_bank);
}

void hexmap_widget_t::zoom_to_selection()
{

}

void hexmap_widget_t::zoom_extents()
{
    camera_pos(glm::vec2(editor->map_data.hex_grid.size) / 2.0f);
    main_window->set_scrollbar_stuff(editor->rendered_map.camera);
}
<commit_msg>the [ and ] keys will now shrink/grow the current brush<commit_after>#include "hexmap_widget.h"

#include <QMouseEvent>

#include <asdf_multiplat/main/asdf_multiplat.h>
#include <asdf_multiplat/data/content_manager.h>

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "terrain_brush_selector.h"

/// https://doc-snapshots.qt.io/qt5-dev/qopenglwidget.html

using namespace std;
//using namespace glm;  //causes namespace collison with uint

using vec2 = glm::vec2;

namespace
{
    constexpr float zoom_per_scroll_tick = 0.5f;

    constexpr float min_zoom = -16.0f;
    constexpr float max_zoom = 128.0f;
}

using tool_type_e = asdf::hexmap::editor::editor_t::tool_type_e;

hexmap_widget_t::hexmap_widget_t(QWidget* _parent)
: QOpenGLWidget(_parent)
{
    setFocusPolicy(Qt::StrongFocus);
    setFocus();
}

void hexmap_widget_t::initializeGL()
{
    using namespace asdf;

    void* qt_gl_context = reinterpret_cast<void*>(context());  //this technically isnt the native context pointer, but I don't think I care so long as it's unique
    app.renderer = make_unique<asdf::asdf_renderer_t>(qt_gl_context);  //do this before content init. Content needs GL_State to exist
    GL_State.set_current_state_machine(app.renderer->gl_state);

    app.renderer->init(); //loads screen shader, among other things
    Content.init(); //needed for Content::shader_path

    defaultFramebufferObject();

    auto shader = Content.create_shader_highest_supported("hexmap");
    Content.shaders.add_resource(shader);

    editor = std::make_unique<hexmap::editor::editor_t>();
    editor->init();
    // hex_map = &(editor.rendered_map);

    editor->map_changed_callback = [this](){
        emit map_data_changed(editor->map_data);
    };

    emit hex_map_initialized();
}

void hexmap_widget_t::resizeGL(int w, int h)
{
    using namespace asdf;

    app.surface_width = w;
    app.surface_height = h;

    editor->resize(w, h);
    main_window->set_scrollbar_stuff(editor->rendered_map.camera);
    //emit camera_changed(hex_map->camera);
}

void hexmap_widget_t::paintGL()
{
    using namespace asdf;

    //set global GL_State proxy object to point to this context
    GL_State.set_current_state_machine(app.renderer->gl_state);

    glDisable(GL_DEPTH_TEST);

    auto& gl_clear_color = asdf::app.renderer->gl_clear_color;
    glClearColor(gl_clear_color.r
                       , gl_clear_color.g
                       , gl_clear_color.b
                       , gl_clear_color.a);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glDisable(GL_CULL_FACE);

    editor->render();
}

glm::uvec2 hexmap_widget_t::map_size_cells() const
{
    return editor->map_data.hex_grid.size_cells();
}

glm::vec2  hexmap_widget_t::map_size_units() const
{
    return editor->map_data.hex_grid.size_units();
}

glm::vec2 hexmap_widget_t::camera_pos() const
{
    return vec2(editor->rendered_map.camera.position.x, editor->rendered_map.camera.position.y);
}

void hexmap_widget_t::camera_pos(glm::vec2 const& p, bool emit_signal)
{
    editor->rendered_map.camera.position.x = p.x;
    editor->rendered_map.camera.position.y = p.y;

    //if(emit_signal)
        //emit camera_changed(hex_map->camera);
}

//final zoom is equal to 2^zoom_exponent
void hexmap_widget_t::camera_zoom_exponent(float zoom_exponent)
{
    editor->rendered_map.camera.position.z = zoom_exponent;
}

// there may be a better solution than a switch statement
asdf::mouse_button_e asdf_button_from_qt(Qt::MouseButton button)
{
    switch(button)
    {
        case Qt::NoButton:
            return asdf::mouse_no_button;
        case Qt::LeftButton:
            return asdf::mouse_left;
        case Qt::RightButton:
            return asdf::mouse_right;
        case Qt::MiddleButton:
            return asdf::mouse_middle;
        case Qt::ExtraButton1:
            return asdf::mouse_4;
        case Qt::ExtraButton2:
            return asdf::mouse_5;

        default:
            return asdf::mouse_no_button;
    }
}

void hexmap_widget_t::mousePressEvent(QMouseEvent* event)
{
    auto& mouse = asdf::app.mouse_state;

    asdf::mouse_button_event_t asdf_event {
      mouse
    , asdf_button_from_qt(event->button())
    , (event->flags() & Qt::MouseEventCreatedDoubleClick) > 0
    };

    mouse.mouse_down(asdf_event, adjusted_screen_coords(event->x(), event->y()));
    update();
}

void hexmap_widget_t::mouseReleaseEvent(QMouseEvent* event)
{
    auto& mouse = asdf::app.mouse_state;

    asdf::mouse_button_event_t asdf_event {
      mouse
    , asdf_button_from_qt(event->button())
    , (event->flags() & Qt::MouseEventCreatedDoubleClick) > 0
    };

    auto obj_sel = editor->object_selection;
    mouse.mouse_up(asdf_event, adjusted_screen_coords(event->x(), event->y()));

    if(obj_sel != editor->object_selection)
    {
        emit object_selection_changed(*editor);
    }

    update();
}

void hexmap_widget_t::mouseMoveEvent(QMouseEvent* event)
{
    auto& mouse = asdf::app.mouse_state;

    asdf::mouse_motion_event_t asdf_event {
        mouse
    };

    auto coords = adjusted_screen_coords(event->x(), event->y());

    if(mouse.is_dragging())
    {
        mouse.mouse_drag(asdf_event, coords);
    }
    else
    {
        mouse.mouse_move(asdf_event, coords);
    }

    update(); //lazy
}

void hexmap_widget_t::wheelEvent(QWheelEvent* event)
{
    if(keyboard_mods == Qt::NoModifier)
    {
        main_window->ui->hexmap_vscroll->event(event); ///FIXME can this be solved without holding onto main_window?
    }
    else if(keyboard_mods == Qt::ShiftModifier)
    {
        // because shift button is held, the hscroll will act as if I'm shift-scrolling the scrollbar
        // which uses large page-sized jumps instead of regular jumps
        Qt::KeyboardModifiers mods = event->modifiers();
        mods.setFlag(Qt::ShiftModifier, false);
        event->setModifiers(mods);
        main_window->ui->hexmap_hscroll->event(event);
    }
    else if(keyboard_mods == Qt::ControlModifier)
    {
        float num_steps = event->angleDelta().y() / 8.0f / 15.0f;

        auto& zoom = editor->rendered_map.camera.position.z;

        zoom += num_steps * zoom_per_scroll_tick;
        zoom = glm::clamp(zoom, min_zoom, max_zoom);

        main_window->set_scrollbar_stuff(editor->rendered_map.camera);
        update();
        emit camera_changed(editor->rendered_map.camera);
    }
}

void hexmap_widget_t::keyPressEvent(QKeyEvent* event)
{
    keyboard_mods = event->modifiers();

    SDL_Keysym sdl_key_event; //being lazy and reusing sdl event for now
    sdl_key_event.sym = event->nativeVirtualKey(); //supposedly virtual keys are a standard

    sdl_key_event.mod = 0;
    sdl_key_event.mod |= KMOD_SHIFT * (event->modifiers() & Qt::ShiftModifier) > 0;
    sdl_key_event.mod |= KMOD_CTRL  * (event->modifiers() & Qt::ControlModifier) > 0;
    sdl_key_event.mod |= KMOD_ALT   * (event->modifiers() & Qt::AltModifier) > 0;
    sdl_key_event.mod |= KMOD_GUI   * (event->modifiers() & Qt::MetaModifier) > 0;

    auto prev_tool = editor->current_tool;

    editor->input->on_key_down(sdl_key_event);

    if(prev_tool != editor->current_tool)
        emit editor_tool_changed(editor->current_tool);

    /// Hxm_Qt specific hotkeys
    switch(event->key())
    {
        case Qt::Key_BracketLeft:
            main_window->brush_settings->shrink_brush();
            update();
            return;
        case Qt::Key_BracketRight:
            main_window->brush_settings->grow_brush();
            update();
            return;
    }

    /// TODO only call this when editor does not handle key
    QWidget::keyPressEvent(event);
}

void hexmap_widget_t::keyReleaseEvent(QKeyEvent *event)
{
    keyboard_mods = event->modifiers();
}

// QT mouse coords have 0,0 at the top-left of the widget
// adjust such that 0,0 is the center
glm::ivec2 hexmap_widget_t::adjusted_screen_coords(int x, int y) const
{
    return glm::ivec2(x - width()/2, height()/2 - y);
}

void hexmap_widget_t::set_editor_tool(tool_type_e new_tool)
{
    editor->set_tool(new_tool);
    emit editor_tool_changed(new_tool);
}

void hexmap_widget_t::set_palette_item(QModelIndex const& index)
{
    switch(editor->current_tool)
    {
        case tool_type_e::terrain_paint:
            editor->current_tile_id = index.row();
            break;
        case tool_type_e::place_objects:
            editor->current_object_id = index.row();
            break;

    default:
        break;
    }
}

void hexmap_widget_t::add_terrain(QStringList const& terrain_filepaths)
{
    if(terrain_filepaths.size() > 0)
    {
        //ensure this is the current gl context before the terrain bank
        //does any rendering in add_texture
        makeCurrent();

        for(auto const& filepath : terrain_filepaths)
        {
            std::string filepath_str{filepath.toUtf8().constData()};

            editor->map_data.terrain_bank.add_texture(filepath_str);
        }

        emit terrain_added(editor->map_data.terrain_bank);
    }
}

void hexmap_widget_t::save_terrain(QString const& filepath)
{
    auto path = std::experimental::filesystem::path(filepath.toUtf8().constData());
    editor->map_data.terrain_bank.save_to_file(path);
}

void hexmap_widget_t::load_terrain(QString const& filepath)
{
    makeCurrent();
    auto path = std::experimental::filesystem::path(filepath.toUtf8().constData());

    editor->map_data.terrain_bank.clear();
    editor->map_data.terrain_bank.load_from_file(path);

    emit terrain_added(editor->map_data.terrain_bank);
}

void hexmap_widget_t::zoom_to_selection()
{

}

void hexmap_widget_t::zoom_extents()
{
    camera_pos(glm::vec2(editor->map_data.hex_grid.size) / 2.0f);
    main_window->set_scrollbar_stuff(editor->rendered_map.camera);
}
<|endoftext|>


<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2018-2020 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <modules/webbrowser/interaction/cefinteractionhandler.h>
#include <modules/webbrowser/interaction/cefkeyboardmapping.h>
#include <modules/webbrowser/renderhandlergl.h>
#include <inviwo/core/interaction/events/pickingevent.h>
#include <inviwo/core/interaction/events/resizeevent.h>
#include <inviwo/core/interaction/events/wheelevent.h>
#include <inviwo/core/interaction/events/touchevent.h>

#include <iostream>
#include <string>
#include <locale>
#include <codecvt>

namespace inviwo {

CEFInteractionHandler::CEFInteractionHandler(CefRefPtr<CefBrowserHost> host) : host_(host){};

void CEFInteractionHandler::invokeEvent(Event* event) {
    switch (event->hash()) {
        case ResizeEvent::chash(): {
            auto resizeEvent = static_cast<ResizeEvent*>(event);
            renderHandler_->updateCanvasSize(resizeEvent->size());
            host_->WasResized();
            break;
        }
        case KeyboardEvent::chash(): {
            auto keyEvent = event->getAs<KeyboardEvent>();
            auto cefEvent = mapKeyEvent(keyEvent);
            host_->SendKeyEvent(cefEvent);
            // Send CHAR event for characters, but not non-char keys like arrows,
            // function keys or clear.
            auto isCharacter = std::iscntrl(cefEvent.character) == 0;
            if (isCharacter && (keyEvent->state() & KeyState::Press)) {
                cefEvent.type = KEYEVENT_CHAR;
                host_->SendKeyEvent(cefEvent);
            }
            event->markAsUsed();
            break;
        }
    }
}

void CEFInteractionHandler::handlePickingEvent(PickingEvent* p) {
    if (p->getEvent()->hash() == MouseEvent::chash()) {
        auto mouseEvent = p->getEventAs<MouseEvent>();
        updateMouseStates(mouseEvent);
        auto cefMouseEvent = mapMouseEvent(mouseEvent);
        if (mouseEvent->state() & MouseState::Move) {
            bool mouseLeave = false;
            host_->SendMouseMoveEvent(cefMouseEvent, mouseLeave);
            p->markAsUsed();
        } else if (mouseEvent->state() & MouseState::Press ||
                   mouseEvent->state() & MouseState::Release) {
            CefBrowserHost::MouseButtonType type;

            if (mouseEvent->button() & MouseButton::Left) {
                type = MBT_LEFT;
            } else if (mouseEvent->button() & MouseButton::Middle) {
                type = MBT_MIDDLE;
            } else {  // if (mouseEvent->button() & MouseButton::Right) {
                type = MBT_RIGHT;
            }
            bool mouseUp = MouseState::Release & mouseEvent->state() ? true : false;
            int clickCount = MouseState::DoubleClick & mouseEvent->state() ? 2 : 1;
            host_->SendMouseClickEvent(cefMouseEvent, type, mouseUp, clickCount);
            p->markAsUsed();
        }
    } else if (auto touchEvent = p->getEventAs<TouchEvent>()) {

        if (!touchEvent->hasTouchPoints()) {
            return;
        }
        TouchDevice::DeviceType type = touchEvent->getDevice()
                                           ? touchEvent->getDevice()->getType()
                                           : TouchDevice::DeviceType::TouchScreen;
        if (type == TouchDevice::DeviceType::TouchPad) {
            // Mouse events are emulated on touch pads for single touch point
            // but we need still need to consume multi-touch events if user pressed on
            p->markAsUsed();
            return;
        }
        const auto& touchPoints = touchEvent->touchPoints();
        for (auto touchPoint : touchPoints) {
            auto cefEvent = mapTouchEvent(&touchPoint, touchEvent->getDevice());
            host_->SendTouchEvent(cefEvent);
        }
        p->markAsUsed();

    } else if (auto wheelEvent = p->getEventAs<WheelEvent>()) {
        auto cefMouseEvent = mapMouseEvent(wheelEvent);
        host_->SendMouseWheelEvent(cefMouseEvent, static_cast<int>(wheelEvent->delta().x),
                                   static_cast<int>(wheelEvent->delta().y));
        p->markAsUsed();
    }
}

CefMouseEvent CEFInteractionHandler::mapMouseEvent(const MouseInteractionEvent* e) {
    CefMouseEvent cefEvent;
    cefEvent.x = static_cast<int>(e->x());
    cefEvent.y = static_cast<int>(e->canvasSize().y) - static_cast<int>(e->y());
    cefEvent.modifiers = modifiers_;
    return cefEvent;
}

CefTouchEvent CEFInteractionHandler::mapTouchEvent(const TouchPoint* p, const TouchDevice* device) {
    CefTouchEvent cefEvent;
    cefEvent.id = p->id();
    // X coordinate relative to the left side of the view.
    cefEvent.x = static_cast<float>(p->pos().x);
    // Y coordinate relative to the top side of the view.
    cefEvent.y = static_cast<float>(p->canvasSize().y - p->pos().y);
    // Radius in pixels. Set to 0 if not applicable.
    cefEvent.radius_x = cefEvent.radius_y = 0;
    // Radius in pixels. Set to 0 if not applicable.
    cefEvent.rotation_angle = 0;
    // The normalized pressure of the pointer input in the range of [0,1].
    cefEvent.pressure = static_cast<float>(p->pressure());
    // The state of the touch point. Touches begin with one CEF_TET_PRESSED event
    // followed by zero or more CEF_TET_MOVED events and finally one
    // CEF_TET_RELEASED or CEF_TET_CANCELLED event. Events not respecting this
    // order will be ignored.
    auto toCefEventType = [](auto state) -> cef_touch_event_type_t {
        switch (state) {
            case TouchState::None:
                return CEF_TET_CANCELLED;
            case TouchState::Started:
                return CEF_TET_PRESSED;
            case TouchState::Updated:
            case TouchState::Stationary:
                return CEF_TET_MOVED;
            case TouchState::Finished:
                return CEF_TET_RELEASED;
            default: // Incorrect usage or new state added (warnings if left out)
                assert(false);
                return CEF_TET_CANCELLED;
        }
    };

    cefEvent.type = toCefEventType(p->state());
    auto toCefPointerType = [](auto device) -> cef_pointer_type_t {
        switch (device.getType()) {
            case TouchDevice::DeviceType::TouchScreen:
                return CEF_POINTER_TYPE_TOUCH;
            case TouchDevice::DeviceType::TouchPad:
                return CEF_POINTER_TYPE_MOUSE;
                // No types for these ones yet
                // case TouchDevice::DeviceType::Pen:
                //    return CEF_POINTER_TYPE_PEN;
                // case TouchDevice::DeviceType::Eraser:
                //    return CEF_POINTER_TYPE_ERASER;
            default: // Incorrect usage or new state added (warnings if left out)
                assert(false);
                return CEF_POINTER_TYPE_TOUCH;
        }
    };
    cefEvent.pointer_type = toCefPointerType(*device);

    cefEvent.modifiers = modifiers_;
    return cefEvent;
}

CefKeyEvent CEFInteractionHandler::mapKeyEvent(const KeyboardEvent* e) {
    CefKeyEvent cefEvent;

    // TODO: Fix key code translation to match the ones used in CEF
    // cefEvent.type = e->state() & KeyState::Press ? KEYEVENT_RAWKEYDOWN : KEYEVENT_CHAR;
    cefEvent.type = e->state() & KeyState::Press ? KEYEVENT_KEYDOWN : KEYEVENT_KEYUP;
    // Convert character to UTF16
#if _MSC_VER
    // Linker error when using char16_t in visual studio
    // https://social.msdn.microsoft.com/Forums/vstudio/en-US/8f40dcd8-c67f-4eba-9134-a19b9178e481/vs-2015-rc-linker-stdcodecvt-error?forum=vcgeneral
    auto textUTF16 = std::wstring_convert<std::codecvt_utf8_utf16<uint16_t>, uint16_t>{}.from_bytes(
        e->text().data());
#else
    auto textUTF16 = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
        e->text().data());
#endif

    if (textUTF16.length() > 0) {
        cefEvent.character = textUTF16[0];
    } else {
        cefEvent.character = 0;
    }

    cefEvent.native_key_code = e->getNativeVirtualKey();

#ifdef _WINDOWS
    // F10 or ALT
    // https://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
    cefEvent.is_system_key = (e->key() == IvwKey::F10) || (e->modifiers() & KeyModifier::Alt);
#else
    // Always false on non-windows platforms
    cefEvent.is_system_key = false;
#endif
    if (e->state() & KeyState::Press) {
        modifiers_ |= cef::keyModifiers(e->modifiers(), e->key());
    } else {
        modifiers_ &= ~cef::keyModifiers(e->modifiers(), e->key());
    }
    cefEvent.modifiers = modifiers_;
    return cefEvent;
}

void CEFInteractionHandler::updateMouseStates(MouseEvent* e) {
    if (e->state() & MouseState::Release) {
        // Remove modifiers
        modifiers_ &= ~(EVENTFLAG_LEFT_MOUSE_BUTTON | EVENTFLAG_MIDDLE_MOUSE_BUTTON |
                        EVENTFLAG_MIDDLE_MOUSE_BUTTON);
    } else {
        // Add modifiers
        modifiers_ |= (e->button() & MouseButton::Left ? EVENTFLAG_LEFT_MOUSE_BUTTON : 0) |
                      (e->button() & MouseButton::Middle ? EVENTFLAG_MIDDLE_MOUSE_BUTTON : 0) |
                      (e->button() & MouseButton::Right ? EVENTFLAG_RIGHT_MOUSE_BUTTON : 0);
    }
}
void CEFInteractionHandler::updateMouseStates(TouchEvent* e) {
    if (e->touchPoints().front().state() & TouchState::Finished) {
        // Remove modifiers
        modifiers_ &= ~(EVENTFLAG_LEFT_MOUSE_BUTTON | EVENTFLAG_MIDDLE_MOUSE_BUTTON |
                        EVENTFLAG_MIDDLE_MOUSE_BUTTON);
    } else {
        // Add modifiers
        modifiers_ |= (EVENTFLAG_LEFT_MOUSE_BUTTON);
    }
}
};  // namespace inviwo
<commit_msg>Webbrowser: Format fix<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2018-2020 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <modules/webbrowser/interaction/cefinteractionhandler.h>
#include <modules/webbrowser/interaction/cefkeyboardmapping.h>
#include <modules/webbrowser/renderhandlergl.h>
#include <inviwo/core/interaction/events/pickingevent.h>
#include <inviwo/core/interaction/events/resizeevent.h>
#include <inviwo/core/interaction/events/wheelevent.h>
#include <inviwo/core/interaction/events/touchevent.h>

#include <iostream>
#include <string>
#include <locale>
#include <codecvt>

namespace inviwo {

CEFInteractionHandler::CEFInteractionHandler(CefRefPtr<CefBrowserHost> host) : host_(host){};

void CEFInteractionHandler::invokeEvent(Event* event) {
    switch (event->hash()) {
        case ResizeEvent::chash(): {
            auto resizeEvent = static_cast<ResizeEvent*>(event);
            renderHandler_->updateCanvasSize(resizeEvent->size());
            host_->WasResized();
            break;
        }
        case KeyboardEvent::chash(): {
            auto keyEvent = event->getAs<KeyboardEvent>();
            auto cefEvent = mapKeyEvent(keyEvent);
            host_->SendKeyEvent(cefEvent);
            // Send CHAR event for characters, but not non-char keys like arrows,
            // function keys or clear.
            auto isCharacter = std::iscntrl(cefEvent.character) == 0;
            if (isCharacter && (keyEvent->state() & KeyState::Press)) {
                cefEvent.type = KEYEVENT_CHAR;
                host_->SendKeyEvent(cefEvent);
            }
            event->markAsUsed();
            break;
        }
    }
}

void CEFInteractionHandler::handlePickingEvent(PickingEvent* p) {
    if (p->getEvent()->hash() == MouseEvent::chash()) {
        auto mouseEvent = p->getEventAs<MouseEvent>();
        updateMouseStates(mouseEvent);
        auto cefMouseEvent = mapMouseEvent(mouseEvent);
        if (mouseEvent->state() & MouseState::Move) {
            bool mouseLeave = false;
            host_->SendMouseMoveEvent(cefMouseEvent, mouseLeave);
            p->markAsUsed();
        } else if (mouseEvent->state() & MouseState::Press ||
                   mouseEvent->state() & MouseState::Release) {
            CefBrowserHost::MouseButtonType type;

            if (mouseEvent->button() & MouseButton::Left) {
                type = MBT_LEFT;
            } else if (mouseEvent->button() & MouseButton::Middle) {
                type = MBT_MIDDLE;
            } else {  // if (mouseEvent->button() & MouseButton::Right) {
                type = MBT_RIGHT;
            }
            bool mouseUp = MouseState::Release & mouseEvent->state() ? true : false;
            int clickCount = MouseState::DoubleClick & mouseEvent->state() ? 2 : 1;
            host_->SendMouseClickEvent(cefMouseEvent, type, mouseUp, clickCount);
            p->markAsUsed();
        }
    } else if (auto touchEvent = p->getEventAs<TouchEvent>()) {

        if (!touchEvent->hasTouchPoints()) {
            return;
        }
        TouchDevice::DeviceType type = touchEvent->getDevice()
                                           ? touchEvent->getDevice()->getType()
                                           : TouchDevice::DeviceType::TouchScreen;
        if (type == TouchDevice::DeviceType::TouchPad) {
            // Mouse events are emulated on touch pads for single touch point
            // but we need still need to consume multi-touch events if user pressed on
            p->markAsUsed();
            return;
        }
        const auto& touchPoints = touchEvent->touchPoints();
        for (auto touchPoint : touchPoints) {
            auto cefEvent = mapTouchEvent(&touchPoint, touchEvent->getDevice());
            host_->SendTouchEvent(cefEvent);
        }
        p->markAsUsed();

    } else if (auto wheelEvent = p->getEventAs<WheelEvent>()) {
        auto cefMouseEvent = mapMouseEvent(wheelEvent);
        host_->SendMouseWheelEvent(cefMouseEvent, static_cast<int>(wheelEvent->delta().x),
                                   static_cast<int>(wheelEvent->delta().y));
        p->markAsUsed();
    }
}

CefMouseEvent CEFInteractionHandler::mapMouseEvent(const MouseInteractionEvent* e) {
    CefMouseEvent cefEvent;
    cefEvent.x = static_cast<int>(e->x());
    cefEvent.y = static_cast<int>(e->canvasSize().y) - static_cast<int>(e->y());
    cefEvent.modifiers = modifiers_;
    return cefEvent;
}

CefTouchEvent CEFInteractionHandler::mapTouchEvent(const TouchPoint* p, const TouchDevice* device) {
    CefTouchEvent cefEvent;
    cefEvent.id = p->id();
    // X coordinate relative to the left side of the view.
    cefEvent.x = static_cast<float>(p->pos().x);
    // Y coordinate relative to the top side of the view.
    cefEvent.y = static_cast<float>(p->canvasSize().y - p->pos().y);
    // Radius in pixels. Set to 0 if not applicable.
    cefEvent.radius_x = cefEvent.radius_y = 0;
    // Radius in pixels. Set to 0 if not applicable.
    cefEvent.rotation_angle = 0;
    // The normalized pressure of the pointer input in the range of [0,1].
    cefEvent.pressure = static_cast<float>(p->pressure());
    // The state of the touch point. Touches begin with one CEF_TET_PRESSED event
    // followed by zero or more CEF_TET_MOVED events and finally one
    // CEF_TET_RELEASED or CEF_TET_CANCELLED event. Events not respecting this
    // order will be ignored.
    auto toCefEventType = [](auto state) -> cef_touch_event_type_t {
        switch (state) {
            case TouchState::None:
                return CEF_TET_CANCELLED;
            case TouchState::Started:
                return CEF_TET_PRESSED;
            case TouchState::Updated:
            case TouchState::Stationary:
                return CEF_TET_MOVED;
            case TouchState::Finished:
                return CEF_TET_RELEASED;
            default:  // Incorrect usage or new state added (warnings if left out)
                assert(false);
                return CEF_TET_CANCELLED;
        }
    };

    cefEvent.type = toCefEventType(p->state());
    auto toCefPointerType = [](auto device) -> cef_pointer_type_t {
        switch (device.getType()) {
            case TouchDevice::DeviceType::TouchScreen:
                return CEF_POINTER_TYPE_TOUCH;
            case TouchDevice::DeviceType::TouchPad:
                return CEF_POINTER_TYPE_MOUSE;
                // No types for these ones yet
                // case TouchDevice::DeviceType::Pen:
                //    return CEF_POINTER_TYPE_PEN;
                // case TouchDevice::DeviceType::Eraser:
                //    return CEF_POINTER_TYPE_ERASER;
            default:  // Incorrect usage or new state added (warnings if left out)
                assert(false);
                return CEF_POINTER_TYPE_TOUCH;
        }
    };
    cefEvent.pointer_type = toCefPointerType(*device);

    cefEvent.modifiers = modifiers_;
    return cefEvent;
}

CefKeyEvent CEFInteractionHandler::mapKeyEvent(const KeyboardEvent* e) {
    CefKeyEvent cefEvent;

    // TODO: Fix key code translation to match the ones used in CEF
    // cefEvent.type = e->state() & KeyState::Press ? KEYEVENT_RAWKEYDOWN : KEYEVENT_CHAR;
    cefEvent.type = e->state() & KeyState::Press ? KEYEVENT_KEYDOWN : KEYEVENT_KEYUP;
    // Convert character to UTF16
#if _MSC_VER
    // Linker error when using char16_t in visual studio
    // https://social.msdn.microsoft.com/Forums/vstudio/en-US/8f40dcd8-c67f-4eba-9134-a19b9178e481/vs-2015-rc-linker-stdcodecvt-error?forum=vcgeneral
    auto textUTF16 = std::wstring_convert<std::codecvt_utf8_utf16<uint16_t>, uint16_t>{}.from_bytes(
        e->text().data());
#else
    auto textUTF16 = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
        e->text().data());
#endif

    if (textUTF16.length() > 0) {
        cefEvent.character = textUTF16[0];
    } else {
        cefEvent.character = 0;
    }

    cefEvent.native_key_code = e->getNativeVirtualKey();

#ifdef _WINDOWS
    // F10 or ALT
    // https://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
    cefEvent.is_system_key = (e->key() == IvwKey::F10) || (e->modifiers() & KeyModifier::Alt);
#else
    // Always false on non-windows platforms
    cefEvent.is_system_key = false;
#endif
    if (e->state() & KeyState::Press) {
        modifiers_ |= cef::keyModifiers(e->modifiers(), e->key());
    } else {
        modifiers_ &= ~cef::keyModifiers(e->modifiers(), e->key());
    }
    cefEvent.modifiers = modifiers_;
    return cefEvent;
}

void CEFInteractionHandler::updateMouseStates(MouseEvent* e) {
    if (e->state() & MouseState::Release) {
        // Remove modifiers
        modifiers_ &= ~(EVENTFLAG_LEFT_MOUSE_BUTTON | EVENTFLAG_MIDDLE_MOUSE_BUTTON |
                        EVENTFLAG_MIDDLE_MOUSE_BUTTON);
    } else {
        // Add modifiers
        modifiers_ |= (e->button() & MouseButton::Left ? EVENTFLAG_LEFT_MOUSE_BUTTON : 0) |
                      (e->button() & MouseButton::Middle ? EVENTFLAG_MIDDLE_MOUSE_BUTTON : 0) |
                      (e->button() & MouseButton::Right ? EVENTFLAG_RIGHT_MOUSE_BUTTON : 0);
    }
}
void CEFInteractionHandler::updateMouseStates(TouchEvent* e) {
    if (e->touchPoints().front().state() & TouchState::Finished) {
        // Remove modifiers
        modifiers_ &= ~(EVENTFLAG_LEFT_MOUSE_BUTTON | EVENTFLAG_MIDDLE_MOUSE_BUTTON |
                        EVENTFLAG_MIDDLE_MOUSE_BUTTON);
    } else {
        // Add modifiers
        modifiers_ |= (EVENTFLAG_LEFT_MOUSE_BUTTON);
    }
}
};  // namespace inviwo
<|endoftext|>