<commit_before>/**
 *  For conditions of distribution and use, see copyright notice in license.txt
 *
 *  @file   EC_Hydrax.cpp
 *  @brief  A photorealistic water plane component using Hydrax, http://www.ogre3d.org/tikiwiki/Hydrax
 */

#define OGRE_INTEROP

#include "DebugOperatorNew.h"

#include "EC_Hydrax.h"

#include "Scene.h"
#include "Framework.h"
#include "FrameAPI.h"
#include "AssetAPI.h"
#include "IAssetTransfer.h"
#include "IAsset.h"

#include "OgreWorld.h"
#include "Renderer.h"
#include "EC_Camera.h"
#include "Entity.h"
#include "OgreConversionUtils.h"
#include "Profiler.h"

#include "AttributeMetadata.h"
#ifdef SKYX_ENABLED
#include "EC_SkyX.h"
#endif

#include <Hydrax.h>
#include <Noise/Perlin/Perlin.h>
#include <Noise/FFT/FFT.h>
#include <Modules/ProjectedGrid/ProjectedGrid.h>
#include <Modules/RadialGrid/RadialGrid.h>
#include <Modules/SimpleGrid/SimpleGrid.h>

#include "LoggingFunctions.h"
#include "MemoryLeakCheck.h"

struct EC_HydraxImpl
{
    EC_HydraxImpl() : hydrax(0), module(0) {}
    ~EC_HydraxImpl()
    {
        if (hydrax)
            hydrax->remove();
        SAFE_DELETE(hydrax);
        //SAFE_DELETE(module); ///< @todo Possible mem leak?
    }

    Hydrax::Hydrax *hydrax;
    Hydrax::Module::Module *module;
#ifdef SKYX_ENABLED
    boost::weak_ptr<EC_SkyX> skyX;
#endif
};

EC_Hydrax::EC_Hydrax(Scene* scene) :
    IComponent(scene),
    configRef(this, "Config ref", AssetReference("HydraxDefault.hdx")),
    visible(this, "Visible", true),
    position(this, "Position", float3(0.0, 0.0, 0.0)),
//    noiseModule(this, "Noise module", 0),
//    noiseType(this, "Noise type", 0),
//    normalMode(this, "Normal mode", 0),
    impl(0)
{
/*
    static AttributeMetadata noiseTypeMetadata;
    static AttributeMetadata normalModeMetadata;
    static bool metadataInitialized = false;
    if (!metadataInitialized)
    {
        noiseTypeMetadata.enums[Perlin] = "Perlin";
        noiseTypeMetadata.enums[FFT] = "FFT";

        normalModeMetadata.enums[Texture] = "Texture";
        normalModeMetadata.enums[Vertex] = "Vertex";
        normalModeMetadata.enums[RTT] = "RTT";

        metadataInitialized = true;
    }

    noiseType.SetMetadata(&noiseTypeMetadata);
    normalMode.SetMetadata(&normalModeMetadata);
*/
    OgreWorldPtr w = scene->GetWorld<OgreWorld>();
    if (!w)
    {
        LogError("EC_Hydrax: no OgreWorld available. Cannot be created.");
        return;
    }

    connect(w->GetRenderer(), SIGNAL(MainCameraChanged(Entity *)), SLOT(OnActiveCameraChanged(Entity *)));
    connect(this, SIGNAL(ParentEntitySet()), SLOT(Create()));
}

EC_Hydrax::~EC_Hydrax()
{
    SAFE_DELETE(impl);
}

void EC_Hydrax::Create()
{
    SAFE_DELETE(impl);

    if (framework->IsHeadless())
        return;

    try
    {
        if (!ParentScene())
        {
            LogError("EC_Hydrax: no parent scene. Cannot be created.");
            return;
        }

        OgreWorldPtr w = ParentScene()->GetWorld<OgreWorld>();
        assert(w);

        Entity *mainCamera = w->GetRenderer()->MainCamera();
        if (!mainCamera)
        {
            LogError("Cannot create EC_Hydrax: No main camera set!");
            return; // Can't create Hydrax just yet, no main camera set.
        }

        Ogre::Camera *cam = mainCamera->GetComponent<EC_Camera>()->GetCamera();
        impl = new EC_HydraxImpl();
        impl->hydrax = new Hydrax::Hydrax(w->GetSceneManager(), cam, w->GetRenderer()->MainViewport());

        // Using projected grid module by default
        Hydrax::Module::ProjectedGrid *module = new Hydrax::Module::ProjectedGrid(impl->hydrax, new Hydrax::Noise::Perlin(),
            Ogre::Plane(Ogre::Vector3::UNIT_Y, Ogre::Vector3::ZERO), Hydrax::MaterialManager::NM_VERTEX);
        impl->hydrax->setModule(module);
        impl->module = module;

        // Load all parameters from config file
        impl->hydrax->loadCfg(configRef.Get().ref.toStdString());
        impl->hydrax->create();
        impl->hydrax->setPosition(position.Get());

        connect(framework->Frame(), SIGNAL(PostFrameUpdate(float)), SLOT(Update(float)), Qt::UniqueConnection);
        connect(this, SIGNAL(AttributeChanged(IAttribute*, AttributeChange::Type)), SLOT(UpdateAttribute(IAttribute*)), Qt::UniqueConnection);
    }
    catch(Ogre::Exception &e)
    {
        // Currently if we try to create more than one Hydrax component we end up here due to Ogre internal name collision.
        LogError("Could not create EC_Hydrax: " + std::string(e.what()));
    }
}

void EC_Hydrax::OnActiveCameraChanged(Entity *newActiveCamera)
{
    if (!newActiveCamera)
    {
        SAFE_DELETE(impl);
        return;
    }
    // If we haven't yet initialized, do a full init.
    if (!impl)
        Create();
    else // Otherwise, update the camera to an existing initialized Hydrax instance.
        if (impl && impl->hydrax)
            impl->hydrax->setCamera(newActiveCamera->GetComponent<EC_Camera>()->GetCamera());
}

void EC_Hydrax::UpdateAttribute(IAttribute *attr)
{
    if (attr == &configRef)
    {
        if (!configRef.Get().ref.isEmpty())
        {
            AssetTransferPtr transfer = framework->Asset()->RequestAsset(configRef.Get().ref, "Binary");
            if (transfer.get())
                connect(transfer.get(), SIGNAL(Succeeded(AssetPtr)), SLOT(ConfigLoadSucceeded(AssetPtr)), Qt::UniqueConnection);
        }
        else
            LoadDefaultConfig();
    }

    if (impl && !impl->hydrax)
        return;
    else if (attr == &visible)
        impl->hydrax->setVisible(visible.Get());
    else if (attr == &position)
        impl->hydrax->setPosition(position.Get());
/*
    else if (attr == &noiseModule || attr == &normalMode || &noiseType)
        UpdateNoiseModule();
*/
}
/*
void EC_Hydrax::UpdateNoiseModule()
{
    Hydrax::Noise::Noise *noise = 0;
    switch(noiseType.Get())
    {
    case Perlin:
        noise = new Hydrax::Noise::Perlin();
        break;
    case FFT:
        noise = new Hydrax::Noise::FFT();
        break;
    default:
        LogError("Invalid Hydrax noise module type.");
        return;
    }

    Hydrax::Module::Module *module = 0;
    switch(noiseModule.Get())
    {
    case ProjectedGrid:
        module = new Hydrax::Module::ProjectedGrid(impl->hydrax, noise, Ogre::Plane(Ogre::Vector3::UNIT_Y, Ogre::Vector3::ZERO),
            (Hydrax::MaterialManager::NormalMode)normalMode.Get());
        break;
    case RadialGrid:
        module = new Hydrax::Module::RadialGrid(impl->hydrax, noise, (Hydrax::MaterialManager::NormalMode)normalMode.Get());
        break;
    case SimpleGrid:
        module = new Hydrax::Module::SimpleGrid(impl->hydrax, noise, (Hydrax::MaterialManager::NormalMode)normalMode.Get());
        break;
    default:
        SAFE_DELETE(noise);
        LogError("Invalid Hydrax noise module type.");
        return;
    }
    //if (impl->module && noise)
    //    impl->module->setNoise(noise);

    impl->hydrax->setModule(module);
    impl->module = module;
    //impl->hydrax->loadCfg("HydraxDemo.hdx");
    impl->hydrax->create();
}
*/
void EC_Hydrax::Update(float frameTime)
{
    if (impl && impl->hydrax)
    {
        PROFILE(EC_Hydrax_Update);
#ifdef SKYX_ENABLED
        // Find out if we have SkyX in the scene. If yes, use its sun position.
        if (impl->skyX.expired())
        {
            EntityList entities = ParentEntity()->ParentScene()->GetEntitiesWithComponent(EC_SkyX::TypeNameStatic());
            if (!entities.empty())
                impl->skyX = (*entities.begin())->GetComponent<EC_SkyX>();
        }
        if (!impl->skyX.expired() && impl->hydrax->isCreated())
            impl->hydrax->setSunPosition(impl->skyX.lock()->SunPosition());
#endif
        if (impl->hydrax->isCreated())
            impl->hydrax->update(frameTime);
    }
}

void EC_Hydrax::ConfigLoadSucceeded(AssetPtr asset)
{
    if (!impl || !impl->hydrax || !impl->module)
    {
        LogError("EC_Hydrax: Could not apply loaded config, hydrax not initialized.");
        return;
    }

    std::vector<u8> rawData;
    asset->SerializeTo(rawData);
    QString configData = QString::fromAscii((const char*)&rawData[0], rawData.size());

    if (configData.isEmpty())
    {
        LogInfo("EC_Hydrax: Downloaded config is empty!");
        return;
    }

    try
    {
        // Update the noise module
        if (configData.contains("noise=fft", Qt::CaseInsensitive))
        {
            /// \note Using the FFT noise plugin seems to crash somewhere after we leave this function.
            /// FFT looks better so would be nice to investigate further!
            if (impl->module->getNoise()->getName() != "FFT")
                impl->module->setNoise(new Hydrax::Noise::FFT());
        }
        else if (configData.contains("noise=perlin", Qt::CaseInsensitive))
        {
            if (impl->module->getNoise()->getName() != "Perlin")
                impl->module->setNoise(new Hydrax::Noise::Perlin());
        }
        else
        {
            LogError("EC_Hydrax: Unknown noise param in loaded config, acceptable = FFT/Perlin.");
            return;
        }

        // Load config from the asset data string.
        impl->hydrax->remove();
        impl->hydrax->loadCfgString(configData.toStdString());
        impl->hydrax->create();
        impl->hydrax->setPosition(position.Get());
    }
    catch (Ogre::Exception &e)
    {
        LogError(std::string("EC_Hydrax: Ogre threw exception while loading new config: ") + e.what());
    }
}

void EC_Hydrax::LoadDefaultConfig()
{
    if (!impl || !impl->hydrax || !impl->module)
    {
        LogError("EC_Hydrax: Could not apply default config, hydrax not initialized.");
        return;
    }

    LogInfo(impl->module->getNoise()->getName());
    if (impl->module->getNoise()->getName() != "Perlin")
        impl->module->setNoise(new Hydrax::Noise::Perlin());

    // Load all parameters from the default config file we ship in /media/hydrax
    /// \todo Inspect if we can change the current ShaderMode to HLSL or GLSL on the fly here, depending on the platform!
    try
    {
        impl->hydrax->remove();
        impl->hydrax->loadCfg("HydraxDefault.hdx");
        impl->hydrax->create();
        impl->hydrax->setPosition(position.Get());
    }
    catch(Ogre::Exception &e)
    {
        LogError("EC_Hydrax failed to load default config: " + std::string(e.what()));
    }
}
<commit_msg>Hydrax: Fix a null check and remove debug print.<commit_after>/**
 *  For conditions of distribution and use, see copyright notice in license.txt
 *
 *  @file   EC_Hydrax.cpp
 *  @brief  A photorealistic water plane component using Hydrax, http://www.ogre3d.org/tikiwiki/Hydrax
 */

#define OGRE_INTEROP

#include "DebugOperatorNew.h"

#include "EC_Hydrax.h"

#include "Scene.h"
#include "Framework.h"
#include "FrameAPI.h"
#include "AssetAPI.h"
#include "IAssetTransfer.h"
#include "IAsset.h"

#include "OgreWorld.h"
#include "Renderer.h"
#include "EC_Camera.h"
#include "Entity.h"
#include "OgreConversionUtils.h"
#include "Profiler.h"

#include "AttributeMetadata.h"
#ifdef SKYX_ENABLED
#include "EC_SkyX.h"
#endif

#include <Hydrax.h>
#include <Noise/Perlin/Perlin.h>
#include <Noise/FFT/FFT.h>
#include <Modules/ProjectedGrid/ProjectedGrid.h>
#include <Modules/RadialGrid/RadialGrid.h>
#include <Modules/SimpleGrid/SimpleGrid.h>

#include "LoggingFunctions.h"
#include "MemoryLeakCheck.h"

struct EC_HydraxImpl
{
    EC_HydraxImpl() : hydrax(0), module(0) {}
    ~EC_HydraxImpl()
    {
        if (hydrax)
            hydrax->remove();
        SAFE_DELETE(hydrax);
        //SAFE_DELETE(module); ///< @todo Possible mem leak?
    }

    Hydrax::Hydrax *hydrax;
    Hydrax::Module::Module *module;
#ifdef SKYX_ENABLED
    boost::weak_ptr<EC_SkyX> skyX;
#endif
};

EC_Hydrax::EC_Hydrax(Scene* scene) :
    IComponent(scene),
    configRef(this, "Config ref", AssetReference("HydraxDefault.hdx")),
    visible(this, "Visible", true),
    position(this, "Position", float3(0.0, 0.0, 0.0)),
//    noiseModule(this, "Noise module", 0),
//    noiseType(this, "Noise type", 0),
//    normalMode(this, "Normal mode", 0),
    impl(0)
{
/*
    static AttributeMetadata noiseTypeMetadata;
    static AttributeMetadata normalModeMetadata;
    static bool metadataInitialized = false;
    if (!metadataInitialized)
    {
        noiseTypeMetadata.enums[Perlin] = "Perlin";
        noiseTypeMetadata.enums[FFT] = "FFT";

        normalModeMetadata.enums[Texture] = "Texture";
        normalModeMetadata.enums[Vertex] = "Vertex";
        normalModeMetadata.enums[RTT] = "RTT";

        metadataInitialized = true;
    }

    noiseType.SetMetadata(&noiseTypeMetadata);
    normalMode.SetMetadata(&normalModeMetadata);
*/
    OgreWorldPtr w = scene->GetWorld<OgreWorld>();
    if (!w)
    {
        LogError("EC_Hydrax: no OgreWorld available. Cannot be created.");
        return;
    }

    connect(w->GetRenderer(), SIGNAL(MainCameraChanged(Entity *)), SLOT(OnActiveCameraChanged(Entity *)));
    connect(this, SIGNAL(ParentEntitySet()), SLOT(Create()));
}

EC_Hydrax::~EC_Hydrax()
{
    SAFE_DELETE(impl);
}

void EC_Hydrax::Create()
{
    SAFE_DELETE(impl);

    if (framework->IsHeadless())
        return;

    try
    {
        if (!ParentScene())
        {
            LogError("EC_Hydrax: no parent scene. Cannot be created.");
            return;
        }

        OgreWorldPtr w = ParentScene()->GetWorld<OgreWorld>();
        assert(w);

        Entity *mainCamera = w->GetRenderer()->MainCamera();
        if (!mainCamera)
        {
            LogError("Cannot create EC_Hydrax: No main camera set!");
            return; // Can't create Hydrax just yet, no main camera set.
        }

        Ogre::Camera *cam = mainCamera->GetComponent<EC_Camera>()->GetCamera();
        impl = new EC_HydraxImpl();
        impl->hydrax = new Hydrax::Hydrax(w->GetSceneManager(), cam, w->GetRenderer()->MainViewport());

        // Using projected grid module by default
        Hydrax::Module::ProjectedGrid *module = new Hydrax::Module::ProjectedGrid(impl->hydrax, new Hydrax::Noise::Perlin(),
            Ogre::Plane(Ogre::Vector3::UNIT_Y, Ogre::Vector3::ZERO), Hydrax::MaterialManager::NM_VERTEX);
        impl->hydrax->setModule(module);
        impl->module = module;

        // Load all parameters from config file
        impl->hydrax->loadCfg(configRef.Get().ref.toStdString());
        impl->hydrax->create();
        impl->hydrax->setPosition(position.Get());

        connect(framework->Frame(), SIGNAL(PostFrameUpdate(float)), SLOT(Update(float)), Qt::UniqueConnection);
        connect(this, SIGNAL(AttributeChanged(IAttribute*, AttributeChange::Type)), SLOT(UpdateAttribute(IAttribute*)), Qt::UniqueConnection);
    }
    catch(Ogre::Exception &e)
    {
        // Currently if we try to create more than one Hydrax component we end up here due to Ogre internal name collision.
        LogError("Could not create EC_Hydrax: " + std::string(e.what()));
    }
}

void EC_Hydrax::OnActiveCameraChanged(Entity *newActiveCamera)
{
    if (!newActiveCamera)
    {
        SAFE_DELETE(impl);
        return;
    }
    // If we haven't yet initialized, do a full init.
    if (!impl)
        Create();
    else // Otherwise, update the camera to an existing initialized Hydrax instance.
        if (impl && impl->hydrax)
            impl->hydrax->setCamera(newActiveCamera->GetComponent<EC_Camera>()->GetCamera());
}

void EC_Hydrax::UpdateAttribute(IAttribute *attr)
{
    if (attr == &configRef)
    {
        if (!configRef.Get().ref.isEmpty())
        {
            AssetTransferPtr transfer = framework->Asset()->RequestAsset(configRef.Get().ref, "Binary");
            if (transfer.get())
                connect(transfer.get(), SIGNAL(Succeeded(AssetPtr)), SLOT(ConfigLoadSucceeded(AssetPtr)), Qt::UniqueConnection);
        }
        else
            LoadDefaultConfig();
    }

    if (!impl || !impl->hydrax)
        return;
    else if (attr == &visible)
        impl->hydrax->setVisible(visible.Get());
    else if (attr == &position)
        impl->hydrax->setPosition(position.Get());
/*
    else if (attr == &noiseModule || attr == &normalMode || &noiseType)
        UpdateNoiseModule();
*/
}
/*
void EC_Hydrax::UpdateNoiseModule()
{
    Hydrax::Noise::Noise *noise = 0;
    switch(noiseType.Get())
    {
    case Perlin:
        noise = new Hydrax::Noise::Perlin();
        break;
    case FFT:
        noise = new Hydrax::Noise::FFT();
        break;
    default:
        LogError("Invalid Hydrax noise module type.");
        return;
    }

    Hydrax::Module::Module *module = 0;
    switch(noiseModule.Get())
    {
    case ProjectedGrid:
        module = new Hydrax::Module::ProjectedGrid(impl->hydrax, noise, Ogre::Plane(Ogre::Vector3::UNIT_Y, Ogre::Vector3::ZERO),
            (Hydrax::MaterialManager::NormalMode)normalMode.Get());
        break;
    case RadialGrid:
        module = new Hydrax::Module::RadialGrid(impl->hydrax, noise, (Hydrax::MaterialManager::NormalMode)normalMode.Get());
        break;
    case SimpleGrid:
        module = new Hydrax::Module::SimpleGrid(impl->hydrax, noise, (Hydrax::MaterialManager::NormalMode)normalMode.Get());
        break;
    default:
        SAFE_DELETE(noise);
        LogError("Invalid Hydrax noise module type.");
        return;
    }
    //if (impl->module && noise)
    //    impl->module->setNoise(noise);

    impl->hydrax->setModule(module);
    impl->module = module;
    //impl->hydrax->loadCfg("HydraxDemo.hdx");
    impl->hydrax->create();
}
*/
void EC_Hydrax::Update(float frameTime)
{
    if (impl && impl->hydrax)
    {
        PROFILE(EC_Hydrax_Update);
#ifdef SKYX_ENABLED
        // Find out if we have SkyX in the scene. If yes, use its sun position.
        if (impl->skyX.expired())
        {
            EntityList entities = ParentEntity()->ParentScene()->GetEntitiesWithComponent(EC_SkyX::TypeNameStatic());
            if (!entities.empty())
                impl->skyX = (*entities.begin())->GetComponent<EC_SkyX>();
        }
        if (!impl->skyX.expired() && impl->hydrax->isCreated())
            impl->hydrax->setSunPosition(impl->skyX.lock()->SunPosition());
#endif
        if (impl->hydrax->isCreated())
            impl->hydrax->update(frameTime);
    }
}

void EC_Hydrax::ConfigLoadSucceeded(AssetPtr asset)
{
    if (!impl || !impl->hydrax || !impl->module)
    {
        LogError("EC_Hydrax: Could not apply loaded config, hydrax not initialized.");
        return;
    }

    std::vector<u8> rawData;
    asset->SerializeTo(rawData);
    QString configData = QString::fromAscii((const char*)&rawData[0], rawData.size());

    if (configData.isEmpty())
    {
        LogInfo("EC_Hydrax: Downloaded config is empty!");
        return;
    }

    try
    {
        // Update the noise module
        if (configData.contains("noise=fft", Qt::CaseInsensitive))
        {
            /// \note Using the FFT noise plugin seems to crash somewhere after we leave this function.
            /// FFT looks better so would be nice to investigate further!
            if (impl->module->getNoise()->getName() != "FFT")
                impl->module->setNoise(new Hydrax::Noise::FFT());
        }
        else if (configData.contains("noise=perlin", Qt::CaseInsensitive))
        {
            if (impl->module->getNoise()->getName() != "Perlin")
                impl->module->setNoise(new Hydrax::Noise::Perlin());
        }
        else
        {
            LogError("EC_Hydrax: Unknown noise param in loaded config, acceptable = FFT/Perlin.");
            return;
        }

        // Load config from the asset data string.
        impl->hydrax->remove();
        impl->hydrax->loadCfgString(configData.toStdString());
        impl->hydrax->create();
        impl->hydrax->setPosition(position.Get());
    }
    catch (Ogre::Exception &e)
    {
        LogError(std::string("EC_Hydrax: Ogre threw exception while loading new config: ") + e.what());
    }
}

void EC_Hydrax::LoadDefaultConfig()
{
    if (!impl || !impl->hydrax || !impl->module)
    {
        LogError("EC_Hydrax: Could not apply default config, hydrax not initialized.");
        return;
    }

    if (impl->module->getNoise()->getName() != "Perlin")
        impl->module->setNoise(new Hydrax::Noise::Perlin());

    // Load all parameters from the default config file we ship in /media/hydrax
    /// \todo Inspect if we can change the current ShaderMode to HLSL or GLSL on the fly here, depending on the platform!
    try
    {
        impl->hydrax->remove();
        impl->hydrax->loadCfg("HydraxDefault.hdx");
        impl->hydrax->create();
        impl->hydrax->setPosition(position.Get());
    }
    catch(Ogre::Exception &e)
    {
        LogError("EC_Hydrax failed to load default config: " + std::string(e.what()));
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Global Phasing Ltd.
//
// Functions for transparent reading of gzipped files. Uses zlib.

#ifndef GEMMI_GZ_HPP_
#define GEMMI_GZ_HPP_
#include "util.hpp"     // iends_with
#include "fileutil.hpp" // file_open
#include "input.hpp"    // MaybeStdin
#include <cstdio>       // fseek, ftell, fread
#include <memory>
#include <string>
#include <zlib.h>

namespace gemmi {

// Throws if the size is not found or if it is suspicious.
// Anything outside of the arbitrary limits from 1 to 10x of the compressed
// size looks suspicious to us.
inline size_t estimate_uncompressed_size(const std::string& path) {
  fileptr_t f = file_open(path.c_str(), "rb");
  if (std::fseek(f.get(), -4, SEEK_END) != 0)
    fail("fseek() failed (empty file?): " + path);
  long pos = std::ftell(f.get());
  if (pos <= 0)
    fail("ftell() failed on " + path);
  size_t gzipped_size = pos + 4;
  unsigned char buf[4];
  if (std::fread(buf, 1, 4, f.get()) != 4)
    fail("Failed to read last 4 bytes of: " + path);
  size_t orig_size = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
  if (orig_size + 100 < gzipped_size || orig_size > 100 * gzipped_size)
    fail("Cannot determine uncompressed size of " + path +
         "\nWould it be " + std::to_string(gzipped_size) + " -> " +
         std::to_string(orig_size) + " bytes?");
  return orig_size;
}

class MaybeGzipped : public MaybeStdin {
public:
  struct GzStream {
    gzFile f;
    explicit operator bool() const { return f; }
    char* gets(char* line, int size) { return gzgets(f, line, size); }
    int getc() { return gzgetc(f); }
    bool read(void* buf, int len) { return gzread(f, buf, len) == len; }
  };

  explicit MaybeGzipped(const std::string& path)
    : MaybeStdin(path), memory_size_(0), file_(nullptr) {}
  ~MaybeGzipped() {
    if (file_)
#if ZLIB_VERNUM >= 0x1235
      gzclose_r(file_);
#else
      gzclose(file_);
#endif
  }

  bool is_compressed() const { return iends_with(path(), ".gz"); }
  std::string basepath() const {
    return is_compressed() ? path().substr(0, path().size() - 3) : path();
  }
  size_t memory_size() const { return memory_size_; }

  std::unique_ptr<char[]> memory() {
    if (!is_compressed())
      return MaybeStdin::memory();
    memory_size_ = estimate_uncompressed_size(path());
    open();
    if (memory_size_ > 500000000)
      fail("For now gz files above 500MB uncompressed are not supported.");
    std::unique_ptr<char[]> mem(new char[memory_size_]);
    int bytes_read = gzread(file_, mem.get(), (unsigned) memory_size_);
    if (bytes_read < (int) memory_size_ && !gzeof(file_)) {
      int errnum;
      std::string err_str = gzerror(file_, &errnum);
      if (errnum)
        fail("Error reading " + path() + ": " + err_str);
    }
    return mem;
  }

  GzStream get_stream() {
    if (!is_compressed())
      return GzStream{nullptr};
    open();
#if ZLIB_VERNUM >= 0x1235
    gzbuffer(file_, 64*1024);
#endif
    return GzStream{file_};
  }

private:
  size_t memory_size_;
  gzFile file_;

  void open() {
    file_ = gzopen(path().c_str(), "rb");
    if (!file_)
      fail("Failed to gzopen: " + path());
  }
};

} // namespace gemmi

#endif
<commit_msg>increase the limit on files ungzipped in the memory to 2 GiB<commit_after>// Copyright 2017 Global Phasing Ltd.
//
// Functions for transparent reading of gzipped files. Uses zlib.

#ifndef GEMMI_GZ_HPP_
#define GEMMI_GZ_HPP_
#include "util.hpp"     // iends_with
#include "fileutil.hpp" // file_open
#include "input.hpp"    // MaybeStdin
#include <cstdio>       // fseek, ftell, fread
#include <memory>
#include <string>
#include <zlib.h>

namespace gemmi {

// Throws if the size is not found or if it is suspicious.
// Anything outside of the arbitrary limits from 1 to 10x of the compressed
// size looks suspicious to us.
inline size_t estimate_uncompressed_size(const std::string& path) {
  fileptr_t f = file_open(path.c_str(), "rb");
  if (std::fseek(f.get(), -4, SEEK_END) != 0)
    fail("fseek() failed (empty file?): " + path);
  long pos = std::ftell(f.get());
  if (pos <= 0)
    fail("ftell() failed on " + path);
  size_t gzipped_size = pos + 4;
  unsigned char buf[4];
  if (std::fread(buf, 1, 4, f.get()) != 4)
    fail("Failed to read last 4 bytes of: " + path);
  unsigned orig_size = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
  if (orig_size + 100 < gzipped_size || orig_size > 100 * gzipped_size)
    fail("Cannot determine uncompressed size of " + path +
         "\nWould it be " + std::to_string(gzipped_size) + " -> " +
         std::to_string(orig_size) + " bytes?");
  return orig_size;
}

class MaybeGzipped : public MaybeStdin {
public:
  struct GzStream {
    gzFile f;
    explicit operator bool() const { return f; }
    char* gets(char* line, int size) { return gzgets(f, line, size); }
    int getc() { return gzgetc(f); }
    bool read(void* buf, int len) { return gzread(f, buf, len) == len; }
  };

  explicit MaybeGzipped(const std::string& path)
    : MaybeStdin(path), memory_size_(0), file_(nullptr) {}
  ~MaybeGzipped() {
    if (file_)
#if ZLIB_VERNUM >= 0x1235
      gzclose_r(file_);
#else
      gzclose(file_);
#endif
  }

  bool is_compressed() const { return iends_with(path(), ".gz"); }
  std::string basepath() const {
    return is_compressed() ? path().substr(0, path().size() - 3) : path();
  }
  size_t memory_size() const { return memory_size_; }

  std::unique_ptr<char[]> memory() {
    if (!is_compressed())
      return MaybeStdin::memory();
    memory_size_ = estimate_uncompressed_size(path());
    open();
    if (memory_size_ > 2147483647)
      fail("For now gz files above 2 GiB uncompressed are not supported.");
    std::unique_ptr<char[]> mem(new char[memory_size_]);
    int bytes_read = gzread(file_, mem.get(), (unsigned) memory_size_);
    if (bytes_read < (int) memory_size_ && !gzeof(file_)) {
      int errnum;
      std::string err_str = gzerror(file_, &errnum);
      if (errnum)
        fail("Error reading " + path() + ": " + err_str);
    }
    return mem;
  }

  GzStream get_stream() {
    if (!is_compressed())
      return GzStream{nullptr};
    open();
#if ZLIB_VERNUM >= 0x1235
    gzbuffer(file_, 64*1024);
#endif
    return GzStream{file_};
  }

private:
  size_t memory_size_;
  gzFile file_;

  void open() {
    file_ = gzopen(path().c_str(), "rb");
    if (!file_)
      fail("Failed to gzopen: " + path());
  }
};

} // namespace gemmi

#endif
<|endoftext|>


<commit_before>#pragma once

#include <cassert>
#include <cstddef>
#include <cstdint>

template<class T, T Max>
class histogram_log2
{
  static constexpr std::size_t
  floor_log2_const(T x)
  {
    return (x == 0) ? (1/x)
      : (x == 1) ? 0
      : 1 + floor_log2_const(x >> 1);
  }

  static T
  floor_log2(T x)
  {
    return 8 * sizeof(long long) - __builtin_clzll(x) - 1;
  }

  T sum_, min_, max_;
  T zero_;
  T buckets_[floor_log2_const(Max)];
  T over_;
  enum { NBUCKETS = sizeof(buckets_) / sizeof(buckets_[0]) };

  void
  get_print_stats(unsigned *lwidth, unsigned *min_bucket,
                  unsigned *max_bucket) const
  {
    // Compute the label width.  This is fixed, rather than being
    // based on used buckets, so that multiple histograms are
    // comparable.
    char buf[32];
    snprintf(buf, sizeof buf, "%llu", (long long unsigned)Max);
    *lwidth = strlen(buf);

    // Find the minimum used bucket
    *min_bucket = 0;
    if (!zero_)
      for (; *min_bucket < NBUCKETS; ++*min_bucket)
        if (buckets_[*min_bucket])
          break;

    // Find maximum used bucket (plus one, so we always end with a
    // zero label)
    if (over_) {
      *max_bucket = NBUCKETS;
    } else {
      *max_bucket = 0;
      for (std::size_t i = 0; i < NBUCKETS; ++i)
        if (buckets_[i])
          *max_bucket = i;
      if (*max_bucket < NBUCKETS)
        ++*max_bucket;
    }
  }

  static const char *
  get_bar(unsigned width)
  {
    static const char bar[] = "================================================================================";
    unsigned barlen = sizeof(bar) - 1;
    assert(width <= barlen);
    return &bar[barlen - width];
  }

public:
  constexpr histogram_log2()
    : sum_(0), min_(~0), max_(0), zero_(0), buckets_{}, over_(0)
  {
  }

  histogram_log2 &
  operator+=(T val)
  {
    sum_ += val;
    if (val < min_)
      min_ = val;
    if (val > max_)
      max_ = val;
    if (val <= 0)
      ++zero_;
    else if (val >= Max)
      ++over_;
    else
      ++buckets_[floor_log2(val)];
    return *this;
  }

  histogram_log2 &
  operator+=(const histogram_log2 &other)
  {
    sum_ += other.sum_;
    if (other.min_ < min_)
      min_ = other.min_;
    if (other.max_ > max_)
      max_ = other.max_;
    zero_ += other.zero_;
    for (std::size_t i = 0; i < NBUCKETS; ++i)
      buckets_[i] += other.buckets_[i];
    over_ += other.over_;
    return *this;
  }

  T
  sum() const
  {
    return sum_;
  }

  T
  min() const
  {
    return min_;
  }

  T
  max() const
  {
    return max_;
  }

  T
  count() const
  {
    T res = zero_ + over_;
    for (T v : buckets_)
      res += v;
    return res;
  }

  T
  mean() const
  {
    return sum_ / count();
  }

  double
  meand() const
  {
    return sum_ / (double)count();
  }

  void
  print_stats() const
  {
    printf("count %llu  min %llu  mean %llu  max %llu\n",
           (long long unsigned)count(), (long long unsigned)min(),
           (long long unsigned)mean(), (long long unsigned)max());
  }

  void
  print() const
  {
    unsigned lwidth, min_bucket, max_bucket;
    get_print_stats(&lwidth, &min_bucket, &max_bucket);

    if (zero_)
      printf("%*llu- : %llu\n", lwidth, 0ull, (long long unsigned)zero_);
    for (unsigned i = min_bucket; i <= max_bucket; ++i) {
      bool last = i == max_bucket;
      if (i == NBUCKETS)
        printf("%*llu..: %llu\n", lwidth, (long long unsigned)Max,
               (long long unsigned)over_);
      else
        printf("%*llu%s: %llu\n", lwidth, 1ull<<i,
               last ? "  " : "- ", (long long unsigned)buckets_[i]);
    }
  }

  void
  print_bars() const
  {
    unsigned lwidth, min_bucket, max_bucket;
    get_print_stats(&lwidth, &min_bucket, &max_bucket);

    unsigned max_count = 0;
    for (auto v : buckets_)
      if (v > max_count)
        max_count = v;

    unsigned bar_width = 75 - lwidth;
    if (zero_)
      printf("%*llu- : %s\n", lwidth, 0ull, get_bar(bar_width * zero_ / max_count));
    for (unsigned i = min_bucket; i <= max_bucket; ++i) {
      bool last = i == max_bucket;
      if (i == NBUCKETS)
        printf("%*llu..: %s\n", lwidth, (long long unsigned)Max,
               get_bar(bar_width * over_ / max_count));
      else
        printf("%*llu%s: %s\n", lwidth, 1ull<<i,
               last ? "  " : "- ",
               get_bar(bar_width * buckets_[i] / max_count));
    }
  }
};
<commit_msg>histogram: Don't crash with zero samples<commit_after>#pragma once

#include <cassert>
#include <cstddef>
#include <cstdint>

template<class T, T Max>
class histogram_log2
{
  static constexpr std::size_t
  floor_log2_const(T x)
  {
    return (x == 0) ? (1/x)
      : (x == 1) ? 0
      : 1 + floor_log2_const(x >> 1);
  }

  static T
  floor_log2(T x)
  {
    return 8 * sizeof(long long) - __builtin_clzll(x) - 1;
  }

  T sum_, min_, max_;
  T zero_;
  T buckets_[floor_log2_const(Max)];
  T over_;
  enum { NBUCKETS = sizeof(buckets_) / sizeof(buckets_[0]) };

  void
  get_print_stats(unsigned *lwidth, unsigned *min_bucket,
                  unsigned *max_bucket) const
  {
    // Compute the label width.  This is fixed, rather than being
    // based on used buckets, so that multiple histograms are
    // comparable.
    char buf[32];
    snprintf(buf, sizeof buf, "%llu", (long long unsigned)Max);
    *lwidth = strlen(buf);

    // Find the minimum used bucket
    *min_bucket = 0;
    if (!zero_)
      for (; *min_bucket < NBUCKETS; ++*min_bucket)
        if (buckets_[*min_bucket])
          break;

    // Find maximum used bucket (plus one, so we always end with a
    // zero label)
    if (over_) {
      *max_bucket = NBUCKETS;
    } else {
      *max_bucket = 0;
      for (std::size_t i = 0; i < NBUCKETS; ++i)
        if (buckets_[i])
          *max_bucket = i;
      if (*max_bucket < NBUCKETS)
        ++*max_bucket;
    }
  }

  static const char *
  get_bar(unsigned width)
  {
    static const char bar[] = "================================================================================";
    unsigned barlen = sizeof(bar) - 1;
    assert(width <= barlen);
    return &bar[barlen - width];
  }

public:
  constexpr histogram_log2()
    : sum_(0), min_(~0), max_(0), zero_(0), buckets_{}, over_(0)
  {
  }

  histogram_log2 &
  operator+=(T val)
  {
    sum_ += val;
    if (val < min_)
      min_ = val;
    if (val > max_)
      max_ = val;
    if (val <= 0)
      ++zero_;
    else if (val >= Max)
      ++over_;
    else
      ++buckets_[floor_log2(val)];
    return *this;
  }

  histogram_log2 &
  operator+=(const histogram_log2 &other)
  {
    sum_ += other.sum_;
    if (other.min_ < min_)
      min_ = other.min_;
    if (other.max_ > max_)
      max_ = other.max_;
    zero_ += other.zero_;
    for (std::size_t i = 0; i < NBUCKETS; ++i)
      buckets_[i] += other.buckets_[i];
    over_ += other.over_;
    return *this;
  }

  T
  sum() const
  {
    return sum_;
  }

  T
  min() const
  {
    return min_;
  }

  T
  max() const
  {
    return max_;
  }

  T
  count() const
  {
    T res = zero_ + over_;
    for (T v : buckets_)
      res += v;
    return res;
  }

  T
  mean() const
  {
    return sum_ / count();
  }

  double
  meand() const
  {
    return sum_ / (double)count();
  }

  void
  print_stats() const
  {
    if (count() == 0) {
      printf("count 0\n");
      return;
    }
    printf("count %llu  min %llu  mean %llu  max %llu\n",
           (long long unsigned)count(), (long long unsigned)min(),
           (long long unsigned)mean(), (long long unsigned)max());
  }

  void
  print() const
  {
    unsigned lwidth, min_bucket, max_bucket;
    get_print_stats(&lwidth, &min_bucket, &max_bucket);

    if (zero_)
      printf("%*llu- : %llu\n", lwidth, 0ull, (long long unsigned)zero_);
    for (unsigned i = min_bucket; i <= max_bucket; ++i) {
      bool last = i == max_bucket;
      if (i == NBUCKETS)
        printf("%*llu..: %llu\n", lwidth, (long long unsigned)Max,
               (long long unsigned)over_);
      else
        printf("%*llu%s: %llu\n", lwidth, 1ull<<i,
               last ? "  " : "- ", (long long unsigned)buckets_[i]);
    }
  }

  void
  print_bars() const
  {
    unsigned lwidth, min_bucket, max_bucket;
    get_print_stats(&lwidth, &min_bucket, &max_bucket);

    unsigned max_count = 0;
    for (auto v : buckets_)
      if (v > max_count)
        max_count = v;
    if (max_count == 0) {
      printf("%*llu..: \n", lwidth, 0ull);
      return;
    }

    unsigned bar_width = 75 - lwidth;
    if (zero_)
      printf("%*llu- : %s\n", lwidth, 0ull, get_bar(bar_width * zero_ / max_count));
    for (unsigned i = min_bucket; i <= max_bucket; ++i) {
      bool last = i == max_bucket;
      if (i == NBUCKETS)
        printf("%*llu..: %s\n", lwidth, (long long unsigned)Max,
               get_bar(bar_width * over_ / max_count));
      else
        printf("%*llu%s: %s\n", lwidth, 1ull<<i,
               last ? "  " : "- ",
               get_bar(bar_width * buckets_[i] / max_count));
    }
  }
};
<|endoftext|>


<commit_before>// -*- coding:utf-8-unix; -*-
#include <stdint.h>
#include "macros.hh"
#ifdef __clang__
#include <x86intrin.h>
#endif
//
// http://www.1024cores.net/home/lock-free-algorithms/tricks/per-processor-data
//
namespace NAMESPACE {
//
class Processor {
 public:
  static inline uint32_t Current() {
    _timestap = __builtin_ia32_rdtscp(&_number);
    return _number;
  }
  //
  static inline uint32_t Timestap() {
    _timestap = __builtin_ia32_rdtscp(&_number);
    return _timestap;
  }
  //
  static inline uint32_t Rdscp(uint32_t* cpu) {
    return __builtin_ia32_rdtscp(cpu);
  }
 protected:
 private:
  static __thread uint32_t _number;
  static __thread uint32_t _timestap;
}; // class Processor
//
__thread uint32_t Processor::_number = 0;
__thread uint32_t Processor::_timestap = 0;
} // namespace NAMESPACE
<commit_msg>Revert "fix build"<commit_after>// -*- coding:utf-8-unix; -*-
#include <stdint.h>
#include "macros.hh"
//
// http://www.1024cores.net/home/lock-free-algorithms/tricks/per-processor-data
//
namespace NAMESPACE {
//
class Processor {
 public:
  static inline uint32_t Current() {
    _timestap = __builtin_ia32_rdtscp(&_number);
    return _number;
  }
  //
  static inline uint32_t Timestap() {
    _timestap = __builtin_ia32_rdtscp(&_number);
    return _timestap;
  }
  //
  static inline uint32_t Rdscp(uint32_t* cpu) {
    return __builtin_ia32_rdtscp(cpu);
  }
 protected:
 private:
  static __thread uint32_t _number;
  static __thread uint32_t _timestap;
}; // class Processor
//
__thread uint32_t Processor::_number = 0;
__thread uint32_t Processor::_timestap = 0;
} // namespace NAMESPACE
<|endoftext|>


<commit_before><commit_msg>Improve example<commit_after><|endoftext|>


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

#include <cmath>

#include "SurgSim/DataStructures/Image.h"
#include "SurgSim/DataStructures/ImageMap.h"
#include "SurgSim/Graphics/OsgMaterial.h"
#include "SurgSim/Graphics/PaintBehavior.h"
#include "SurgSim/Math/Scalar.h"

namespace SurgSim
{
namespace Graphics
{
PaintBehavior::PaintBehavior(const std::string& name) :
	Framework::Behavior(name),
	m_width(0),
	m_height(0),
	m_radius(0)
{
	SURGSIM_ADD_SERIALIZABLE_PROPERTY(PaintBehavior, std::shared_ptr<Framework::Component>, Representation,
									  getRepresentation, setRepresentation);
	SURGSIM_ADD_SERIALIZABLE_PROPERTY(PaintBehavior, Math::Vector4d, Color, getColor, setColor);
}

void PaintBehavior::setRepresentation(std::shared_ptr<Framework::Component> representation)
{
	m_representation = Framework::checkAndConvert<Graphics::OsgMeshRepresentation>(representation,
																				   "SurgSim::Graphics::Representation");
}

std::shared_ptr<Graphics::OsgMeshRepresentation> PaintBehavior::getRepresentation() const
{
	return m_representation;
}

void PaintBehavior::setColor(const Math::Vector4d& color)
{
	m_color = color;
}

Math::Vector4d PaintBehavior::getColor() const
{
	return m_color;
}

void PaintBehavior::setCoordinates(const std::vector<DataStructures::IndexedLocalCoordinate>& coordinates)
{
	boost::unique_lock<boost::mutex> lock(m_mutex);
	m_coordinates = coordinates;
}

bool PaintBehavior::doInitialize()
{
	m_texture = std::make_shared<OsgTexture2d>();
	m_texture->setSize(m_width, m_height);
	osg::Texture* osgTexture = m_texture->getOsgTexture();
	osgTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
	osgTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
	osgTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
	osgTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
	osgTexture->setInternalFormat(GL_RGBA32F_ARB);
	osgTexture->setSourceFormat(GL_RGBA);
	osgTexture->setSourceType(GL_BYTE);

	int textureSize = m_width * m_height * 4;
	auto data = new unsigned char[textureSize]();
	for (int i = 0; i < textureSize; i++)
	{
		data[i] = 0;
	}

	osg::ref_ptr<osg::Image> image = new osg::Image();
	image->setImage(m_width, m_height, 1, GL_RGBA_INTEGER, GL_RGBA, GL_BYTE, data, osg::Image::NO_DELETE);

	m_texture->getOsgTexture2d()->setImage(image);

	m_representation->getMaterial()->addUniform("sampler2D", "paintMap");
	m_representation->getMaterial()->setValue("paintMap", m_texture);

	return true;
}

bool PaintBehavior::doWakeUp()
{
	if (m_representation == nullptr)
	{
		SURGSIM_LOG_SEVERE(Framework::Logger::getDefaultLogger()) << getClassName() << " named " << getName() <<
									 " does not have a graphics representation to paint into";
		return false;
	}

	if (m_texture == nullptr)
	{
		SURGSIM_LOG_SEVERE(Framework::Logger::getDefaultLogger()) << getClassName() << " named " << getName() <<
									 " does not have a texture to paint onto";
		return false;
	}

	if (m_radius <= 0.0 || m_radius > 1.0)
	{
		SURGSIM_LOG_SEVERE(Framework::Logger::getDefaultLogger()) << getClassName() << " named " << getName() <<
									" does not have a radius set";
		return false;
	}

	buildBrush(getRadius());

	return true;
}

void PaintBehavior::update(double dt)
{
	std::vector<Math::Vector2d> uvCoordinates;

	{
		boost::unique_lock<boost::mutex> lock(m_mutex);
		auto mesh = m_representation->getMesh();
		for (auto coordinate : m_coordinates)
		{
			if (coordinate.index < mesh->getNumTriangles())
			{
				auto vertex1 = mesh->getVertex(mesh->getTriangle(coordinate.index).verticesId[0]);
				auto vertex2 = mesh->getVertex(mesh->getTriangle(coordinate.index).verticesId[1]);
				auto vertex3 = mesh->getVertex(mesh->getTriangle(coordinate.index).verticesId[2]);
				Math::Vector2d uv = vertex1.data.texture.getValue() * coordinate.coordinate[0] +
									vertex2.data.texture.getValue() * coordinate.coordinate[1] +
									vertex3.data.texture.getValue() * coordinate.coordinate[2];
				uvCoordinates.push_back(uv);
			}
		}
	}

	for (auto& uv : uvCoordinates)
	{
		Math::Vector2d xy = toPixel(uv);

		paint(xy);

		m_texture->getOsgTexture2d()->dirtyTextureObject();
	}
}

void PaintBehavior::setRadius(double radius)
{
		m_radius = radius;
}

double PaintBehavior::getRadius() const
{
	return m_radius;
}

void PaintBehavior::buildBrush(double radius)
{
	int brushWidth = static_cast<int>(ceil(2 * radius * m_width));
	int brushHeight = static_cast<int>(ceil(2 * radius * m_height));

	int centerX = brushWidth / 2;
	int centerY = brushHeight / 2;

	m_brushOffsetX = -1 * centerX;
	m_brushOffsetY = -1 * centerY;

	m_brush.resize(brushWidth, brushHeight);

	for (int i = 0; i < m_brush.cols(); i++)
	{
		for (int j = 0; j < m_brush.rows(); j++)
		{
			double mag = ((i - centerX) * (i - centerX)) + ((j - centerY) * (j - centerY));
			if (sqrt(mag) < m_brush.cols() / 2.0)
			{
				m_brush(i, j) = 1.0;
			}
			else
			{
				m_brush(i, j) = 0.0;
			}
		}
	}
}

void PaintBehavior::buildAntiAliasedBrush(double radius)
{
	static const double sqrt2Half = 0.7071067811865475;
	double dist;
	int brushWidth = static_cast<int>(ceil(2 * radius * m_width));
	int brushHeight = static_cast<int>(ceil(2 * radius * m_height));

	int centerX = brushWidth / 2;
	int centerY = brushHeight / 2;

	m_brushOffsetX = -1 * centerX;
	m_brushOffsetY = -1 * centerY;

	m_brush.resize(brushWidth, brushHeight);

	for (int i = 0; i < m_brush.cols(); i++)
	{
		for (int j = 0; j < m_brush.rows(); j++)
		{
			double mag = (i - centerX) * (i - centerX) + (j - centerY) * (j - centerY);
			dist = (m_brush.cols() / 2) - sqrt(mag);
			dist /= m_brush.cols() / 2;
			if (dist > sqrt2Half)
			{
				m_brush(i, j) = 1.0;
			}
			else if (dist < 0.0)
			{
				m_brush(i, j) = 0.0;
			}
			else
			{
				m_brush(i, j) = sqrt(2) * dist;
			}
		}
	}
}

Math::Vector2d PaintBehavior::toPixel(const Math::Vector2d& uv)
{
	double s = uv[0];
	double t = uv[1];

	s = Math::clamp(s, 0.0, 1.0, 0.0);
	t = Math::clamp(t, 0.0, 1.0, 0.0);

	Math::Vector2d xy;
	xy << floor(s * m_width + 0.5), floor(t * m_height + 0.5);

	return xy;
}

void PaintBehavior::paint(const Math::Vector2d& coordinates)
{
	size_t numChannels = 4;

	DataStructures::ImageMap<unsigned char> image(m_width,
												  m_height,
												  numChannels,
												  m_texture->getOsgTexture2d()->getImage()->data());

	size_t topLeftX = static_cast<size_t>(std::max(0.0, coordinates[0] + m_brushOffsetX));
	size_t topLeftY = static_cast<size_t>(std::max(0.0, coordinates[1] + m_brushOffsetY));
	size_t bottomRightX = std::min(m_width - topLeftX, static_cast<size_t>(m_brush.cols()));
	size_t bottomRightY = std::min(m_height - topLeftY, static_cast<size_t>(m_brush.rows()));
	for (size_t channel = 0; channel < numChannels; channel++)
	{
		auto imageBlock = image.getChannel(channel).block(topLeftX, topLeftY, bottomRightX, bottomRightY);
		auto brushBlock = m_brush.topLeftCorner(bottomRightX, bottomRightY);
		imageBlock = (brushBlock.array() > 0).select((brushBlock * m_color(channel)*255).template cast<unsigned char>(),
						imageBlock);
	}
}

void PaintBehavior::setTextureSize(int width, int height)
{
	m_width = width;
	m_height = height;
}

} // Graphics
} // SurgSim<commit_msg>Use texture uniform instead of convenience method<commit_after>// This file is a part of the OpenSurgSim project.
// Copyright 2016, SimQuest Solutions Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <cmath>

#include "SurgSim/DataStructures/Image.h"
#include "SurgSim/DataStructures/ImageMap.h"
#include "SurgSim/Graphics/OsgMaterial.h"
#include "SurgSim/Graphics/PaintBehavior.h"
#include "SurgSim/Math/Scalar.h"

namespace SurgSim
{
namespace Graphics
{
PaintBehavior::PaintBehavior(const std::string& name) :
	Framework::Behavior(name),
	m_width(0),
	m_height(0),
	m_radius(0)
{
	SURGSIM_ADD_SERIALIZABLE_PROPERTY(PaintBehavior, std::shared_ptr<Framework::Component>, Representation,
									  getRepresentation, setRepresentation);
	SURGSIM_ADD_SERIALIZABLE_PROPERTY(PaintBehavior, Math::Vector4d, Color, getColor, setColor);
}

void PaintBehavior::setRepresentation(std::shared_ptr<Framework::Component> representation)
{
	m_representation = Framework::checkAndConvert<Graphics::OsgMeshRepresentation>(representation,
																				   "SurgSim::Graphics::Representation");
}

std::shared_ptr<Graphics::OsgMeshRepresentation> PaintBehavior::getRepresentation() const
{
	return m_representation;
}

void PaintBehavior::setColor(const Math::Vector4d& color)
{
	m_color = color;
}

Math::Vector4d PaintBehavior::getColor() const
{
	return m_color;
}

void PaintBehavior::setCoordinates(const std::vector<DataStructures::IndexedLocalCoordinate>& coordinates)
{
	boost::unique_lock<boost::mutex> lock(m_mutex);
	m_coordinates = coordinates;
}

bool PaintBehavior::doInitialize()
{
	auto textureUniform = std::make_shared<Graphics::OsgTextureUniform<Graphics::OsgTexture2d>>("paintMap");

	m_texture = std::make_shared<OsgTexture2d>();
	m_texture->setSize(m_width, m_height);
	osg::Texture* osgTexture = m_texture->getOsgTexture();
	osgTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
	osgTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
	osgTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
	osgTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
	osgTexture->setInternalFormat(GL_RGBA32F_ARB);
	osgTexture->setSourceFormat(GL_RGBA);
	osgTexture->setSourceType(GL_BYTE);

	int textureSize = m_width * m_height * 4;
	auto data = new unsigned char[textureSize]();
	for (int i = 0; i < textureSize; i++)
	{
		data[i] = 0;
	}

	osg::ref_ptr<osg::Image> image = new osg::Image();
	image->setImage(m_width, m_height, 1, GL_RGBA32F_ARB, GL_RGBA, GL_BYTE, data, osg::Image::NO_DELETE);

	m_texture->getOsgTexture2d()->setImage(image);
	textureUniform->set(m_texture);
	m_representation->getMaterial()->addUniform(textureUniform);
	return true;
}

bool PaintBehavior::doWakeUp()
{
	if (m_representation == nullptr)
	{
		SURGSIM_LOG_SEVERE(Framework::Logger::getDefaultLogger()) << getClassName() << " named " << getName() <<
									 " does not have a graphics representation to paint into";
		return false;
	}

	if (m_texture == nullptr)
	{
		SURGSIM_LOG_SEVERE(Framework::Logger::getDefaultLogger()) << getClassName() << " named " << getName() <<
									 " does not have a texture to paint onto";
		return false;
	}

	if (m_radius <= 0.0 || m_radius > 1.0)
	{
		SURGSIM_LOG_SEVERE(Framework::Logger::getDefaultLogger()) << getClassName() << " named " << getName() <<
									" does not have a radius set";
		return false;
	}

	buildBrush(getRadius());

	return true;
}

void PaintBehavior::update(double dt)
{
	std::vector<Math::Vector2d> uvCoordinates;

	{
		boost::unique_lock<boost::mutex> lock(m_mutex);
		auto mesh = m_representation->getMesh();
		for (auto coordinate : m_coordinates)
		{
			if (coordinate.index < mesh->getNumTriangles())
			{
				auto vertex1 = mesh->getVertex(mesh->getTriangle(coordinate.index).verticesId[0]);
				auto vertex2 = mesh->getVertex(mesh->getTriangle(coordinate.index).verticesId[1]);
				auto vertex3 = mesh->getVertex(mesh->getTriangle(coordinate.index).verticesId[2]);
				Math::Vector2d uv = vertex1.data.texture.getValue() * coordinate.coordinate[0] +
									vertex2.data.texture.getValue() * coordinate.coordinate[1] +
									vertex3.data.texture.getValue() * coordinate.coordinate[2];
				uvCoordinates.push_back(uv);
			}
		}
	}

	for (auto& uv : uvCoordinates)
	{
		Math::Vector2d xy = toPixel(uv);

		paint(xy);

		m_texture->getOsgTexture2d()->dirtyTextureObject();
	}
}

void PaintBehavior::setRadius(double radius)
{
		m_radius = radius;
}

double PaintBehavior::getRadius() const
{
	return m_radius;
}

void PaintBehavior::buildBrush(double radius)
{
	int brushWidth = static_cast<int>(ceil(2 * radius * m_width));
	int brushHeight = static_cast<int>(ceil(2 * radius * m_height));

	int centerX = brushWidth / 2;
	int centerY = brushHeight / 2;

	m_brushOffsetX = -1 * centerX;
	m_brushOffsetY = -1 * centerY;

	m_brush.resize(brushWidth, brushHeight);

	for (int i = 0; i < m_brush.cols(); i++)
	{
		for (int j = 0; j < m_brush.rows(); j++)
		{
			double mag = ((i - centerX) * (i - centerX)) + ((j - centerY) * (j - centerY));
			if (sqrt(mag) < m_brush.cols() / 2.0)
			{
				m_brush(i, j) = 1.0;
			}
			else
			{
				m_brush(i, j) = 0.0;
			}
		}
	}
}

void PaintBehavior::buildAntiAliasedBrush(double radius)
{
	static const double sqrt2Half = 0.7071067811865475;
	double dist;
	int brushWidth = static_cast<int>(ceil(2 * radius * m_width));
	int brushHeight = static_cast<int>(ceil(2 * radius * m_height));

	int centerX = brushWidth / 2;
	int centerY = brushHeight / 2;

	m_brushOffsetX = -1 * centerX;
	m_brushOffsetY = -1 * centerY;

	m_brush.resize(brushWidth, brushHeight);

	for (int i = 0; i < m_brush.cols(); i++)
	{
		for (int j = 0; j < m_brush.rows(); j++)
		{
			double mag = (i - centerX) * (i - centerX) + (j - centerY) * (j - centerY);
			dist = (m_brush.cols() / 2) - sqrt(mag);
			dist /= m_brush.cols() / 2;
			if (dist > sqrt2Half)
			{
				m_brush(i, j) = 1.0;
			}
			else if (dist < 0.0)
			{
				m_brush(i, j) = 0.0;
			}
			else
			{
				m_brush(i, j) = sqrt(2) * dist;
			}
		}
	}
}

Math::Vector2d PaintBehavior::toPixel(const Math::Vector2d& uv)
{
	double s = uv[0];
	double t = uv[1];

	s = Math::clamp(s, 0.0, 1.0, 0.0);
	t = Math::clamp(t, 0.0, 1.0, 0.0);

	Math::Vector2d xy;
	xy << floor(s * m_width + 0.5), floor(t * m_height + 0.5);

	return xy;
}

void PaintBehavior::paint(const Math::Vector2d& coordinates)
{
	size_t numChannels = 4;

	DataStructures::ImageMap<unsigned char> image(m_width,
												  m_height,
												  numChannels,
												  m_texture->getOsgTexture2d()->getImage()->data());

	size_t topLeftX = static_cast<size_t>(std::max(0.0, coordinates[0] + m_brushOffsetX));
	size_t topLeftY = static_cast<size_t>(std::max(0.0, coordinates[1] + m_brushOffsetY));
	size_t bottomRightX = std::min(m_width - topLeftX, static_cast<size_t>(m_brush.cols()));
	size_t bottomRightY = std::min(m_height - topLeftY, static_cast<size_t>(m_brush.rows()));
	for (size_t channel = 0; channel < numChannels; channel++)
	{
		auto imageBlock = image.getChannel(channel).block(topLeftX, topLeftY, bottomRightX, bottomRightY);
		auto brushBlock = m_brush.topLeftCorner(bottomRightX, bottomRightY);
		imageBlock = (brushBlock.array() > 0).select((brushBlock * m_color(channel)*255).template cast<unsigned char>(),
						imageBlock);
	}
}

void PaintBehavior::setTextureSize(int width, int height)
{
	m_width = width;
	m_height = height;
}

} // Graphics
} // SurgSim<|endoftext|>


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

  Library   : Image Registration Toolkit (IRTK)
  Module    : $Id$
  Copyright : Imperial College, Department of Computing
              Visual Information Processing (VIP), 2008 onwards
  Date      : $Date$
  Version   : $Revision$
  Changes   : $Author$

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

#include <irtkImage.h>

#include <irtkHistogram.h>

#include <irtkTransformation.h>

#include <nr.h>

// Definition of available adaptivity measures
typedef enum { Entropy, Variance } AdaptivityMeasure;

// Definition of available adaptivity selection methods
typedef enum { PercentOfDOFs,
               PercentOfAdaptivity
             } AdaptivitySelection;

// Input transformation
char *dofin_name  = NULL;

// Output transformation
char *dofout_name = NULL;

// Image for adavptivity calculation
char *target_name = NULL;

irtkGreyImage *target;
irtkFreeFormTransformation3D *ffd;
irtkMultiLevelFreeFormTransformation *mffd;

void usage()
{
  cerr << "Usage: ffdedit [target] [dofin] [dofout] [options]" << endl;
  cerr << "where [options] are one or more of the following: " << endl;
  cerr << "-mode     <n>" << endl;
  cerr << "-level    <n>" << endl;
  cerr << "-percent  <n>" << endl;
  cerr << "-measure  <n>" << endl;
  exit(1);
}

void FFDEdit1(AdaptivityMeasure measure, double value)
{
  float *arr, *brr;
  double adaptivity;
  irtkGreyPixel min, max, *ptr2target;
  int index, x, y, z, x1, y1, z1, x2, y2, z2, n;

  // Figure out number of control points
  n = ffd->NumberOfDOFs()/3;

  // Allocate
  arr = new float [n];
  brr = new float [n];

  // Set up 1D histogram
  target->GetMinMax(&min, &max);
  irtkHistogram histo(max-min+1);
  histo.PutMin(min - 0.5);
  histo.PutMax(max + 0.5);

  // Get lattice dimensions and loop over lattice
  for (x = 0; x < ffd->GetX(); x++) {
    for (y = 0; y < ffd->GetY(); y++) {
      for (z = 0; z < ffd->GetZ(); z++) {

        // Get index and point from lattice coordinates
        index = ffd->LatticeToIndex(x, y, z);
        ffd->BoundingBoxImage(target, index, x1, y1, z1, x2, y2, z2);

        // Reset histogram and fill
        histo.Reset();
        for (int k = z1; k <= z2; k++) {
          for (int j = y1; j <= y2; j++) {

            // Get pointer to target image
            ptr2target = target->GetPointerToVoxels(x1, j, k);

            for (int i = x1; i <= x2; i++) {
              histo.AddSample(*ptr2target);
              ptr2target++;
            }
          }
        }

        // Compute adaptivity measure
        switch (measure) {
        case Variance:
          adaptivity = histo.Variance();
          break;
        case Entropy:
          adaptivity = histo.Entropy();
          break;
        default:
          cerr << "Unknown adaptivity measure" << endl;
          exit(1);
        }

        // Store in array
        arr[index] = adaptivity;
        brr[index] = index;
      }
    }
  }

  // Quicksort
  sort2(n, arr-1, brr-1);

  // Set top % to active, bottom % to passive
  index = round(value*(double)n);
  for (int i=(int)n; i>index; i--) {
    ffd->IndexToLattice(int(brr[i-1]), x, y, z);
    ffd->PutStatusCP(x, y, z, _Active, _Active, _Active);
  }
  for (int i=index; i>0; i--) {
    ffd->IndexToLattice(int(brr[i-1]), x, y, z);
    ffd->PutStatusCP(x, y, z, _Passive, _Passive, _Passive);
  }

  // Be good
  delete [] arr;
  delete [] brr;
}

void FFDEdit2(AdaptivityMeasure measure, double value)
{
  double adaptivity;
  irtkGreyPixel min, max, *ptr2target;
  int index, x, y, z, x1, y1, z1, x2, y2, z2;

  // Set up 1D histogram
  target->GetMinMax(&min, &max);
  irtkHistogram histo(max-min+1);
  histo.PutMin(min - 0.5);
  histo.PutMax(max + 0.5);

  // Get lattice dimensions and loop over lattice
  ptr2target = target->GetPointerToVoxels();
  for (int z=0; z<target->GetZ(); z++) {
    for (int y=0; y<target->GetY(); y++) {
      for (int x=0; x<target->GetX(); x++) {
        histo.AddSample(*ptr2target);
        ptr2target++;
      }
    }
  }

  // Compute adaptivity measure
  switch (measure) {
  case Variance:
    value = value * histo.Variance();
    break;
  case Entropy:
    value = value * histo.Entropy();
    break;
  default:
    cerr << "Unknown adaptivity measure" << endl;
    exit(1);
  }

  for (x = 0; x < ffd->GetX(); x++) {
    for (y = 0; y < ffd->GetY(); y++) {
      for (z = 0; z < ffd->GetZ(); z++) {

        // Get index and point from lattice coordinates
        index = ffd->LatticeToIndex(x, y, z);
        ffd->BoundingBoxImage(target, index, x1, y1, z1, x2, y2, z2);

        // Reset histogram and fill
        histo.Reset();
        for (int k = z1; k <= z2; k++) {
          for (int j = y1; j <= y2; j++) {

            // Get pointer to target image
            ptr2target = target->GetPointerToVoxels(x1, j, k);

            for (int i = x1; i <= x2; i++) {
              histo.AddSample(*ptr2target);
              ptr2target++;
            }
          }
        }

        // Compute adaptivity measure
        switch (measure) {
        case Variance:
          adaptivity = histo.Variance();
          break;
        case Entropy:
          adaptivity = histo.Entropy();
          break;
        default:
          cerr << "Unknown adaptivity measure" << endl;
          exit(1);
        }

        // Set status according to adaptivity in region
        if (adaptivity < value) {
          ffd->PutStatusCP(x, y, z, _Passive, _Passive, _Passive);
        } else {
          ffd->PutStatusCP(x, y, z, _Active, _Active, _Active);
        }
      }
    }
  }
}

int main(int argc, char **argv)
{
  double percentage;
  int i, x, y, z, ok, active, passive, level;
  AdaptivityMeasure measure;
  AdaptivitySelection selection;

  // Check command line
  if (argc < 4) {
    usage();
  }

  // Parse file names
  target_name = argv[1];
  argc--;
  argv++;
  dofin_name  = argv[1];
  argc--;
  argv++;
  dofout_name = argv[1];
  argc--;
  argv++;

  // Read target
  cout << "Reading image ... "; cout.flush();
  target = new irtkGreyImage;
  target->Read(target_name);
  cout << "done" << endl;

  // Read transformation
  mffd = new irtkMultiLevelFreeFormTransformation;
  mffd->irtkTransformation::Read(dofin_name);

  // Default level is the last level
  level = mffd->NumberOfLevels()-1;

  // Default measure is entropy
  measure = Entropy;

  // Default selection is percent of DOFs
  selection = PercentOfDOFs;

  // Default percenttage is 50%
  percentage = 0.5;

  // Parse remaining parameters
  while (argc > 1) {
    ok = false;
    if ((ok == false) && (strcmp(argv[1], "-measure") == 0)) {
      argc--;
      argv++;
      if (strcmp(argv[1], "entropy") == 0) {
        measure = Entropy;
      } else {
        if (strcmp(argv[1], "variance") == 0) {
          measure = Variance;
        } else {
          cerr << "Unknown measure: " << argv[1] << endl;
          exit(1);
        }
      }
      argc--;
      argv++;
      ok = true;
    }
    if ((ok == false) && (strcmp(argv[1], "-mode") == 0)) {
      argc--;
      argv++;
      if (strcmp(argv[1], "PercentOfDOFs") == 0) {
        selection = PercentOfDOFs;
      } else {
        if (strcmp(argv[1], "PercentOfAdaptivity") == 0) {
          selection = PercentOfAdaptivity;
        } else {
          cerr << "Unknown mode: " << argv[1] << endl;
          exit(1);
        }
      }
      argc--;
      argv++;
      ok = true;
    }
    if ((ok == false) && (strcmp(argv[1], "-percent") == 0)) {
      argc--;
      argv++;
      percentage = atof(argv[1]);
      argc--;
      argv++;
      ok = true;
    }
    if ((ok == false) && (strcmp(argv[1], "-level") == 0)) {
      argc--;
      argv++;
      level = atoi(argv[1]);
      argc--;
      argv++;
      ok = true;
    }
    if (ok == false) {
      cerr << "Can't parse argument " << argv[1] << endl;
      usage();
    }
  }

  // Extract current transformation level
  ffd = dynamic_cast<irtkFreeFormTransformation3D *>(mffd->GetLocalTransformation(level));

  if (ffd == NULL) {
    cerr << "Free-form transformation is not 3D" << endl;
    exit(1);
  }

  // Edit current transformation level
  cout << "Editing transformation level " << level << " ... ";
  cout.flush();
  switch (selection) {
  case PercentOfDOFs:
    FFDEdit1(measure, percentage);
    break;
  case PercentOfAdaptivity:
    FFDEdit2(measure, percentage);
    break;
  }
  cout << "done" << endl;

  // Calculate number of active and passive control points
  i = 0;
  active  = 0;
  passive = 0;
  for (x = 0; x < ffd->GetX(); x++) {
    for (y = 0; y < ffd->GetY(); y++) {
      for (z = 0; z < ffd->GetZ(); z++) {
        _Status sx, sy, sz;
        ffd->GetStatusCP(x, y, z, sx, sy, sz);
        if (sx == _Active) {
          active++;
        } else {
          passive++;
        }
        if (sy == _Active) {
          active++;
        } else {
          passive++;
        }
        if (sz == _Active) {
          active++;
        } else {
          passive++;
        }
        i++;
      }
    }
  }
  cout << "Transformation level " << level << " has " << endl;
  cout << active << " active control points  \t ("
       << active*100.0/(active+passive) << "%)" << endl;
  cout << passive << " passive control points \t ("
       << passive*100.0/(active+passive) << "%)" << endl;

  // Write transformation
  mffd->irtkTransformation::Write(dofout_name);
}

<commit_msg>Replaced recipes sort2 function from ffdedit.cc<commit_after>/*=========================================================================

  Library   : Image Registration Toolkit (IRTK)
  Module    : $Id$
  Copyright : Imperial College, Department of Computing
              Visual Information Processing (VIP), 2008 onwards
  Date      : $Date$
  Version   : $Revision$
  Changes   : $Author$

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

#include <irtkImage.h>

#include <irtkHistogram.h>

#include <irtkTransformation.h>

#include <algorithm>

// Definition of available adaptivity measures
typedef enum { Entropy, Variance } AdaptivityMeasure;

// Definition of available adaptivity selection methods
typedef enum { PercentOfDOFs,
               PercentOfAdaptivity
             } AdaptivitySelection;

// Input transformation
char *dofin_name  = NULL;

// Output transformation
char *dofout_name = NULL;

// Image for adavptivity calculation
char *target_name = NULL;

irtkGreyImage *target;
irtkFreeFormTransformation3D *ffd;
irtkMultiLevelFreeFormTransformation *mffd;

void usage()
{
  cerr << "Usage: ffdedit [target] [dofin] [dofout] [options]" << endl;
  cerr << "where [options] are one or more of the following: " << endl;
  cerr << "-mode     <n>" << endl;
  cerr << "-level    <n>" << endl;
  cerr << "-percent  <n>" << endl;
  cerr << "-measure  <n>" << endl;
  exit(1);
}

void sort2(int index, float *array1, float *array2) {
  int indices[index];
  float array1_store[index];
  float array2_store[index];

  for (int i = 0; i < index; i++) {
    // create array with indices to be sorted
    indices[i] = i;
    // keep a back up of the original arrays
    array1_store[i] = array1[i];
    array2_store[i] = array2[i];
  }

  // sort the first array and keep the index order
  std::sort(indices, indices + index, sort_indices(array1));

  // update the arrays
  for (int i = 0; i < index; i++) {
    array1[i+1] = array1_store[indices[i]];
    array2[i+1] = array2_store[indices[i]];
  }
}

void FFDEdit1(AdaptivityMeasure measure, double value)
{
  float *arr, *brr;
  double adaptivity;
  irtkGreyPixel min, max, *ptr2target;
  int index, x, y, z, x1, y1, z1, x2, y2, z2, n;

  // Figure out number of control points
  n = ffd->NumberOfDOFs()/3;

  // Allocate
  arr = new float [n];
  brr = new float [n];

  // Set up 1D histogram
  target->GetMinMax(&min, &max);
  irtkHistogram histo(max-min+1);
  histo.PutMin(min - 0.5);
  histo.PutMax(max + 0.5);

  // Get lattice dimensions and loop over lattice
  for (x = 0; x < ffd->GetX(); x++) {
    for (y = 0; y < ffd->GetY(); y++) {
      for (z = 0; z < ffd->GetZ(); z++) {

        // Get index and point from lattice coordinates
        index = ffd->LatticeToIndex(x, y, z);
        ffd->BoundingBoxImage(target, index, x1, y1, z1, x2, y2, z2);

        // Reset histogram and fill
        histo.Reset();
        for (int k = z1; k <= z2; k++) {
          for (int j = y1; j <= y2; j++) {

            // Get pointer to target image
            ptr2target = target->GetPointerToVoxels(x1, j, k);

            for (int i = x1; i <= x2; i++) {
              histo.AddSample(*ptr2target);
              ptr2target++;
            }
          }
        }

        // Compute adaptivity measure
        switch (measure) {
        case Variance:
          adaptivity = histo.Variance();
          break;
        case Entropy:
          adaptivity = histo.Entropy();
          break;
        default:
          cerr << "Unknown adaptivity measure" << endl;
          exit(1);
        }

        // Store in array
        arr[index] = adaptivity;
        brr[index] = index;
      }
    }
  }

  // Quicksort
  sort2(n, arr-1, brr-1);

  // Set top % to active, bottom % to passive
  index = round(value*(double)n);
  for (int i=(int)n; i>index; i--) {
    ffd->IndexToLattice(int(brr[i-1]), x, y, z);
    ffd->PutStatusCP(x, y, z, _Active, _Active, _Active);
  }
  for (int i=index; i>0; i--) {
    ffd->IndexToLattice(int(brr[i-1]), x, y, z);
    ffd->PutStatusCP(x, y, z, _Passive, _Passive, _Passive);
  }

  // Be good
  delete [] arr;
  delete [] brr;
}

void FFDEdit2(AdaptivityMeasure measure, double value)
{
  double adaptivity;
  irtkGreyPixel min, max, *ptr2target;
  int index, x, y, z, x1, y1, z1, x2, y2, z2;

  // Set up 1D histogram
  target->GetMinMax(&min, &max);
  irtkHistogram histo(max-min+1);
  histo.PutMin(min - 0.5);
  histo.PutMax(max + 0.5);

  // Get lattice dimensions and loop over lattice
  ptr2target = target->GetPointerToVoxels();
  for (int z=0; z<target->GetZ(); z++) {
    for (int y=0; y<target->GetY(); y++) {
      for (int x=0; x<target->GetX(); x++) {
        histo.AddSample(*ptr2target);
        ptr2target++;
      }
    }
  }

  // Compute adaptivity measure
  switch (measure) {
  case Variance:
    value = value * histo.Variance();
    break;
  case Entropy:
    value = value * histo.Entropy();
    break;
  default:
    cerr << "Unknown adaptivity measure" << endl;
    exit(1);
  }

  for (x = 0; x < ffd->GetX(); x++) {
    for (y = 0; y < ffd->GetY(); y++) {
      for (z = 0; z < ffd->GetZ(); z++) {

        // Get index and point from lattice coordinates
        index = ffd->LatticeToIndex(x, y, z);
        ffd->BoundingBoxImage(target, index, x1, y1, z1, x2, y2, z2);

        // Reset histogram and fill
        histo.Reset();
        for (int k = z1; k <= z2; k++) {
          for (int j = y1; j <= y2; j++) {

            // Get pointer to target image
            ptr2target = target->GetPointerToVoxels(x1, j, k);

            for (int i = x1; i <= x2; i++) {
              histo.AddSample(*ptr2target);
              ptr2target++;
            }
          }
        }

        // Compute adaptivity measure
        switch (measure) {
        case Variance:
          adaptivity = histo.Variance();
          break;
        case Entropy:
          adaptivity = histo.Entropy();
          break;
        default:
          cerr << "Unknown adaptivity measure" << endl;
          exit(1);
        }

        // Set status according to adaptivity in region
        if (adaptivity < value) {
          ffd->PutStatusCP(x, y, z, _Passive, _Passive, _Passive);
        } else {
          ffd->PutStatusCP(x, y, z, _Active, _Active, _Active);
        }
      }
    }
  }
}

int main(int argc, char **argv)
{
  double percentage;
  int i, x, y, z, ok, active, passive, level;
  AdaptivityMeasure measure;
  AdaptivitySelection selection;

  // Check command line
  if (argc < 4) {
    usage();
  }

  // Parse file names
  target_name = argv[1];
  argc--;
  argv++;
  dofin_name  = argv[1];
  argc--;
  argv++;
  dofout_name = argv[1];
  argc--;
  argv++;

  // Read target
  cout << "Reading image ... "; cout.flush();
  target = new irtkGreyImage;
  target->Read(target_name);
  cout << "done" << endl;

  // Read transformation
  mffd = new irtkMultiLevelFreeFormTransformation;
  mffd->irtkTransformation::Read(dofin_name);

  // Default level is the last level
  level = mffd->NumberOfLevels()-1;

  // Default measure is entropy
  measure = Entropy;

  // Default selection is percent of DOFs
  selection = PercentOfDOFs;

  // Default percenttage is 50%
  percentage = 0.5;

  // Parse remaining parameters
  while (argc > 1) {
    ok = false;
    if ((ok == false) && (strcmp(argv[1], "-measure") == 0)) {
      argc--;
      argv++;
      if (strcmp(argv[1], "entropy") == 0) {
        measure = Entropy;
      } else {
        if (strcmp(argv[1], "variance") == 0) {
          measure = Variance;
        } else {
          cerr << "Unknown measure: " << argv[1] << endl;
          exit(1);
        }
      }
      argc--;
      argv++;
      ok = true;
    }
    if ((ok == false) && (strcmp(argv[1], "-mode") == 0)) {
      argc--;
      argv++;
      if (strcmp(argv[1], "PercentOfDOFs") == 0) {
        selection = PercentOfDOFs;
      } else {
        if (strcmp(argv[1], "PercentOfAdaptivity") == 0) {
          selection = PercentOfAdaptivity;
        } else {
          cerr << "Unknown mode: " << argv[1] << endl;
          exit(1);
        }
      }
      argc--;
      argv++;
      ok = true;
    }
    if ((ok == false) && (strcmp(argv[1], "-percent") == 0)) {
      argc--;
      argv++;
      percentage = atof(argv[1]);
      argc--;
      argv++;
      ok = true;
    }
    if ((ok == false) && (strcmp(argv[1], "-level") == 0)) {
      argc--;
      argv++;
      level = atoi(argv[1]);
      argc--;
      argv++;
      ok = true;
    }
    if (ok == false) {
      cerr << "Can't parse argument " << argv[1] << endl;
      usage();
    }
  }

  // Extract current transformation level
  ffd = dynamic_cast<irtkFreeFormTransformation3D *>(mffd->GetLocalTransformation(level));

  if (ffd == NULL) {
    cerr << "Free-form transformation is not 3D" << endl;
    exit(1);
  }

  // Edit current transformation level
  cout << "Editing transformation level " << level << " ... ";
  cout.flush();
  switch (selection) {
  case PercentOfDOFs:
    FFDEdit1(measure, percentage);
    break;
  case PercentOfAdaptivity:
    FFDEdit2(measure, percentage);
    break;
  }
  cout << "done" << endl;

  // Calculate number of active and passive control points
  i = 0;
  active  = 0;
  passive = 0;
  for (x = 0; x < ffd->GetX(); x++) {
    for (y = 0; y < ffd->GetY(); y++) {
      for (z = 0; z < ffd->GetZ(); z++) {
        _Status sx, sy, sz;
        ffd->GetStatusCP(x, y, z, sx, sy, sz);
        if (sx == _Active) {
          active++;
        } else {
          passive++;
        }
        if (sy == _Active) {
          active++;
        } else {
          passive++;
        }
        if (sz == _Active) {
          active++;
        } else {
          passive++;
        }
        i++;
      }
    }
  }
  cout << "Transformation level " << level << " has " << endl;
  cout << active << " active control points  \t ("
       << active*100.0/(active+passive) << "%)" << endl;
  cout << passive << " passive control points \t ("
       << passive*100.0/(active+passive) << "%)" << endl;

  // Write transformation
  mffd->irtkTransformation::Write(dofout_name);
}

<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>

#include "configuration.h"
#include "extern.h"
#include "database.h"

#include <boost/program_options.hpp>
namespace po = boost::program_options;

// static data member defs
int Config::ITEM_DAMAGE_RATE;
int Config::WEAPON_DAM_MIN_HARDNESS;
int Config::WEAPON_DAM_MAX_HARDNESS;
int Config::WEAPON_DAM_MAX_SHARP;
bool Config::NUKE_REPAIR_ITEMS;
bool Config::CHECK_MULTIPLAY;
bool Config::FORCE_MULTIPLAY_COMPLIANCE;
bool Config::REPO_MOBS;
bool Config::SUPER_REPO_MOBS;
bool Config::NO_DAMAGED_ITEMS_SHOP;
bool Config::auto_deletion;
bool Config::rent_only_deletion;
bool Config::rent_tax;
bool Config::nuke_inactive_mobs;
bool Config::load_on_death;
bool Config::throw_format_exceptions;
bool Config::no_specials;
bool Config::b_trimmed;
sstring Config::data_dir;
bool Config::no_mail;

const int Config::Port::PROD=7900;
const int Config::Port::ALPHA=6969;
const int Config::Port::GAMMA=6961;

extern std::vector <std::string> db_hosts;
extern std::vector <std::string> db_names;
extern std::vector <std::string> db_users;
extern std::vector <std::string> db_passwords;

void sendHelp(po::options_description desc){
  std::cout << "Usage: sneezy [options] [port]" << std::endl;
  std::cout << desc;
}

bool Config::doConfiguration(int argc, char *argv[])
{
  using std::string;
  string configFile="sneezy.cfg";

  // command line only options
  po::options_description cmdline("Command line only");
  cmdline.add_options()
    ("help", "produce help message")
    ("config,c", po::value<string>(&configFile)->default_value("sneezy.cfg"),
     "configuration file to use")
    ;

  // command line OR in config file
  po::options_description config("Configuration + Command line");
  config.add_options()
    ("lib,l", po::value<string>(&data_dir)->default_value(Path::DATA),
     "data directory to run in")
    ("no_specials,s", po::value<bool>(&no_specials)->default_value(false),
     "suppress assignment of special routines")
    ("trimmed,t", po::value<bool>(&b_trimmed)->default_value(false),
     "load as trimmed port")
    ("port,p", po::value<int>(&gamePort)->default_value(Config::Port::PROD),
     "game port")
    ;

  // config file only options
  po::options_description configOnly("Configuration File Only");
  configOnly.add_options()
    ("item_damage_rate",
     po::value<int>(&ITEM_DAMAGE_RATE)->default_value(1),
     "see configuration.h")
    ("weapon_dam_min_hardness",
     po::value<int>(&WEAPON_DAM_MIN_HARDNESS)->default_value(20),
     "see configuration.h")
    ("weapon_dam_max_hardness",
     po::value<int>(&WEAPON_DAM_MAX_HARDNESS)->default_value(150),
     "see configuration.h")
    ("weapon_dam_max_sharp",
     po::value<int>(&WEAPON_DAM_MAX_SHARP)->default_value(150),
     "see configuration.h")
    ("nuke_repair_items",
     po::value<bool>(&NUKE_REPAIR_ITEMS)->default_value(true),
     "see configuration.h")
    ("check_multiplay",
     po::value<bool>(&CHECK_MULTIPLAY)->default_value(true),
     "see configuration.h")
    ("force_multiplay_compliance",
     po::value<bool>(&FORCE_MULTIPLAY_COMPLIANCE)->default_value(false),
     "see configuration.h")
    ("repo_mobs",
     po::value<bool>(&REPO_MOBS)->default_value(false),
     "see configuration.h")
    ("super_repo_mobs",
     po::value<bool>(&SUPER_REPO_MOBS)->default_value(false),
     "see configuration.h")
    ("no_damaged_items_shop",
     po::value<bool>(&NO_DAMAGED_ITEMS_SHOP)->default_value(false),
     "see configuration.h")
    ("auto_deletion",
     po::value<bool>(&auto_deletion)->default_value(false),
     "see configuration.h")
    ("rent_only_deletion",
     po::value<bool>(&rent_only_deletion)->default_value(false),
     "see configuration.h")
    ("rent_tax",
     po::value<bool>(&rent_tax)->default_value(true),
     "see configuration.h")
    ("nuke_inactive_mobs",
     po::value<bool>(&nuke_inactive_mobs)->default_value(false),
     "see configuration.h")
    ("load_on_death",
     po::value<bool>(&load_on_death)->default_value(true),
     "see configuration.h")
    ("throw_format_exceptions",
     po::value<bool>(&throw_format_exceptions)->default_value(true),
     "see configuration.h")
    ("no_mail",
     po::value<bool>(&no_mail)->default_value(false),
     "see configuration.h")
    ;

  // database options
  po::options_description database_hosts("Database hosts");
  database_hosts.add_options()
    ("sneezy_host", po::value<string>(&db_hosts[DB_SNEEZY]),
     "host for sneezy database")
    ("immortal_host", po::value<string>(&db_hosts[DB_IMMORTAL]),
     "host for immortal database")
    ;

  // database options
  po::options_description database_names("Database names");
  database_names.add_options()
    ("sneezy_db", po::value<string>(&db_names[DB_SNEEZY]),
     "name for sneezy database")
    ("immortal_db", po::value<string>(&db_names[DB_IMMORTAL]),
     "name for immortal database")

    ;

  po::options_description database_users("Database usernames");
  database_users.add_options()
    ("sneezy_user", po::value<string>(&db_users[DB_SNEEZY]),
     "sneezy database username")
    ("immortal_user", po::value<string>(&db_users[DB_IMMORTAL]),
     "immortal database username")
    ;

  po::options_description database_passwords("Database passwords");
  database_users.add_options()
    ("sneezy_password", po::value<string>(&db_passwords[DB_SNEEZY]),
     "sneezy database password")
    ("immortal_password", po::value<string>(&db_passwords[DB_IMMORTAL]),
     "immortal database password")
    ;

  po::options_description cmdline_options;
  cmdline_options.add(cmdline).add(config).add(database_names).add(database_hosts).add(database_users).add(database_passwords);

  po::options_description config_options;
  config_options.add(config).add(database_names).add(database_hosts).add(database_users).add(database_passwords).add(configOnly);

  po::options_description visible("Allowed options");
  visible.add(cmdline).add(config).add(database_names).add(database_hosts).add(database_users).add(database_passwords).add(configOnly);

  // first positional argument is port number
  po::positional_options_description p;
  p.add("port", -1);

  po::variables_map vm;

  if(argc){
    po::store(po::command_line_parser(argc, argv).
	      options(cmdline_options).positional(p).run(), vm);
  }
  po::notify(vm);
  std::ifstream ifs(configFile.c_str());

  if(!ifs.is_open()){
    std::cout << format("Failed to open config file '%s'\n") % configFile;
  }

  po::store(parse_config_file(ifs, config_options), vm);
  po::notify(vm);

  if(vm.count("help")){
    sendHelp(visible);
    return false;
  }
  return true;
}

<commit_msg>Turned on checking for multiplay by default<commit_after>#include <iostream>
#include <fstream>

#include "configuration.h"
#include "extern.h"
#include "database.h"

#include <boost/program_options.hpp>
namespace po = boost::program_options;

// static data member defs
int Config::ITEM_DAMAGE_RATE;
int Config::WEAPON_DAM_MIN_HARDNESS;
int Config::WEAPON_DAM_MAX_HARDNESS;
int Config::WEAPON_DAM_MAX_SHARP;
bool Config::NUKE_REPAIR_ITEMS;
bool Config::CHECK_MULTIPLAY;
bool Config::FORCE_MULTIPLAY_COMPLIANCE;
bool Config::REPO_MOBS;
bool Config::SUPER_REPO_MOBS;
bool Config::NO_DAMAGED_ITEMS_SHOP;
bool Config::auto_deletion;
bool Config::rent_only_deletion;
bool Config::rent_tax;
bool Config::nuke_inactive_mobs;
bool Config::load_on_death;
bool Config::throw_format_exceptions;
bool Config::no_specials;
bool Config::b_trimmed;
sstring Config::data_dir;
bool Config::no_mail;

const int Config::Port::PROD=7900;
const int Config::Port::ALPHA=6969;
const int Config::Port::GAMMA=6961;

extern std::vector <std::string> db_hosts;
extern std::vector <std::string> db_names;
extern std::vector <std::string> db_users;
extern std::vector <std::string> db_passwords;

void sendHelp(po::options_description desc){
  std::cout << "Usage: sneezy [options] [port]" << std::endl;
  std::cout << desc;
}

bool Config::doConfiguration(int argc, char *argv[])
{
  using std::string;
  string configFile="sneezy.cfg";

  // command line only options
  po::options_description cmdline("Command line only");
  cmdline.add_options()
    ("help", "produce help message")
    ("config,c", po::value<string>(&configFile)->default_value("sneezy.cfg"),
     "configuration file to use")
    ;

  // command line OR in config file
  po::options_description config("Configuration + Command line");
  config.add_options()
    ("lib,l", po::value<string>(&data_dir)->default_value(Path::DATA),
     "data directory to run in")
    ("no_specials,s", po::value<bool>(&no_specials)->default_value(false),
     "suppress assignment of special routines")
    ("trimmed,t", po::value<bool>(&b_trimmed)->default_value(false),
     "load as trimmed port")
    ("port,p", po::value<int>(&gamePort)->default_value(Config::Port::PROD),
     "game port")
    ;

  // config file only options
  po::options_description configOnly("Configuration File Only");
  configOnly.add_options()
    ("item_damage_rate",
     po::value<int>(&ITEM_DAMAGE_RATE)->default_value(1),
     "see configuration.h")
    ("weapon_dam_min_hardness",
     po::value<int>(&WEAPON_DAM_MIN_HARDNESS)->default_value(20),
     "see configuration.h")
    ("weapon_dam_max_hardness",
     po::value<int>(&WEAPON_DAM_MAX_HARDNESS)->default_value(150),
     "see configuration.h")
    ("weapon_dam_max_sharp",
     po::value<int>(&WEAPON_DAM_MAX_SHARP)->default_value(150),
     "see configuration.h")
    ("nuke_repair_items",
     po::value<bool>(&NUKE_REPAIR_ITEMS)->default_value(true),
     "see configuration.h")
    ("check_multiplay",
     po::value<bool>(&CHECK_MULTIPLAY)->default_value(true),
     "see configuration.h")
    ("force_multiplay_compliance",
     po::value<bool>(&FORCE_MULTIPLAY_COMPLIANCE)->default_value(true),
     "see configuration.h")
    ("repo_mobs",
     po::value<bool>(&REPO_MOBS)->default_value(false),
     "see configuration.h")
    ("super_repo_mobs",
     po::value<bool>(&SUPER_REPO_MOBS)->default_value(false),
     "see configuration.h")
    ("no_damaged_items_shop",
     po::value<bool>(&NO_DAMAGED_ITEMS_SHOP)->default_value(false),
     "see configuration.h")
    ("auto_deletion",
     po::value<bool>(&auto_deletion)->default_value(false),
     "see configuration.h")
    ("rent_only_deletion",
     po::value<bool>(&rent_only_deletion)->default_value(false),
     "see configuration.h")
    ("rent_tax",
     po::value<bool>(&rent_tax)->default_value(true),
     "see configuration.h")
    ("nuke_inactive_mobs",
     po::value<bool>(&nuke_inactive_mobs)->default_value(false),
     "see configuration.h")
    ("load_on_death",
     po::value<bool>(&load_on_death)->default_value(true),
     "see configuration.h")
    ("throw_format_exceptions",
     po::value<bool>(&throw_format_exceptions)->default_value(true),
     "see configuration.h")
    ("no_mail",
     po::value<bool>(&no_mail)->default_value(false),
     "see configuration.h")
    ;

  // database options
  po::options_description database_hosts("Database hosts");
  database_hosts.add_options()
    ("sneezy_host", po::value<string>(&db_hosts[DB_SNEEZY]),
     "host for sneezy database")
    ("immortal_host", po::value<string>(&db_hosts[DB_IMMORTAL]),
     "host for immortal database")
    ;

  // database options
  po::options_description database_names("Database names");
  database_names.add_options()
    ("sneezy_db", po::value<string>(&db_names[DB_SNEEZY]),
     "name for sneezy database")
    ("immortal_db", po::value<string>(&db_names[DB_IMMORTAL]),
     "name for immortal database")

    ;

  po::options_description database_users("Database usernames");
  database_users.add_options()
    ("sneezy_user", po::value<string>(&db_users[DB_SNEEZY]),
     "sneezy database username")
    ("immortal_user", po::value<string>(&db_users[DB_IMMORTAL]),
     "immortal database username")
    ;

  po::options_description database_passwords("Database passwords");
  database_users.add_options()
    ("sneezy_password", po::value<string>(&db_passwords[DB_SNEEZY]),
     "sneezy database password")
    ("immortal_password", po::value<string>(&db_passwords[DB_IMMORTAL]),
     "immortal database password")
    ;

  po::options_description cmdline_options;
  cmdline_options.add(cmdline).add(config).add(database_names).add(database_hosts).add(database_users).add(database_passwords);

  po::options_description config_options;
  config_options.add(config).add(database_names).add(database_hosts).add(database_users).add(database_passwords).add(configOnly);

  po::options_description visible("Allowed options");
  visible.add(cmdline).add(config).add(database_names).add(database_hosts).add(database_users).add(database_passwords).add(configOnly);

  // first positional argument is port number
  po::positional_options_description p;
  p.add("port", -1);

  po::variables_map vm;

  if(argc){
    po::store(po::command_line_parser(argc, argv).
	      options(cmdline_options).positional(p).run(), vm);
  }
  po::notify(vm);
  std::ifstream ifs(configFile.c_str());

  if(!ifs.is_open()){
    std::cout << format("Failed to open config file '%s'\n") % configFile;
  }

  po::store(parse_config_file(ifs, config_options), vm);
  po::notify(vm);

  if(vm.count("help")){
    sendHelp(visible);
    return false;
  }
  return true;
}

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <experimental/optional>
#include <experimental/string_view>
#include <stdexcept>
#include <unordered_set>

#include <seastar/core/future.hh>
#include <seastar/core/print.hh>
#include <seastar/core/sstring.hh>

#include "auth/data_resource.hh"
#include "seastarx.hh"
#include "stdx.hh"

namespace auth {

class authenticated_user;

struct role_config final {
    bool is_superuser{false};
    bool can_login{false};
};

// Differential update for altering existing roles.
struct role_config_update final {
    stdx::optional<bool> is_superuser{};
    stdx::optional<bool> can_login{};
};

// A logical argument error for a role-management operation.
class roles_argument_exception : public std::invalid_argument {
public:
    using std::invalid_argument::invalid_argument;
};

class role_already_exists : public roles_argument_exception {
    sstring _role_name;

public:
    explicit role_already_exists(sstring role_name)
            : roles_argument_exception(sprint("The '%s' role already exists.", role_name))
            , _role_name(std::move(role_name)) {
    }

    stdx::string_view role_name() const noexcept {
        return _role_name;
    }
};

class nonexistant_role : public roles_argument_exception {
    sstring _role_name;

public:
    explicit nonexistant_role(sstring role_name)
            : roles_argument_exception(sprint("The role '%s' does not exist.", role_name))
            , _role_name(std::move(role_name)) {
    }

    stdx::string_view role_name() const noexcept {
        return _role_name;
    }
};

class role_already_included : public roles_argument_exception {
    sstring _role_name, _grantee_name;

public:
    role_already_included(sstring grantee_name, sstring role_name)
            : roles_argument_exception(
                      sprint("'%s' already includes role '%s'.", grantee_name, role_name))
            , _role_name(std::move(role_name))
            , _grantee_name(std::move(grantee_name)) {
    }

    stdx::string_view role_name() const noexcept {
        return _role_name;
    }

    stdx::string_view grantee_name() const noexcept {
        return _grantee_name;
    }
};

class revoke_ungranted_role : public roles_argument_exception {
    sstring _role_name, _revokee_name;

public:
    revoke_ungranted_role(sstring revokee_name, sstring role_name)
            : roles_argument_exception(
                      sprint("'%s' was not granted role '%s', so it cannot be revoked.", revokee_name, role_name))
            , _role_name(std::move(role_name))
            , _revokee_name(std::move(revokee_name)) {
    }

    stdx::string_view role_name() const noexcept {
        return _role_name;
    }

    stdx::string_view revokee_name() const noexcept {
        return _revokee_name;
    }
};

enum class recursive_role_query { yes, no };

// Abstract role manager.
//
// All implementations should throw role-related exceptions as documented, but authorization-related checking is
// handled by the CQL layer, and not here.
class role_manager {
public:
    virtual ~role_manager() = default;

    virtual stdx::string_view qualified_java_name() const noexcept = 0;

    virtual future<> start() = 0;

    virtual future<> stop() = 0;

    // Must throw `role_already_exists` for a role that has previously been created.
    virtual future<>
    create(const authenticated_user& performer, stdx::string_view role_name, const role_config&) = 0;

    // Must throw `nonexistant_role` if the role does not exist.
    virtual future<> drop(const authenticated_user& performer, stdx::string_view role_name) = 0;

    // Must throw `nonexistant_role` if the role does not exist.
    virtual future<>
    alter(const authenticated_user& performer, stdx::string_view role_name, const role_config_update&) = 0;

    // Grant `role_name` to `grantee_name`.
    //
    // Must throw `nonexistant_role` if either the role or the grantee do not exist.
    //
    // Must throw `role_already_included` if granting the role would be redundant, or create a cycle.
    virtual future<>
    grant(const authenticated_user& performer, stdx::string_view grantee_name, stdx::string_view role_name) = 0;

    // Revoke `role_name` from `revokee_name`.
    //
    // Must throw `nonexistant_role` if either the role or the revokee do not exist.
    //
    // Must throw `revoke_ungranted_role` if the role was not granted.
    virtual future<>
    revoke(const authenticated_user& performer, stdx::string_view revokee_name, stdx::string_view role_name) = 0;

    // Must throw `nonexistant_role` if the role does not exist.
    virtual future<std::unordered_set<sstring>> query_granted(stdx::string_view grantee, recursive_role_query) const = 0;

    virtual future<std::unordered_set<sstring>> query_all() const = 0;

    virtual future<bool> exists(stdx::string_view role_name) const = 0;

    /// Must throw `nonexistant_role` if the role does not exist.
    virtual future<bool> is_superuser(stdx::string_view role_name) const = 0;

    // Must throw `nonexistant_role` if the role does not exist.
    virtual future<bool> can_login(stdx::string_view role_name) const = 0;
};

}
<commit_msg>auth: Copying role exceptions cannot throw<commit_after>/*
 * Copyright (C) 2017 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <experimental/optional>
#include <experimental/string_view>
#include <memory>
#include <stdexcept>
#include <unordered_set>

#include <seastar/core/future.hh>
#include <seastar/core/print.hh>
#include <seastar/core/sstring.hh>

#include "auth/data_resource.hh"
#include "seastarx.hh"
#include "stdx.hh"

namespace auth {

class authenticated_user;

struct role_config final {
    bool is_superuser{false};
    bool can_login{false};
};

// Differential update for altering existing roles.
struct role_config_update final {
    stdx::optional<bool> is_superuser{};
    stdx::optional<bool> can_login{};
};

// A logical argument error for a role-management operation.
class roles_argument_exception : public std::invalid_argument {
public:
    using std::invalid_argument::invalid_argument;
};

class role_already_exists : public roles_argument_exception {
    std::shared_ptr<sstring> _role_name;

public:
    explicit role_already_exists(stdx::string_view role_name)
            : roles_argument_exception(sprint("The '%s' role already exists.", role_name))
            , _role_name(std::make_shared<sstring>(role_name)) {
    }

    stdx::string_view role_name() const noexcept {
        return *_role_name;
    }
};

class nonexistant_role : public roles_argument_exception {
    std::shared_ptr<sstring> _role_name;

public:
    explicit nonexistant_role(stdx::string_view role_name)
            : roles_argument_exception(sprint("The role '%s' does not exist.", role_name))
            , _role_name(std::make_shared<sstring>(role_name)) {
    }

    stdx::string_view role_name() const noexcept {
        return *_role_name;
    }
};

class role_already_included : public roles_argument_exception {
    std::shared_ptr<sstring> _role_name, _grantee_name;

public:
    role_already_included(stdx::string_view grantee_name, stdx::string_view role_name)
            : roles_argument_exception(
                      sprint("'%s' already includes role '%s'.", grantee_name, role_name))
            , _role_name(std::make_shared<sstring>(role_name))
            , _grantee_name(std::make_shared<sstring>(grantee_name)) {
    }

    stdx::string_view role_name() const noexcept {
        return *_role_name;
    }

    stdx::string_view grantee_name() const noexcept {
        return *_grantee_name;
    }
};

class revoke_ungranted_role : public roles_argument_exception {
    std::shared_ptr<sstring> _role_name, _revokee_name;

public:
    revoke_ungranted_role(stdx::string_view revokee_name, stdx::string_view role_name)
            : roles_argument_exception(
                      sprint("'%s' was not granted role '%s', so it cannot be revoked.", revokee_name, role_name))
            , _role_name(std::make_shared<sstring>(role_name))
            , _revokee_name(std::make_shared<sstring>(revokee_name)) {
    }

    stdx::string_view role_name() const noexcept {
        return *_role_name;
    }

    stdx::string_view revokee_name() const noexcept {
        return *_revokee_name;
    }
};

enum class recursive_role_query { yes, no };

// Abstract role manager.
//
// All implementations should throw role-related exceptions as documented, but authorization-related checking is
// handled by the CQL layer, and not here.
class role_manager {
public:
    virtual ~role_manager() = default;

    virtual stdx::string_view qualified_java_name() const noexcept = 0;

    virtual future<> start() = 0;

    virtual future<> stop() = 0;

    // Must throw `role_already_exists` for a role that has previously been created.
    virtual future<>
    create(const authenticated_user& performer, stdx::string_view role_name, const role_config&) = 0;

    // Must throw `nonexistant_role` if the role does not exist.
    virtual future<> drop(const authenticated_user& performer, stdx::string_view role_name) = 0;

    // Must throw `nonexistant_role` if the role does not exist.
    virtual future<>
    alter(const authenticated_user& performer, stdx::string_view role_name, const role_config_update&) = 0;

    // Grant `role_name` to `grantee_name`.
    //
    // Must throw `nonexistant_role` if either the role or the grantee do not exist.
    //
    // Must throw `role_already_included` if granting the role would be redundant, or create a cycle.
    virtual future<>
    grant(const authenticated_user& performer, stdx::string_view grantee_name, stdx::string_view role_name) = 0;

    // Revoke `role_name` from `revokee_name`.
    //
    // Must throw `nonexistant_role` if either the role or the revokee do not exist.
    //
    // Must throw `revoke_ungranted_role` if the role was not granted.
    virtual future<>
    revoke(const authenticated_user& performer, stdx::string_view revokee_name, stdx::string_view role_name) = 0;

    // Must throw `nonexistant_role` if the role does not exist.
    virtual future<std::unordered_set<sstring>> query_granted(stdx::string_view grantee, recursive_role_query) const = 0;

    virtual future<std::unordered_set<sstring>> query_all() const = 0;

    virtual future<bool> exists(stdx::string_view role_name) const = 0;

    /// Must throw `nonexistant_role` if the role does not exist.
    virtual future<bool> is_superuser(stdx::string_view role_name) const = 0;

    // Must throw `nonexistant_role` if the role does not exist.
    virtual future<bool> can_login(stdx::string_view role_name) const = 0;
};

}
<|endoftext|>