<commit_before>// Copyright (C) 2016 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Graphics/MaterialPipeline.hpp>

#ifndef NAZARA_RENDERER_OPENGL
#define NAZARA_RENDERER_OPENGL // Mandatory to include the OpenGL headers
#endif

#include <Nazara/Renderer/OpenGL.hpp>
#include <Nazara/Renderer/UberShaderPreprocessor.hpp>
#include <Nazara/Graphics/Debug.hpp>

namespace Nz
{
	namespace
	{
		const UInt8 r_basicFragmentShader[] = {
			#include <Nazara/Graphics/Resources/Shaders/Basic/core.frag.h>
		};

		const UInt8 r_basicVertexShader[] = {
			#include <Nazara/Graphics/Resources/Shaders/Basic/core.vert.h>
		};

		const UInt8 r_phongLightingFragmentShader[] = {
			#include <Nazara/Graphics/Resources/Shaders/PhongLighting/core.frag.h>
		};

		const UInt8 r_phongLightingVertexShader[] = {
			#include <Nazara/Graphics/Resources/Shaders/PhongLighting/core.vert.h>
		};
	}

	/*!
	* \ingroup graphics
	* \class Nz::MaterialPipeline
	*
	* \brief Graphics class used to contains all rendering states that are not allowed to change individually on rendering devices
	*/

	/*!
	* \brief Returns a reference to a MaterialPipeline built with MaterialPipelineInfo
	*
	* This function is using a cache, calling it multiples times with the same MaterialPipelineInfo will returns references to a single MaterialPipeline
	*
	* \param pipelineInfo Pipeline informations used to build/retrieve a MaterialPipeline object
	*/
	MaterialPipelineRef MaterialPipeline::GetPipeline(const MaterialPipelineInfo& pipelineInfo)
	{
		auto it = s_pipelineCache.find(pipelineInfo);
		if (it == s_pipelineCache.end())
			it = s_pipelineCache.insert(it, PipelineCache::value_type(pipelineInfo, New(pipelineInfo)));

		return it->second;
	}

	void MaterialPipeline::GenerateRenderPipeline(UInt32 flags) const
	{
		NazaraAssert(m_pipelineInfo.uberShader, "Material pipeline has no uber shader");

		ParameterList list;
		list.SetParameter("ALPHA_MAPPING",     m_pipelineInfo.hasAlphaMap);
		list.SetParameter("ALPHA_TEST",        m_pipelineInfo.alphaTest);
		list.SetParameter("COMPUTE_TBNMATRIX", m_pipelineInfo.hasNormalMap || m_pipelineInfo.hasHeightMap);
		list.SetParameter("DIFFUSE_MAPPING",   m_pipelineInfo.hasDiffuseMap);
		list.SetParameter("EMISSIVE_MAPPING",  m_pipelineInfo.hasEmissiveMap);
		list.SetParameter("NORMAL_MAPPING",    m_pipelineInfo.hasNormalMap);
		list.SetParameter("PARALLAX_MAPPING",  m_pipelineInfo.hasHeightMap);
		list.SetParameter("SHADOW_MAPPING",    m_pipelineInfo.shadowReceive);
		list.SetParameter("SPECULAR_MAPPING",  m_pipelineInfo.hasSpecularMap);
		list.SetParameter("TEXTURE_MAPPING",   m_pipelineInfo.hasAlphaMap  || m_pipelineInfo.hasDiffuseMap || m_pipelineInfo.hasEmissiveMap ||
		                                       m_pipelineInfo.hasNormalMap || m_pipelineInfo.hasHeightMap  || m_pipelineInfo.hasSpecularMap ||
		                                       flags & ShaderFlags_TextureOverlay);
		list.SetParameter("TRANSFORM",         true);

		list.SetParameter("FLAG_BILLBOARD",      static_cast<bool>((flags & ShaderFlags_Billboard) != 0));
		list.SetParameter("FLAG_DEFERRED",       static_cast<bool>((flags & ShaderFlags_Deferred) != 0));
		list.SetParameter("FLAG_INSTANCING",     static_cast<bool>((flags & ShaderFlags_Instancing) != 0));
		list.SetParameter("FLAG_TEXTUREOVERLAY", static_cast<bool>((flags & ShaderFlags_TextureOverlay) != 0));
		list.SetParameter("FLAG_VERTEXCOLOR",    static_cast<bool>((flags & ShaderFlags_VertexColor) != 0));

		Instance& instance = m_instances[flags];
		instance.uberInstance = m_pipelineInfo.uberShader->Get(list);

		RenderPipelineInfo renderPipelineInfo;
		static_cast<RenderStates&>(renderPipelineInfo).operator=(m_pipelineInfo); // Not my proudest line

		renderPipelineInfo.shader = instance.uberInstance->GetShader();

		instance.renderPipeline.Create(renderPipelineInfo);

		#define CacheUniform(name) instance.uniforms[MaterialUniform_##name] = renderPipelineInfo.shader->GetUniformLocation("Material" #name)

		CacheUniform(AlphaMap);
		CacheUniform(AlphaThreshold);
		CacheUniform(Ambient);
		CacheUniform(Diffuse);
		CacheUniform(DiffuseMap);
		CacheUniform(EmissiveMap);
		CacheUniform(HeightMap);
		CacheUniform(NormalMap);
		CacheUniform(Shininess);
		CacheUniform(Specular);
		CacheUniform(SpecularMap);

		#undef CacheUniform
	}

	bool MaterialPipeline::Initialize()
	{
		// Basic shader
		{
			UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New();

			String fragmentShader(reinterpret_cast<const char*>(r_basicFragmentShader), sizeof(r_basicFragmentShader));
			String vertexShader(reinterpret_cast<const char*>(r_basicVertexShader), sizeof(r_basicVertexShader));

			uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING");
			uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_INSTANCING FLAG_VERTEXCOLOR TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");

			UberShaderLibrary::Register("Basic", uberShader);
		}

		// PhongLighting shader
		{
			UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New();

			String fragmentShader(reinterpret_cast<const char*>(r_phongLightingFragmentShader), sizeof(r_phongLightingFragmentShader));
			String vertexShader(reinterpret_cast<const char*>(r_phongLightingVertexShader), sizeof(r_phongLightingVertexShader));

			uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_DEFERRED FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING EMISSIVE_MAPPING NORMAL_MAPPING PARALLAX_MAPPING SHADOW_MAPPING SPECULAR_MAPPING");
			uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_DEFERRED FLAG_INSTANCING FLAG_VERTEXCOLOR COMPUTE_TBNMATRIX PARALLAX_MAPPING SHADOW_MAPPING TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");

			UberShaderLibrary::Register("PhongLighting", uberShader);
		}

		// Once the base shaders are registered, we can now set some default materials
		MaterialPipelineInfo pipelineInfo;
		pipelineInfo.uberShader = UberShaderLibrary::Get("Basic");

		// Basic 2D - No depth write/face culling
		pipelineInfo.depthWrite = false;
		pipelineInfo.faceCulling = false;

		MaterialPipelineLibrary::Register("Basic2D", GetPipeline(pipelineInfo));

		// Translucent 2D - Alpha blending with no depth write/face culling
		pipelineInfo.blending = true;
		pipelineInfo.depthWrite = false;
		pipelineInfo.faceCulling = false;
		pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha;
		pipelineInfo.srcBlend = BlendFunc_SrcAlpha;

		MaterialPipelineLibrary::Register("Translucent2D", GetPipeline(pipelineInfo));

		return true;
	}

	void MaterialPipeline::Uninitialize()
	{
		s_pipelineCache.clear();
		UberShaderLibrary::Unregister("PhongLighting");
		UberShaderLibrary::Unregister("Basic");
		MaterialPipelineLibrary::Uninitialize();
	}

	MaterialPipelineLibrary::LibraryMap MaterialPipeline::s_library;
	MaterialPipeline::PipelineCache MaterialPipeline::s_pipelineCache;
}<commit_msg>Graphics/MaterialPipeline: Add Translucent3D pipeline<commit_after>// Copyright (C) 2016 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Graphics/MaterialPipeline.hpp>

#ifndef NAZARA_RENDERER_OPENGL
#define NAZARA_RENDERER_OPENGL // Mandatory to include the OpenGL headers
#endif

#include <Nazara/Renderer/OpenGL.hpp>
#include <Nazara/Renderer/UberShaderPreprocessor.hpp>
#include <Nazara/Graphics/Debug.hpp>

namespace Nz
{
	namespace
	{
		const UInt8 r_basicFragmentShader[] = {
			#include <Nazara/Graphics/Resources/Shaders/Basic/core.frag.h>
		};

		const UInt8 r_basicVertexShader[] = {
			#include <Nazara/Graphics/Resources/Shaders/Basic/core.vert.h>
		};

		const UInt8 r_phongLightingFragmentShader[] = {
			#include <Nazara/Graphics/Resources/Shaders/PhongLighting/core.frag.h>
		};

		const UInt8 r_phongLightingVertexShader[] = {
			#include <Nazara/Graphics/Resources/Shaders/PhongLighting/core.vert.h>
		};
	}

	/*!
	* \ingroup graphics
	* \class Nz::MaterialPipeline
	*
	* \brief Graphics class used to contains all rendering states that are not allowed to change individually on rendering devices
	*/

	/*!
	* \brief Returns a reference to a MaterialPipeline built with MaterialPipelineInfo
	*
	* This function is using a cache, calling it multiples times with the same MaterialPipelineInfo will returns references to a single MaterialPipeline
	*
	* \param pipelineInfo Pipeline informations used to build/retrieve a MaterialPipeline object
	*/
	MaterialPipelineRef MaterialPipeline::GetPipeline(const MaterialPipelineInfo& pipelineInfo)
	{
		auto it = s_pipelineCache.find(pipelineInfo);
		if (it == s_pipelineCache.end())
			it = s_pipelineCache.insert(it, PipelineCache::value_type(pipelineInfo, New(pipelineInfo)));

		return it->second;
	}

	void MaterialPipeline::GenerateRenderPipeline(UInt32 flags) const
	{
		NazaraAssert(m_pipelineInfo.uberShader, "Material pipeline has no uber shader");

		ParameterList list;
		list.SetParameter("ALPHA_MAPPING",     m_pipelineInfo.hasAlphaMap);
		list.SetParameter("ALPHA_TEST",        m_pipelineInfo.alphaTest);
		list.SetParameter("COMPUTE_TBNMATRIX", m_pipelineInfo.hasNormalMap || m_pipelineInfo.hasHeightMap);
		list.SetParameter("DIFFUSE_MAPPING",   m_pipelineInfo.hasDiffuseMap);
		list.SetParameter("EMISSIVE_MAPPING",  m_pipelineInfo.hasEmissiveMap);
		list.SetParameter("NORMAL_MAPPING",    m_pipelineInfo.hasNormalMap);
		list.SetParameter("PARALLAX_MAPPING",  m_pipelineInfo.hasHeightMap);
		list.SetParameter("SHADOW_MAPPING",    m_pipelineInfo.shadowReceive);
		list.SetParameter("SPECULAR_MAPPING",  m_pipelineInfo.hasSpecularMap);
		list.SetParameter("TEXTURE_MAPPING",   m_pipelineInfo.hasAlphaMap  || m_pipelineInfo.hasDiffuseMap || m_pipelineInfo.hasEmissiveMap ||
		                                       m_pipelineInfo.hasNormalMap || m_pipelineInfo.hasHeightMap  || m_pipelineInfo.hasSpecularMap ||
		                                       flags & ShaderFlags_TextureOverlay);
		list.SetParameter("TRANSFORM",         true);

		list.SetParameter("FLAG_BILLBOARD",      static_cast<bool>((flags & ShaderFlags_Billboard) != 0));
		list.SetParameter("FLAG_DEFERRED",       static_cast<bool>((flags & ShaderFlags_Deferred) != 0));
		list.SetParameter("FLAG_INSTANCING",     static_cast<bool>((flags & ShaderFlags_Instancing) != 0));
		list.SetParameter("FLAG_TEXTUREOVERLAY", static_cast<bool>((flags & ShaderFlags_TextureOverlay) != 0));
		list.SetParameter("FLAG_VERTEXCOLOR",    static_cast<bool>((flags & ShaderFlags_VertexColor) != 0));

		Instance& instance = m_instances[flags];
		instance.uberInstance = m_pipelineInfo.uberShader->Get(list);

		RenderPipelineInfo renderPipelineInfo;
		static_cast<RenderStates&>(renderPipelineInfo).operator=(m_pipelineInfo); // Not my proudest line

		renderPipelineInfo.shader = instance.uberInstance->GetShader();

		instance.renderPipeline.Create(renderPipelineInfo);

		#define CacheUniform(name) instance.uniforms[MaterialUniform_##name] = renderPipelineInfo.shader->GetUniformLocation("Material" #name)

		CacheUniform(AlphaMap);
		CacheUniform(AlphaThreshold);
		CacheUniform(Ambient);
		CacheUniform(Diffuse);
		CacheUniform(DiffuseMap);
		CacheUniform(EmissiveMap);
		CacheUniform(HeightMap);
		CacheUniform(NormalMap);
		CacheUniform(Shininess);
		CacheUniform(Specular);
		CacheUniform(SpecularMap);

		#undef CacheUniform
	}

	bool MaterialPipeline::Initialize()
	{
		// Basic shader
		{
			UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New();

			String fragmentShader(reinterpret_cast<const char*>(r_basicFragmentShader), sizeof(r_basicFragmentShader));
			String vertexShader(reinterpret_cast<const char*>(r_basicVertexShader), sizeof(r_basicVertexShader));

			uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING");
			uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_INSTANCING FLAG_VERTEXCOLOR TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");

			UberShaderLibrary::Register("Basic", uberShader);
		}

		// PhongLighting shader
		{
			UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New();

			String fragmentShader(reinterpret_cast<const char*>(r_phongLightingFragmentShader), sizeof(r_phongLightingFragmentShader));
			String vertexShader(reinterpret_cast<const char*>(r_phongLightingVertexShader), sizeof(r_phongLightingVertexShader));

			uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_DEFERRED FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING EMISSIVE_MAPPING NORMAL_MAPPING PARALLAX_MAPPING SHADOW_MAPPING SPECULAR_MAPPING");
			uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_DEFERRED FLAG_INSTANCING FLAG_VERTEXCOLOR COMPUTE_TBNMATRIX PARALLAX_MAPPING SHADOW_MAPPING TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");

			UberShaderLibrary::Register("PhongLighting", uberShader);
		}

		// Once the base shaders are registered, we can now set some default materials
		MaterialPipelineInfo pipelineInfo;
		pipelineInfo.uberShader = UberShaderLibrary::Get("Basic");

		// Basic 2D - No depth write/face culling
		pipelineInfo.depthWrite = false;
		pipelineInfo.faceCulling = false;

		MaterialPipelineLibrary::Register("Basic2D", GetPipeline(pipelineInfo));

		// Translucent 2D - Alpha blending with no depth write/face culling
		pipelineInfo.blending = true;
		pipelineInfo.depthWrite = false;
		pipelineInfo.faceCulling = false;
		pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha;
		pipelineInfo.srcBlend = BlendFunc_SrcAlpha;

		MaterialPipelineLibrary::Register("Translucent2D", GetPipeline(pipelineInfo));

		// Translucent 3D - Alpha blending with depth buffer and no depth write/face culling
		pipelineInfo.blending = true;
		pipelineInfo.depthBuffer = true;
		pipelineInfo.depthWrite = false;
		pipelineInfo.faceCulling = false;
		pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha;
		pipelineInfo.srcBlend = BlendFunc_SrcAlpha;

		MaterialPipelineLibrary::Register("Translucent3D", GetPipeline(pipelineInfo));

		return true;
	}

	void MaterialPipeline::Uninitialize()
	{
		s_pipelineCache.clear();
		UberShaderLibrary::Unregister("PhongLighting");
		UberShaderLibrary::Unregister("Basic");
		MaterialPipelineLibrary::Uninitialize();
	}

	MaterialPipelineLibrary::LibraryMap MaterialPipeline::s_library;
	MaterialPipeline::PipelineCache MaterialPipeline::s_pipelineCache;
}<|endoftext|>


<commit_before>// Copyright (c) 2013-2018 mogemimi. Distributed under the MIT license.

#include "SamplerStateGL4.hpp"
#include "ErrorChecker.hpp"
#include "../Basic/Unreachable.hpp"
#include "Pomdog/Graphics/SamplerDescription.hpp"
#include "Pomdog/Utility/Assert.hpp"
#include <algorithm>
#include <utility>

namespace Pomdog {
namespace Detail {
namespace GL4 {
namespace {

GLenum ToTextureAddressMode(TextureAddressMode address) noexcept
{
    switch (address) {
    case TextureAddressMode::Clamp: return GL_CLAMP_TO_EDGE;
    case TextureAddressMode::Mirror: return GL_MIRRORED_REPEAT;
    case TextureAddressMode::Wrap: return GL_REPEAT;
    }
    POMDOG_UNREACHABLE("Unsupported texture address mode");
}

} // unnamed namespace

SamplerStateGL4::SamplerStateGL4(const SamplerDescription& description)
{
    samplerObject = ([] {
        SamplerObjectGL4 sampler;
        glGenSamplers(1, sampler.Data());
        POMDOG_CHECK_ERROR_GL4("glGenSamplers");
        return sampler;
    })();

    glSamplerParameteri(samplerObject->value, GL_TEXTURE_WRAP_S, ToTextureAddressMode(description.AddressU));
    glSamplerParameteri(samplerObject->value, GL_TEXTURE_WRAP_T, ToTextureAddressMode(description.AddressV));
    glSamplerParameteri(samplerObject->value, GL_TEXTURE_WRAP_R, ToTextureAddressMode(description.AddressW));

    POMDOG_CHECK_ERROR_GL4("glSamplerParameteri");

    switch (description.Filter) {
    case TextureFilter::Linear:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        break;
    case TextureFilter::Point:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        break;
    case TextureFilter::LinearMipPoint:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        break;
    case TextureFilter::PointMipLinear:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        break;
    case TextureFilter::MinLinearMagPointMipLinear:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        break;
    case TextureFilter::MinLinearMagPointMipPoint:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        break;
    case TextureFilter::MinPointMagLinearMipLinear:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        break;
    case TextureFilter::MinPointMagLinearMipPoint:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        break;
    case TextureFilter::Anisotropic:
        {
            ///@todo Not implemented:
            glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
            glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            POMDOG_CHECK_ERROR_GL4("glSamplerParameteri");

            POMDOG_ASSERT(1 <= description.MaxAnisotropy && description.MaxAnisotropy <= 16);

            GLfloat deviceMaxAnisotropy = 1.0f;

            glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &deviceMaxAnisotropy);
            POMDOG_CHECK_ERROR_GL4("glGetFloatv");

            deviceMaxAnisotropy = std::min(deviceMaxAnisotropy,
                static_cast<decltype(deviceMaxAnisotropy)>(description.MaxAnisotropy));

            glSamplerParameterf(samplerObject->value, GL_TEXTURE_MAX_ANISOTROPY_EXT, deviceMaxAnisotropy);
            POMDOG_CHECK_ERROR_GL4("glSamplerParameterf");
        }
        break;
    }

    {
        POMDOG_ASSERT(description.MinMipLevel <= description.MaxMipLevel);
        POMDOG_ASSERT(description.MaxMipLevel <= std::numeric_limits<GLfloat>::max());

        glSamplerParameterf(samplerObject->value, GL_TEXTURE_MIN_LOD, description.MaxMipLevel);
        glSamplerParameterf(samplerObject->value, GL_TEXTURE_MAX_LOD, description.MaxMipLevel);
        glSamplerParameterf(samplerObject->value, GL_TEXTURE_LOD_BIAS, description.MipMapLevelOfDetailBias);
        POMDOG_CHECK_ERROR_GL4("glSamplerParameterf");
    }
}

SamplerStateGL4::~SamplerStateGL4()
{
    if (samplerObject) {
        glDeleteSamplers(1, samplerObject->Data());
        POMDOG_CHECK_ERROR_GL4("glDeleteSamplers");
    }
}

void SamplerStateGL4::Apply(int index)
{
    static_assert(GL_TEXTURE19 == (GL_TEXTURE0 + 19), "");
    POMDOG_ASSERT(index >= 0);
    POMDOG_ASSERT(index <= 19);

    POMDOG_ASSERT(samplerObject);
    glBindSampler(index, samplerObject->value);
    POMDOG_CHECK_ERROR_GL4("glBindSampler");
}

} // namespace GL4
} // namespace Detail
} // namespace Pomdog
<commit_msg>Minor refactor<commit_after>// Copyright (c) 2013-2018 mogemimi. Distributed under the MIT license.

#include "SamplerStateGL4.hpp"
#include "ErrorChecker.hpp"
#include "../Basic/Unreachable.hpp"
#include "Pomdog/Graphics/SamplerDescription.hpp"
#include "Pomdog/Utility/Assert.hpp"
#include <algorithm>
#include <utility>

namespace Pomdog {
namespace Detail {
namespace GL4 {
namespace {

GLenum ToTextureAddressMode(TextureAddressMode address) noexcept
{
    switch (address) {
    case TextureAddressMode::Clamp: return GL_CLAMP_TO_EDGE;
    case TextureAddressMode::Mirror: return GL_MIRRORED_REPEAT;
    case TextureAddressMode::Wrap: return GL_REPEAT;
    }
    POMDOG_UNREACHABLE("Unsupported texture address mode");
}

} // unnamed namespace

SamplerStateGL4::SamplerStateGL4(const SamplerDescription& description)
{
    samplerObject = ([] {
        SamplerObjectGL4 sampler;
        glGenSamplers(1, sampler.Data());
        POMDOG_CHECK_ERROR_GL4("glGenSamplers");
        return sampler;
    })();

    glSamplerParameteri(samplerObject->value, GL_TEXTURE_WRAP_S, ToTextureAddressMode(description.AddressU));
    glSamplerParameteri(samplerObject->value, GL_TEXTURE_WRAP_T, ToTextureAddressMode(description.AddressV));
    glSamplerParameteri(samplerObject->value, GL_TEXTURE_WRAP_R, ToTextureAddressMode(description.AddressW));

    POMDOG_CHECK_ERROR_GL4("glSamplerParameteri");

    switch (description.Filter) {
    case TextureFilter::Linear:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        break;
    case TextureFilter::Point:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        break;
    case TextureFilter::LinearMipPoint:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        break;
    case TextureFilter::PointMipLinear:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        break;
    case TextureFilter::MinLinearMagPointMipLinear:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        break;
    case TextureFilter::MinLinearMagPointMipPoint:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        break;
    case TextureFilter::MinPointMagLinearMipLinear:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        break;
    case TextureFilter::MinPointMagLinearMipPoint:
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        break;
    case TextureFilter::Anisotropic: {
        // FIXME: Not implemented
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glSamplerParameteri(samplerObject->value, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        POMDOG_CHECK_ERROR_GL4("glSamplerParameteri");

        POMDOG_ASSERT(1 <= description.MaxAnisotropy && description.MaxAnisotropy <= 16);

        GLfloat deviceMaxAnisotropy = 1.0f;
        glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &deviceMaxAnisotropy);
        POMDOG_CHECK_ERROR_GL4("glGetFloatv");

        deviceMaxAnisotropy = std::min(deviceMaxAnisotropy,
            static_cast<decltype(deviceMaxAnisotropy)>(description.MaxAnisotropy));

        glSamplerParameterf(samplerObject->value, GL_TEXTURE_MAX_ANISOTROPY_EXT, deviceMaxAnisotropy);
        POMDOG_CHECK_ERROR_GL4("glSamplerParameterf");
        break;
    }
    }

    {
        POMDOG_ASSERT(description.MinMipLevel <= description.MaxMipLevel);
        POMDOG_ASSERT(description.MaxMipLevel <= std::numeric_limits<GLfloat>::max());

        glSamplerParameterf(samplerObject->value, GL_TEXTURE_MIN_LOD, description.MaxMipLevel);
        glSamplerParameterf(samplerObject->value, GL_TEXTURE_MAX_LOD, description.MaxMipLevel);
        glSamplerParameterf(samplerObject->value, GL_TEXTURE_LOD_BIAS, description.MipMapLevelOfDetailBias);
        POMDOG_CHECK_ERROR_GL4("glSamplerParameterf");
    }
}

SamplerStateGL4::~SamplerStateGL4()
{
    if (samplerObject) {
        glDeleteSamplers(1, samplerObject->Data());
        POMDOG_CHECK_ERROR_GL4("glDeleteSamplers");
    }
}

void SamplerStateGL4::Apply(int index)
{
    static_assert(GL_TEXTURE19 == (GL_TEXTURE0 + 19), "");
    POMDOG_ASSERT(index >= 0);
    POMDOG_ASSERT(index <= 19);

    POMDOG_ASSERT(samplerObject);
    glBindSampler(index, samplerObject->value);
    POMDOG_CHECK_ERROR_GL4("glBindSampler");
}

} // namespace GL4
} // namespace Detail
} // namespace Pomdog
<|endoftext|>


<commit_before>/*  This file is part of the KDE libraries and the Kate part.
 *
 *  Copyright (C) 2001-2010 Christoph Cullmann <cullmann@kde.org>
 *  Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
 *  Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
 *  Copyright (C) 2007 Dominik Haumann <dhaumann kde org>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 */

#include "katepartpluginmanager.h"
#include "katepartpluginmanager.moc"

#include "kateglobal.h"

#include <ktexteditor/plugin.h>
#include <ktexteditor/document.h>
#include <ktexteditor/view.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kxmlguifactory.h>
#include <kplugininfo.h>

#include <kservicetypetrader.h>
#include <kdebug.h>

KatePartPluginInfo::KatePartPluginInfo(KService::Ptr service)
    : m_pluginInfo(service)
{
}

QString KatePartPluginInfo::saveName() const
{
  QString saveName = m_pluginInfo.pluginName();
  if (saveName.isEmpty())
    saveName = service()->library();
  return saveName;
}

KatePartPluginManager::KatePartPluginManager()
  : QObject(),
    m_config(new KConfig("katepartpluginsrc", KConfig::NoGlobals))
{
  setupPluginList ();
  loadConfig ();
}

KatePartPluginManager::~KatePartPluginManager()
{
  writeConfig();
  // than unload the plugins
  unloadAllPlugins ();
  delete m_config;
  m_config = 0;
}

KatePartPluginManager *KatePartPluginManager::self()
{
  return KateGlobal::self()->pluginManager ();
}

void KatePartPluginManager::setupPluginList ()
{
  KService::List traderList = KServiceTypeTrader::self()->
      query("KTextEditor/Plugin",
            "([X-KDE-Version] >= 4.0) and ([X-KDE-Version] <= " + QString("%1.%2").arg(KDE::versionMajor()).arg(KDE::versionMinor()) + ')');

  foreach(const KService::Ptr &ptr, traderList)
  {
    KatePartPluginInfo info(ptr);

    info.load = false;
    info.plugin = 0L;

    m_pluginList.push_back (info);
  }
}

void KatePartPluginManager::addDocument(KTextEditor::Document *doc)
{
  //kDebug() << doc;
  for (KatePartPluginList::iterator it = m_pluginList.begin();
      it != m_pluginList.end(); ++it)
  {
    if (it->load) {
      it->plugin->addDocument(doc);
    }
  }
}

void KatePartPluginManager::removeDocument(KTextEditor::Document *doc)
{
  //kDebug() << doc;
  for (KatePartPluginList::iterator it = m_pluginList.begin();
      it != m_pluginList.end(); ++it)
  {
    if (it->load) {
      it->plugin->removeDocument(doc);
    }
  }
}

void KatePartPluginManager::addView(KTextEditor::View *view)
{
  //kDebug() << view;
  for (KatePartPluginList::iterator it = m_pluginList.begin();
      it != m_pluginList.end(); ++it)
  {
    if (it->load) {
      it->plugin->addView(view);
    }
  }
}

void KatePartPluginManager::removeView(KTextEditor::View *view)
{
  //kDebug() << view;
  for (KatePartPluginList::iterator it = m_pluginList.begin();
      it != m_pluginList.end(); ++it)
  {
    if (it->load) {
      it->plugin->removeView(view);
    }
  }
}

void KatePartPluginManager::loadConfig ()
{
  // first: unload the plugins
  unloadAllPlugins ();

  KConfigGroup cg = KConfigGroup(m_config, "Kate Part Plugins");

  // disable all plugin if no config...
  foreach (const KatePartPluginInfo &plugin, m_pluginList) {
    bool enabledByDefault = plugin.isEnabledByDefault();
    plugin.load = cg.readEntry (plugin.saveName(), enabledByDefault);
  }

  loadAllPlugins();
}

void KatePartPluginManager::writeConfig()
{
  KConfigGroup cg = KConfigGroup( m_config, "Kate Part Plugins" );
  foreach(const KatePartPluginInfo &it, m_pluginList)
  {
    cg.writeEntry (it.saveName(), it.load);
  }
}

void KatePartPluginManager::loadAllPlugins ()
{
  for (KatePartPluginList::iterator it = m_pluginList.begin();
      it != m_pluginList.end(); ++it)
  {
    if (it->load)
    {
      loadPlugin(*it);
      enablePlugin(*it);
    }
  }
}

void KatePartPluginManager::unloadAllPlugins ()
{
  for (KatePartPluginList::iterator it = m_pluginList.begin();
       it != m_pluginList.end(); ++it)
  {
    if (it->plugin) {
      disablePlugin(*it);
      unloadPlugin(*it);
    }
  }
}

void KatePartPluginManager::loadPlugin (KatePartPluginInfo &item)
{
  if (item.plugin) return;

  // make sure all dependencies are loaded beforehand
  QStringList openDependencies = item.dependencies();
  if ( !openDependencies.empty() )
  {
    for (KatePartPluginList::iterator it = m_pluginList.begin();
      it != m_pluginList.end(); ++it)
    {
      if ( openDependencies.contains( it->saveName() ) )
      {
        loadPlugin( *it );
        openDependencies.removeAll( it->saveName() );
      }
    }
    Q_ASSERT( openDependencies.empty() );
  }

  item.plugin = item.service()->createInstance<KTextEditor::Plugin>(this);
  Q_ASSERT(item.plugin);
  item.load = (item.plugin != 0);
}

void KatePartPluginManager::unloadPlugin (KatePartPluginInfo &item)
{
  if ( !item.plugin ) return;

  // make sure dependent plugins are unloaded beforehand
  for (KatePartPluginList::iterator it = m_pluginList.begin();
    it != m_pluginList.end(); ++it)
  {
    if ( !it->plugin ) continue;

    if ( it->dependencies().contains( item.saveName() ) )
    {
      unloadPlugin( *it );
    }
  }

  delete item.plugin;
  item.plugin = 0L;
  item.load = false;
}

void KatePartPluginManager::enablePlugin (KatePartPluginInfo &item)
{
  // plugin around at all?
  if (!item.plugin || !item.load)
    return;

  // register docs and views
  foreach (KTextEditor::Document *doc, KateGlobal::self()->documents()) {
    if (!doc)
      continue;

    foreach (KTextEditor::View *view, doc->views()) {
      if (!view)
        continue;

      KXMLGUIFactory *viewFactory = view->factory();
      if (viewFactory)
        viewFactory->removeClient(view);

      item.plugin->addView(view);

      if (viewFactory)
        viewFactory->addClient(view);
    }
  }
}

void KatePartPluginManager::disablePlugin (KatePartPluginInfo &item)
{
  // plugin around at all?
  if (!item.plugin || !item.load)
    return;

  // de-register docs and views
  foreach (KTextEditor::Document *doc, KateGlobal::self()->documents()) {
    if (!doc)
      continue;

    foreach (KTextEditor::View *view, doc->views()) {
      if (!view)
        continue;

      KXMLGUIFactory *viewFactory = view->factory();
      if (viewFactory)
        viewFactory->removeClient(view);

      item.plugin->removeView(view);

      if (viewFactory)
        viewFactory->addClient(view);
    }
  }
}

// kate: space-indent on; indent-width 2; replace-tabs on;
<commit_msg>don't assert here, we can handle this, the plugin is just not loaded then<commit_after>/*  This file is part of the KDE libraries and the Kate part.
 *
 *  Copyright (C) 2001-2010 Christoph Cullmann <cullmann@kde.org>
 *  Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
 *  Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
 *  Copyright (C) 2007 Dominik Haumann <dhaumann kde org>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 */

#include "katepartpluginmanager.h"
#include "katepartpluginmanager.moc"

#include "kateglobal.h"

#include <ktexteditor/plugin.h>
#include <ktexteditor/document.h>
#include <ktexteditor/view.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kxmlguifactory.h>
#include <kplugininfo.h>

#include <kservicetypetrader.h>
#include <kdebug.h>

KatePartPluginInfo::KatePartPluginInfo(KService::Ptr service)
    : m_pluginInfo(service)
{
}

QString KatePartPluginInfo::saveName() const
{
  QString saveName = m_pluginInfo.pluginName();
  if (saveName.isEmpty())
    saveName = service()->library();
  return saveName;
}

KatePartPluginManager::KatePartPluginManager()
  : QObject(),
    m_config(new KConfig("katepartpluginsrc", KConfig::NoGlobals))
{
  setupPluginList ();
  loadConfig ();
}

KatePartPluginManager::~KatePartPluginManager()
{
  writeConfig();
  // than unload the plugins
  unloadAllPlugins ();
  delete m_config;
  m_config = 0;
}

KatePartPluginManager *KatePartPluginManager::self()
{
  return KateGlobal::self()->pluginManager ();
}

void KatePartPluginManager::setupPluginList ()
{
  KService::List traderList = KServiceTypeTrader::self()->
      query("KTextEditor/Plugin",
            "([X-KDE-Version] >= 4.0) and ([X-KDE-Version] <= " + QString("%1.%2").arg(KDE::versionMajor()).arg(KDE::versionMinor()) + ')');

  foreach(const KService::Ptr &ptr, traderList)
  {
    KatePartPluginInfo info(ptr);

    info.load = false;
    info.plugin = 0L;

    m_pluginList.push_back (info);
  }
}

void KatePartPluginManager::addDocument(KTextEditor::Document *doc)
{
  //kDebug() << doc;
  for (KatePartPluginList::iterator it = m_pluginList.begin();
      it != m_pluginList.end(); ++it)
  {
    if (it->load) {
      it->plugin->addDocument(doc);
    }
  }
}

void KatePartPluginManager::removeDocument(KTextEditor::Document *doc)
{
  //kDebug() << doc;
  for (KatePartPluginList::iterator it = m_pluginList.begin();
      it != m_pluginList.end(); ++it)
  {
    if (it->load) {
      it->plugin->removeDocument(doc);
    }
  }
}

void KatePartPluginManager::addView(KTextEditor::View *view)
{
  //kDebug() << view;
  for (KatePartPluginList::iterator it = m_pluginList.begin();
      it != m_pluginList.end(); ++it)
  {
    if (it->load) {
      it->plugin->addView(view);
    }
  }
}

void KatePartPluginManager::removeView(KTextEditor::View *view)
{
  //kDebug() << view;
  for (KatePartPluginList::iterator it = m_pluginList.begin();
      it != m_pluginList.end(); ++it)
  {
    if (it->load) {
      it->plugin->removeView(view);
    }
  }
}

void KatePartPluginManager::loadConfig ()
{
  // first: unload the plugins
  unloadAllPlugins ();

  KConfigGroup cg = KConfigGroup(m_config, "Kate Part Plugins");

  // disable all plugin if no config...
  foreach (const KatePartPluginInfo &plugin, m_pluginList) {
    bool enabledByDefault = plugin.isEnabledByDefault();
    plugin.load = cg.readEntry (plugin.saveName(), enabledByDefault);
  }

  loadAllPlugins();
}

void KatePartPluginManager::writeConfig()
{
  KConfigGroup cg = KConfigGroup( m_config, "Kate Part Plugins" );
  foreach(const KatePartPluginInfo &it, m_pluginList)
  {
    cg.writeEntry (it.saveName(), it.load);
  }
}

void KatePartPluginManager::loadAllPlugins ()
{
  for (KatePartPluginList::iterator it = m_pluginList.begin();
      it != m_pluginList.end(); ++it)
  {
    if (it->load)
    {
      loadPlugin(*it);
      enablePlugin(*it);
    }
  }
}

void KatePartPluginManager::unloadAllPlugins ()
{
  for (KatePartPluginList::iterator it = m_pluginList.begin();
       it != m_pluginList.end(); ++it)
  {
    if (it->plugin) {
      disablePlugin(*it);
      unloadPlugin(*it);
    }
  }
}

void KatePartPluginManager::loadPlugin (KatePartPluginInfo &item)
{
  if (item.plugin) return;

  // make sure all dependencies are loaded beforehand
  QStringList openDependencies = item.dependencies();
  if ( !openDependencies.empty() )
  {
    for (KatePartPluginList::iterator it = m_pluginList.begin();
      it != m_pluginList.end(); ++it)
    {
      if ( openDependencies.contains( it->saveName() ) )
      {
        loadPlugin( *it );
        openDependencies.removeAll( it->saveName() );
      }
    }
    Q_ASSERT( openDependencies.empty() );
  }

  // try to load, else reset load flag!
  item.plugin = item.service()->createInstance<KTextEditor::Plugin>(this);
  item.load = (item.plugin != 0);
}

void KatePartPluginManager::unloadPlugin (KatePartPluginInfo &item)
{
  if ( !item.plugin ) return;

  // make sure dependent plugins are unloaded beforehand
  for (KatePartPluginList::iterator it = m_pluginList.begin();
    it != m_pluginList.end(); ++it)
  {
    if ( !it->plugin ) continue;

    if ( it->dependencies().contains( item.saveName() ) )
    {
      unloadPlugin( *it );
    }
  }

  delete item.plugin;
  item.plugin = 0L;
  item.load = false;
}

void KatePartPluginManager::enablePlugin (KatePartPluginInfo &item)
{
  // plugin around at all?
  if (!item.plugin || !item.load)
    return;

  // register docs and views
  foreach (KTextEditor::Document *doc, KateGlobal::self()->documents()) {
    if (!doc)
      continue;

    foreach (KTextEditor::View *view, doc->views()) {
      if (!view)
        continue;

      KXMLGUIFactory *viewFactory = view->factory();
      if (viewFactory)
        viewFactory->removeClient(view);

      item.plugin->addView(view);

      if (viewFactory)
        viewFactory->addClient(view);
    }
  }
}

void KatePartPluginManager::disablePlugin (KatePartPluginInfo &item)
{
  // plugin around at all?
  if (!item.plugin || !item.load)
    return;

  // de-register docs and views
  foreach (KTextEditor::Document *doc, KateGlobal::self()->documents()) {
    if (!doc)
      continue;

    foreach (KTextEditor::View *view, doc->views()) {
      if (!view)
        continue;

      KXMLGUIFactory *viewFactory = view->factory();
      if (viewFactory)
        viewFactory->removeClient(view);

      item.plugin->removeView(view);

      if (viewFactory)
        viewFactory->addClient(view);
    }
  }
}

// kate: space-indent on; indent-width 2; replace-tabs on;
<|endoftext|>


<commit_before>/* +---------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)               |
   |                          https://www.mrpt.org/                            |
   |                                                                           |
   | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file        |
   | See: https://www.mrpt.org/Authors - All rights reserved.                  |
   | Released under BSD License. See details in https://www.mrpt.org/License   |
   +---------------------------------------------------------------------------+
 */

#include "vision-precomp.h"  // Precompiled headers

#include <mrpt/core/exceptions.h>
#include <mrpt/vision/CFeatureLines.h>
#include <mrpt/vision/utils.h>

// Universal include for all versions of OpenCV
#include <mrpt/otherlibs/do_opencv_includes.h>

using namespace mrpt::vision;
using namespace std;

#if MRPT_HAS_OPENCV

static void extractLines_CannyHough(
	const cv::Mat& canny_image, const std::vector<cv::Vec2f> lines,
	std::vector<cv::Vec4i>& segments, size_t threshold)
{
	// Some variables to change the coordinate system from polar to cartesian
	double rho, theta;
	double cosTheta, sinTheta;
	double m, c, cMax;

	// Variables to define the two extreme points on the line, clipped on the
	// window The constraint is x <= xF
	int x, y, xF, yF;

	segments.clear();

	// For each line
	for (size_t n(0); n < lines.size(); ++n)
	{
		// OpenCV implements the Hough accumulator with theta from 0 to PI, thus
		// rho could be negative We want to always have rho >= 0
		if (lines[n][0] < 0)
		{
			rho = -lines[n][0];
			theta = lines[n][1] - CV_PI;
		}
		else
		{
			rho = lines[n][0];
			theta = lines[n][1];
		}

		if (rho == 0 && theta == 0)
		{  // That case appeared at least once, so a test is needed
			continue;
		}

		// Since the step for theta in cv::HoughLines should not be too small,
		// theta should exactly be 0 (and the line vertical) when this condition
		// is true
		if (fabs(theta) < 0.00001)
		{
			x = xF = static_cast<int>(rho + 0.5);
			y = 0;
			yF = canny_image.rows - 1;
		}
		else
		{
			// Get the (m, c) slope and y-intercept from (rho, theta), so that y
			// = mx + c <=> (x, y) is on the line
			cosTheta = cos(theta);
			sinTheta = sin(theta);
			m = -cosTheta / sinTheta;  // We are certain that sinTheta != 0
			c = rho * (sinTheta - m * cosTheta);

			// Get the two extreme points (x, y) and (xF, xF) for the line
			// inside the window, using (m, c)
			if (c >= 0)
			{
				if (c < canny_image.rows)
				{
					// (x, y) is at the left of the window
					x = 0;
					y = static_cast<int>(c);
				}
				else
				{
					// (x, y) is at the bottom of the window
					y = canny_image.rows - 1;
					x = static_cast<int>((y - c) / m);
				}
			}
			else
			{
				// (x, y) is at the top of the window
				x = static_cast<int>(-c / m);
				y = 0;
			}
			// Define the intersection with the right side of the window
			cMax = m * (canny_image.cols - 1) + c;
			if (cMax >= 0)
			{
				if (cMax < canny_image.rows)
				{
					// (xF, yF) is at the right of the window
					xF = canny_image.cols - 1;
					yF = static_cast<int>(cMax);
				}
				else
				{
					// (xF, yF) is at the bottom of the window
					yF = canny_image.rows - 1;
					xF = static_cast<int>((yF - c) / m);
				}
			}
			else
			{
				// (xF, yF) is at the top of the window
				xF = static_cast<int>(-c / m);
				yF = 0;
			}
		}

		// Going through the line using the Bresenham algorithm
		// dx1, dx2, dy1 and dy2 are increments that allow to be successful for
		// each of the 8 octants (possible directions while iterating)
		bool onSegment = false;
		int memory;
		int memoryX = 0, memoryY = 0;
		int xPrev = 0, yPrev = 0;
		size_t nbPixels = 0;

		int w = xF - x;
		int h = yF - y;
		int dx1, dy1, dx2, dy2 = 0;

		int longest, shortest;
		int numerator;

		if (w < 0)
		{
			longest = -w;
			dx1 = -1;
			dx2 = -1;
		}
		else
		{
			longest = w;
			dx1 = 1;
			dx2 = 1;
		}

		if (h < 0)
		{
			shortest = -h;
			dy1 = -1;
		}
		else
		{
			shortest = h;
			dy1 = 1;
		}

		// We want to know whether the direction is more horizontal or vertical,
		// and set the increments accordingly
		if (longest <= shortest)
		{
			memory = longest;
			longest = shortest;
			shortest = memory;
			dx2 = 0;
			if (h < 0)
			{
				dy2 = -1;
			}
			else
			{
				dy2 = 1;
			}
		}

		numerator = longest / 2;

		// cout << n << " numerator " << numerator << endl;
		for (int i(0); i <= longest; ++i)
		{
			// For each pixel, we don't want to use a classic "plot", but to
			// look into canny_image for a black or white pixel
			if (onSegment)
			{
				if (canny_image.at<char>(y, x) == 0 || i == longest)
				{
					// We are leaving a segment
					onSegment = false;
					if (nbPixels >= threshold)
					{
						segments.emplace_back(memoryX, memoryY, xPrev, yPrev);
						// cout << "new segment " << segments.back() << endl;
					}
				}
				else
				{
					// We are still on a segment
					++nbPixels;
				}
			}
			else if (canny_image.at<char>(y, x) != 0)
			{
				// We are entering a segment, and keep this first position in
				// (memoryX, memoryY)
				onSegment = true;
				nbPixels = 0;
				memoryX = x;
				memoryY = y;
			}

			// xPrev and yPrev are used when leaving a segment, to keep in
			// memory the last pixel on it
			xPrev = x;
			yPrev = y;

			// Next pixel using the condition of the Bresenham algorithm
			numerator += shortest;
			if (numerator >= longest)
			{
				numerator -= longest;
				x += dx1;
				y += dy1;
			}
			else
			{
				x += dx2;
				y += dy2;
			}
		}
	}
}
#endif

void CFeatureLines::extractLines(
	const mrpt::img::CImage& img_in,
	std::vector<std::array<int, 4>>& segments_out, size_t threshold,
	const bool display)
{
#if MRPT_HAS_OPENCV

	using namespace cv;
	using namespace std;
	const Mat image = img_in.asCvMat<Mat>(mrpt::img::SHALLOW_COPY);

	// Canny edge detector
	cv::Mat canny_img;
	int lowThreshold = 150;
	// int const max_lowThreshold = 100;
	int ratio = 3;
	int kernel_size = 3;
	cv::Canny(
		image, canny_img, lowThreshold, lowThreshold * ratio,
		kernel_size);  // 250, 600 // CAUTION: Both thresholds depend on the
	// input image, they might be a bit hard to set because
	// they depend on the strength of the gradients
	//            cv::namedWindow("Canny detector", cv::WINDOW_AUTOSIZE);
	//            cv::imshow("Canny detector", canny_img);
	//            cv::waitKey(0);
	//            cv::destroyWindow("Canny detector");

	// Get lines through the Hough transform

	// CvMat matCannyStub, *matCannyImg = (CvMat*)canny_img.getAs<IplImage>();
	// CV_CALL(matCannyImg = cvGetMat(canny_img.getAs<IplImage>(),
	// &matCannyStub));

	std::vector<cv::Vec2f> lines;
	cv::HoughLines(
		canny_img, lines, 1, CV_PI / 180.0,
		threshold);  // CAUTION: The last parameter depends on the input image,
	// it's the smallest number of pixels to consider a line in
	// the accumulator
	//    double minLineLength=50, maxLineGap=5;
	//    cv::HoughLinesP(canny_img, lines, 1, CV_PI / 180.0, threshold,
	//    minLineLength, maxLineGap); // CAUTION: The last parameter depends on
	//    the input image, it's the smallest number of pixels to consider a line
	//    in the accumulator
	// std::cout << lines.size() << " lines detected" << std::endl;

	// Possible dilatation of the canny detector
	// Useful when the lines are thin and not perfectly straight
	cv::Mat filteredCanny;
	// Choose whether filtering the Canny or not, for thin and non-perfect edges
	/*filteredCanny = canny_img.clone();*/
	cv::dilate(canny_img, filteredCanny, cv::Mat());
	//    cv::namedWindow("Filtered Canny detector");
	//    cv::imshow("Filtered Canny detector", filteredCanny);
	//    cv::waitKey(0);
	//    cv::destroyWindow("Filtered Canny detector");

	// Extracting segments (pairs of points) from the filtered Canny detector
	// And using the line parameters from lines
	std::vector<cv::Vec4i> segments;
	extractLines_CannyHough(filteredCanny, lines, segments, threshold);

	// Force the segments to have a predefined order (e1y <= e2y)
	for (auto line = begin(segments); line != end(segments); ++line)
		if ((*line)[1] == (*line)[3])
		{
			if ((*line)[0] > (*line)[1])
				*line =
					cv::Vec4i((*line)[2], (*line)[3], (*line)[0], (*line)[1]);
		}
		else if ((*line)[1] < (*line)[3])
			*line = cv::Vec4i((*line)[2], (*line)[3], (*line)[0], (*line)[1]);

	// convert cv->STL:
	segments_out.resize(segments.size());
	for (unsigned i = 0; i < segments.size(); i++)
		for (int k = 0; i < 4; k++) segments_out[i][k] = segments[i][k];

	// Display 2D segments
	if (display)
	{
		cv::Mat image_lines;
		image.convertTo(image_lines, CV_8UC1, 1.0 / 2);
		for (auto line = begin(segments); line != end(segments); ++line)
		{
			cv::line(
				image_lines, cv::Point((*line)[0], (*line)[1]),
				cv::Point((*line)[2], (*line)[3]), cv::Scalar(255, 0, 255), 1);
			cv::circle(
				image_lines, cv::Point((*line)[0], (*line)[1]), 3,
				cv::Scalar(255, 0, 255), 3);
			cv::putText(
				image_lines, string(to_string(distance(begin(segments), line))),
				cv::Point(
					((*line)[0] + (*line)[2]) / 2,
					((*line)[1] + (*line)[3]) / 2),
				0, 1.2, cv::Scalar(200, 0, 0), 3);
		}
		cv::imshow("lines", image_lines);
		cv::moveWindow("lines", 20, 100 + 700);
		cv::waitKey(0);
	}
#else
	THROW_EXCEPTION("Requires building MRPT with OpenCV support");
#endif
}
<commit_msg>Fix obvious CFeatureLines warning<commit_after>/* +---------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)               |
   |                          https://www.mrpt.org/                            |
   |                                                                           |
   | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file        |
   | See: https://www.mrpt.org/Authors - All rights reserved.                  |
   | Released under BSD License. See details in https://www.mrpt.org/License   |
   +---------------------------------------------------------------------------+
 */

#include "vision-precomp.h"  // Precompiled headers

#include <mrpt/core/exceptions.h>
#include <mrpt/vision/CFeatureLines.h>
#include <mrpt/vision/utils.h>

// Universal include for all versions of OpenCV
#include <mrpt/otherlibs/do_opencv_includes.h>

using namespace mrpt::vision;
using namespace std;

#if MRPT_HAS_OPENCV

static void extractLines_CannyHough(
	const cv::Mat& canny_image, const std::vector<cv::Vec2f> lines,
	std::vector<cv::Vec4i>& segments, size_t threshold)
{
	// Some variables to change the coordinate system from polar to cartesian
	double rho, theta;
	double cosTheta, sinTheta;
	double m, c, cMax;

	// Variables to define the two extreme points on the line, clipped on the
	// window The constraint is x <= xF
	int x, y, xF, yF;

	segments.clear();

	// For each line
	for (size_t n(0); n < lines.size(); ++n)
	{
		// OpenCV implements the Hough accumulator with theta from 0 to PI, thus
		// rho could be negative We want to always have rho >= 0
		if (lines[n][0] < 0)
		{
			rho = -lines[n][0];
			theta = lines[n][1] - CV_PI;
		}
		else
		{
			rho = lines[n][0];
			theta = lines[n][1];
		}

		if (rho == 0 && theta == 0)
		{  // That case appeared at least once, so a test is needed
			continue;
		}

		// Since the step for theta in cv::HoughLines should not be too small,
		// theta should exactly be 0 (and the line vertical) when this condition
		// is true
		if (fabs(theta) < 0.00001)
		{
			x = xF = static_cast<int>(rho + 0.5);
			y = 0;
			yF = canny_image.rows - 1;
		}
		else
		{
			// Get the (m, c) slope and y-intercept from (rho, theta), so that y
			// = mx + c <=> (x, y) is on the line
			cosTheta = cos(theta);
			sinTheta = sin(theta);
			m = -cosTheta / sinTheta;  // We are certain that sinTheta != 0
			c = rho * (sinTheta - m * cosTheta);

			// Get the two extreme points (x, y) and (xF, xF) for the line
			// inside the window, using (m, c)
			if (c >= 0)
			{
				if (c < canny_image.rows)
				{
					// (x, y) is at the left of the window
					x = 0;
					y = static_cast<int>(c);
				}
				else
				{
					// (x, y) is at the bottom of the window
					y = canny_image.rows - 1;
					x = static_cast<int>((y - c) / m);
				}
			}
			else
			{
				// (x, y) is at the top of the window
				x = static_cast<int>(-c / m);
				y = 0;
			}
			// Define the intersection with the right side of the window
			cMax = m * (canny_image.cols - 1) + c;
			if (cMax >= 0)
			{
				if (cMax < canny_image.rows)
				{
					// (xF, yF) is at the right of the window
					xF = canny_image.cols - 1;
					yF = static_cast<int>(cMax);
				}
				else
				{
					// (xF, yF) is at the bottom of the window
					yF = canny_image.rows - 1;
					xF = static_cast<int>((yF - c) / m);
				}
			}
			else
			{
				// (xF, yF) is at the top of the window
				xF = static_cast<int>(-c / m);
				yF = 0;
			}
		}

		// Going through the line using the Bresenham algorithm
		// dx1, dx2, dy1 and dy2 are increments that allow to be successful for
		// each of the 8 octants (possible directions while iterating)
		bool onSegment = false;
		int memory;
		int memoryX = 0, memoryY = 0;
		int xPrev = 0, yPrev = 0;
		size_t nbPixels = 0;

		int w = xF - x;
		int h = yF - y;
		int dx1, dy1, dx2, dy2 = 0;

		int longest, shortest;
		int numerator;

		if (w < 0)
		{
			longest = -w;
			dx1 = -1;
			dx2 = -1;
		}
		else
		{
			longest = w;
			dx1 = 1;
			dx2 = 1;
		}

		if (h < 0)
		{
			shortest = -h;
			dy1 = -1;
		}
		else
		{
			shortest = h;
			dy1 = 1;
		}

		// We want to know whether the direction is more horizontal or vertical,
		// and set the increments accordingly
		if (longest <= shortest)
		{
			memory = longest;
			longest = shortest;
			shortest = memory;
			dx2 = 0;
			if (h < 0)
			{
				dy2 = -1;
			}
			else
			{
				dy2 = 1;
			}
		}

		numerator = longest / 2;

		// cout << n << " numerator " << numerator << endl;
		for (int i(0); i <= longest; ++i)
		{
			// For each pixel, we don't want to use a classic "plot", but to
			// look into canny_image for a black or white pixel
			if (onSegment)
			{
				if (canny_image.at<char>(y, x) == 0 || i == longest)
				{
					// We are leaving a segment
					onSegment = false;
					if (nbPixels >= threshold)
					{
						segments.emplace_back(memoryX, memoryY, xPrev, yPrev);
						// cout << "new segment " << segments.back() << endl;
					}
				}
				else
				{
					// We are still on a segment
					++nbPixels;
				}
			}
			else if (canny_image.at<char>(y, x) != 0)
			{
				// We are entering a segment, and keep this first position in
				// (memoryX, memoryY)
				onSegment = true;
				nbPixels = 0;
				memoryX = x;
				memoryY = y;
			}

			// xPrev and yPrev are used when leaving a segment, to keep in
			// memory the last pixel on it
			xPrev = x;
			yPrev = y;

			// Next pixel using the condition of the Bresenham algorithm
			numerator += shortest;
			if (numerator >= longest)
			{
				numerator -= longest;
				x += dx1;
				y += dy1;
			}
			else
			{
				x += dx2;
				y += dy2;
			}
		}
	}
}
#endif

void CFeatureLines::extractLines(
	const mrpt::img::CImage& img_in,
	std::vector<std::array<int, 4>>& segments_out, size_t threshold,
	const bool display)
{
#if MRPT_HAS_OPENCV

	using namespace cv;
	using namespace std;
	const Mat image = img_in.asCvMat<Mat>(mrpt::img::SHALLOW_COPY);

	// Canny edge detector
	cv::Mat canny_img;
	int lowThreshold = 150;
	// int const max_lowThreshold = 100;
	int ratio = 3;
	int kernel_size = 3;
	cv::Canny(
		image, canny_img, lowThreshold, lowThreshold * ratio,
		kernel_size);  // 250, 600 // CAUTION: Both thresholds depend on the
	// input image, they might be a bit hard to set because
	// they depend on the strength of the gradients
	//            cv::namedWindow("Canny detector", cv::WINDOW_AUTOSIZE);
	//            cv::imshow("Canny detector", canny_img);
	//            cv::waitKey(0);
	//            cv::destroyWindow("Canny detector");

	// Get lines through the Hough transform

	// CvMat matCannyStub, *matCannyImg = (CvMat*)canny_img.getAs<IplImage>();
	// CV_CALL(matCannyImg = cvGetMat(canny_img.getAs<IplImage>(),
	// &matCannyStub));

	std::vector<cv::Vec2f> lines;
	cv::HoughLines(
		canny_img, lines, 1, CV_PI / 180.0,
		threshold);  // CAUTION: The last parameter depends on the input image,
	// it's the smallest number of pixels to consider a line in
	// the accumulator
	//    double minLineLength=50, maxLineGap=5;
	//    cv::HoughLinesP(canny_img, lines, 1, CV_PI / 180.0, threshold,
	//    minLineLength, maxLineGap); // CAUTION: The last parameter depends on
	//    the input image, it's the smallest number of pixels to consider a line
	//    in the accumulator
	// std::cout << lines.size() << " lines detected" << std::endl;

	// Possible dilatation of the canny detector
	// Useful when the lines are thin and not perfectly straight
	cv::Mat filteredCanny;
	// Choose whether filtering the Canny or not, for thin and non-perfect edges
	/*filteredCanny = canny_img.clone();*/
	cv::dilate(canny_img, filteredCanny, cv::Mat());
	//    cv::namedWindow("Filtered Canny detector");
	//    cv::imshow("Filtered Canny detector", filteredCanny);
	//    cv::waitKey(0);
	//    cv::destroyWindow("Filtered Canny detector");

	// Extracting segments (pairs of points) from the filtered Canny detector
	// And using the line parameters from lines
	std::vector<cv::Vec4i> segments;
	extractLines_CannyHough(filteredCanny, lines, segments, threshold);

	// Force the segments to have a predefined order (e1y <= e2y)
	for (auto line = begin(segments); line != end(segments); ++line)
		if ((*line)[1] == (*line)[3])
		{
			if ((*line)[0] > (*line)[1])
				*line =
					cv::Vec4i((*line)[2], (*line)[3], (*line)[0], (*line)[1]);
		}
		else if ((*line)[1] < (*line)[3])
			*line = cv::Vec4i((*line)[2], (*line)[3], (*line)[0], (*line)[1]);

	// convert cv->STL:
	segments_out.resize(segments.size());
	for (unsigned i = 0; i < segments.size(); i++)
		for (size_t k = 0; k < segments_out[i].size(); k++) segments_out[i][k] = segments[i][k];

	// Display 2D segments
	if (display)
	{
		cv::Mat image_lines;
		image.convertTo(image_lines, CV_8UC1, 1.0 / 2);
		for (auto line = begin(segments); line != end(segments); ++line)
		{
			cv::line(
				image_lines, cv::Point((*line)[0], (*line)[1]),
				cv::Point((*line)[2], (*line)[3]), cv::Scalar(255, 0, 255), 1);
			cv::circle(
				image_lines, cv::Point((*line)[0], (*line)[1]), 3,
				cv::Scalar(255, 0, 255), 3);
			cv::putText(
				image_lines, string(to_string(distance(begin(segments), line))),
				cv::Point(
					((*line)[0] + (*line)[2]) / 2,
					((*line)[1] + (*line)[3]) / 2),
				0, 1.2, cv::Scalar(200, 0, 0), 3);
		}
		cv::imshow("lines", image_lines);
		cv::moveWindow("lines", 20, 100 + 700);
		cv::waitKey(0);
	}
#else
	THROW_EXCEPTION("Requires building MRPT with OpenCV support");
#endif
}
<|endoftext|>


<commit_before>/**
 * @file   RotatePath.hpp
 * @brief  RotatePath class prototype.
 * @author zer0
 * @date   2017-07-31
 */

#ifndef __INCLUDE_LIBTBAG__LIBTBAG_FILESYSTEM_ROTATEPATH_HPP__
#define __INCLUDE_LIBTBAG__LIBTBAG_FILESYSTEM_ROTATEPATH_HPP__

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

#include <libtbag/config.h>
#include <libtbag/predef.hpp>
#include <libtbag/Err.hpp>
#include <libtbag/Unit.hpp>

#include <libtbag/debug/Assert.hpp>
#include <libtbag/archive/Archive.hpp>
#include <libtbag/string/Environments.hpp>
#include <libtbag/filesystem/details/FsCommon.hpp>
#include <libtbag/filesystem/File.hpp>
#include <libtbag/filesystem/Path.hpp>
#include <libtbag/time/TimePoint.hpp>

#include <cassert>
#include <string>
#include <memory>

// -------------------
NAMESPACE_LIBTBAG_OPEN
// -------------------

namespace filesystem {

/**
 * Condition check and data writer interface of RotatePath.
 *
 * @author zer0
 * @date   2017-10-21
 */
struct WriterInterface
{
    using Err = libtbag::Err;

    WriterInterface() { /* EMPTY. */ }
    virtual ~WriterInterface() { /* EMPTY. */ }

    virtual bool ready() const = 0;
    virtual bool open(char const * path) = 0;
    virtual void flush() = 0;
    virtual void close() = 0;

    /**
     * Write method.
     *
     * @param[in] buffer
     *      If you want a wirte in file, this parameter is the source buffer.
     * @param[in] size
     *      If you want a wirte in file, this parameter is the size of source buffer.
     * @param[out] written
     *      If the function returns successfully, it returns the size written.
     *
     * @retval E_SUCCESS
     * @retval E_CONTINUE
     *
     * @retval E_EOPEN
     * @retval E_WRERR
     * @retval E_SMALLBUF
     * @retval E_UNKNOWN
     */
    virtual Err write(char const * buffer, int size, int * written) = 0;
};

/**
 * Maximum file size check and writer.
 *
 * @author zer0
 * @date   2017-10-21
 */
struct MaxSizeWriter : public WriterInterface
{
    using File = libtbag::filesystem::File;

    TBAG_CONSTEXPR static std::size_t const DEFAULT_MAX_SIZE = 1 * MEGA_BYTE_TO_BYTE;

    std::size_t max_size;
    File file;

    MaxSizeWriter(std::size_t size = DEFAULT_MAX_SIZE) : max_size(size)
    { /* EMPTY. */ }

    virtual ~MaxSizeWriter()
    {
        if (ready()) {
            flush();
            close();
        }
    }

    virtual bool ready() const override
    {
        return file.isOpen();
    }

    virtual bool open(char const * path) override
    {
        using namespace libtbag::filesystem::details;
        if (file.open(path, File::Flags(FILE_OPEN_FLAG_WRITE_ONLY | FILE_OPEN_CREATE))) {
            file.seek(0);
            return true;
        }
        return false;
    }

    virtual void flush() override
    {
        // EMPTY.
    }

    virtual void close() override
    {
        file.close();
    }

    virtual Err write(char const * buffer, int size, int * written) override
    {
        assert(buffer != nullptr);
        assert(written != nullptr);

        if (!file.isOpen()) {
            return E_OPEN;
        }

        if (file.offset() >= max_size) {
            *written = 0;
            return E_SMALLBUF;
        }

        assert(file.offset() < max_size);
        auto const MAXIMUM_WRITE_SIZE = file.offset() + size;

        if (MAXIMUM_WRITE_SIZE == max_size) {
            *written = file.write(buffer, size);
            if (*written > 0 && *written == size) {
                return E_CONTINUE;
            } else {
                return E_WRERR;
            }

        } else if (MAXIMUM_WRITE_SIZE < max_size) {
            *written = file.write(buffer, size);
            if (*written > 0 && *written == size) {
                return E_SUCCESS;
            } else {
                return E_WRERR;
            }

        } else {
            assert(MAXIMUM_WRITE_SIZE > max_size);
            assert(max_size > file.offset());
            *written = file.write(buffer, max_size - file.offset());
            if (*written > 0) {
                return E_SMALLBUF;
            } else {
                return E_WRERR;
            }
        }

        TBAG_INACCESSIBLE_BLOCK_ASSERT();
        return E_UNKNOWN;
    }
};

/**
 * Rules of redefine the path.
 *
 * @author zer0
 * @date   2017-11-20
 */
struct UpdaterInterface
{
    UpdaterInterface() { /* EMPTY. */ }
    virtual ~UpdaterInterface() { /* EMPTY. */ }

    virtual Path update(Path const & prev) = 0;
};

/**
 * Count based updater.
 *
 * @author zer0
 * @date   2017-11-20
 */
struct CounterUpdater : public UpdaterInterface
{
    TBAG_CONSTEXPR static char const * const DEFAULT_PREFIX = "";
    TBAG_CONSTEXPR static char const * const DEFAULT_SUFFIX = ".log";

    std::string prefix;
    std::string suffix;
    std::size_t count;

    CounterUpdater(std::string const & p = DEFAULT_PREFIX, std::string const & s = DEFAULT_SUFFIX, std::size_t c = 0)
            : prefix(p), suffix(s), count(c)
    { /* EMPTY. */ }
    virtual ~CounterUpdater()
    { /* EMPTY. */ }

    virtual Path update(Path const & prev) override
    {
        Path next;
        do {
            next = (prefix + std::to_string(count) + suffix);
            count++;
        } while (next.exists());
        return next;
    }
};

/**
 * Time based updater.
 *
 * @author zer0
 * @date   2017-11-20
 */
struct TimeFormatUpdater : public UpdaterInterface
{
    TBAG_CONSTEXPR static char const * const DEFAULT_TIME_FORMAT_STRING = "$py$pm$pd_$ph$pi$ps.log";

    std::string format;

    TimeFormatUpdater() : format(DEFAULT_TIME_FORMAT_STRING)
    { /* EMPTY. */ }
    TimeFormatUpdater(std::string const & f) : format(f)
    { /* EMPTY. */ }
    virtual ~TimeFormatUpdater()
    { /* EMPTY. */ }

    virtual Path update(Path const & prev) override
    {
        return Path(libtbag::time::TimePoint::now().fformat(format));
    }

    /**
     * @remarks
     *

     *   /prefix/file.log -> /prefix/file-$py$pm$pdT$ph$pi$ps_$pl$pc.log
     *

     */
    static std::string getDefaultTimeFormatString(Path const & path)
    {
        std::string const PREFIX = path.getNameWithoutExtension();
        std::string const FORMAT = "-$py$pm$pdT$ph$pi$ps_$pl$pc";
        std::string const SUFFIX = path.getExtensionName();
        return (path.getParent() / (PREFIX + FORMAT + SUFFIX)).toString();
    }
};

/**
 * Cleaning an expired path.
 *
 * @author zer0
 * @date   2019-07-08
 */
struct CleanerInterface
{
    CleanerInterface() { /* EMPTY. */ }
    virtual ~CleanerInterface() { /* EMPTY. */ }

    virtual void clean(Path const & path) = 0;
};

/**
 * Archiving an expired path.
 *
 * @author zer0
 * @date   2019-07-08
 */
struct ArchiveCleaner : public CleanerInterface
{
    TBAG_CONSTEXPR static char const * const DEFAULT_ARCHIVE_SUFFIX = ".zip";

    std::string archive_suffix;
    bool remove_source_file;

    ArchiveCleaner() : archive_suffix(DEFAULT_ARCHIVE_SUFFIX), remove_source_file(true)
    { /* EMPTY. */ }
    ArchiveCleaner(std::string const & suffix) : archive_suffix(suffix), remove_source_file(true)
    { /* EMPTY. */ }
    ArchiveCleaner(std::string const & suffix, bool remove) : archive_suffix(suffix), remove_source_file(remove)
    { /* EMPTY. */ }
    virtual ~ArchiveCleaner()
    { /* EMPTY. */ }

    virtual void clean(Path const & path) override
    {
        using namespace libtbag::archive;
        auto const success_count = compressArchive(path.toString() + archive_suffix,
                                                   { path.toString() },
                                                   getCompressFormatFromOutputFileName(archive_suffix));
        assert(success_count == 1);
        if (remove_source_file) {
            path.remove();
        }
    }
};

/**
 * RotatePath class prototype.
 *
 * @author zer0
 * @date   2017-07-31
 *
 * @remarks
 *  - absolute time.
 *  - file size.
 */
struct TBAG_API RotatePath
{
    using Environments = libtbag::string::Environments;

    using SharedWriter = std::shared_ptr<WriterInterface>;
    using SharedUpdater = std::shared_ptr<UpdaterInterface>;
    using SharedCleaner = std::shared_ptr<CleanerInterface>;

    struct InitParams
    {
        SharedWriter writer;
        SharedUpdater updater;
        SharedCleaner cleaner;
    };

    TBAG_CONSTEXPR static char const * const WRITER_KEY_SIZE = "size";
    TBAG_CONSTEXPR static char const * const UPDATER_KEY_COUNTER = "counter";
    TBAG_CONSTEXPR static char const * const UPDATER_KEY_TIME = "time";
    TBAG_CONSTEXPR static char const * const CLEANER_KEY_ARCHIVE = "archive";

    /**
     * @remarks
     *  Examples:
     *  - size and counter: size=1024m counter=/prefix/path/log,.log,0
     *  - size and time and archive: size=1024m time=/prefix/path/file-$py$pm$pdT$ph$pi$ps.log archive=.zip
     */
    static InitParams createParams(std::string const & arguments, Environments const & envs);
    static InitParams createParams(std::string const & arguments);

    Path path;

    SharedWriter writer;
    SharedUpdater updater;
    SharedCleaner cleaner;

    RotatePath();
    RotatePath(std::string const & arguments);

    explicit RotatePath(InitParams const & params);
    explicit RotatePath(Path const & p, InitParams const & params);
    explicit RotatePath(Path const & p);
    explicit RotatePath(Path const & p, SharedWriter const & w, SharedUpdater const & u);
    explicit RotatePath(SharedWriter const & w, SharedUpdater const & u);
    explicit RotatePath(Path const & p, SharedWriter const & w, SharedUpdater const & u, SharedCleaner const & c);
    explicit RotatePath(SharedWriter const & w, SharedUpdater const & u, SharedCleaner const & c);

    ~RotatePath();

    inline bool isReady() const TBAG_NOEXCEPT_SPECIFIER(
            TBAG_NOEXCEPT_OPERATOR((bool)writer) &&
            TBAG_NOEXCEPT_OPERATOR((bool)updater))
    { return writer && updater; }

    void init(std::string const & args, Environments const & envs);
    void init(std::string const & args);

    void update();
    void flush();

    Err write(char const * buffer, std::size_t size);
};

} // namespace filesystem

// --------------------
NAMESPACE_LIBTBAG_CLOSE
// --------------------

#endif // __INCLUDE_LIBTBAG__LIBTBAG_FILESYSTEM_ROTATEPATH_HPP__

<commit_msg>Update MaxSizeWriter::open() method.<commit_after>/**
 * @file   RotatePath.hpp
 * @brief  RotatePath class prototype.
 * @author zer0
 * @date   2017-07-31
 */

#ifndef __INCLUDE_LIBTBAG__LIBTBAG_FILESYSTEM_ROTATEPATH_HPP__
#define __INCLUDE_LIBTBAG__LIBTBAG_FILESYSTEM_ROTATEPATH_HPP__

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

#include <libtbag/config.h>
#include <libtbag/predef.hpp>
#include <libtbag/Err.hpp>
#include <libtbag/Unit.hpp>

#include <libtbag/debug/Assert.hpp>
#include <libtbag/archive/Archive.hpp>
#include <libtbag/string/Environments.hpp>
#include <libtbag/filesystem/details/FsCommon.hpp>
#include <libtbag/filesystem/File.hpp>
#include <libtbag/filesystem/Path.hpp>
#include <libtbag/time/TimePoint.hpp>

#include <cassert>
#include <string>
#include <memory>

// -------------------
NAMESPACE_LIBTBAG_OPEN
// -------------------

namespace filesystem {

/**
 * Condition check and data writer interface of RotatePath.
 *
 * @author zer0
 * @date   2017-10-21
 */
struct WriterInterface
{
    using Err = libtbag::Err;

    WriterInterface() { /* EMPTY. */ }
    virtual ~WriterInterface() { /* EMPTY. */ }

    virtual bool ready() const = 0;
    virtual bool open(char const * path) = 0;
    virtual void flush() = 0;
    virtual void close() = 0;

    /**
     * Write method.
     *
     * @param[in] buffer
     *      If you want a wirte in file, this parameter is the source buffer.
     * @param[in] size
     *      If you want a wirte in file, this parameter is the size of source buffer.
     * @param[out] written
     *      If the function returns successfully, it returns the size written.
     *
     * @retval E_SUCCESS
     * @retval E_CONTINUE
     *
     * @retval E_EOPEN
     * @retval E_WRERR
     * @retval E_SMALLBUF
     * @retval E_UNKNOWN
     */
    virtual Err write(char const * buffer, int size, int * written) = 0;
};

/**
 * Maximum file size check and writer.
 *
 * @author zer0
 * @date   2017-10-21
 */
struct MaxSizeWriter : public WriterInterface
{
    using File = libtbag::filesystem::File;

    TBAG_CONSTEXPR static std::size_t const DEFAULT_MAX_SIZE = 1 * MEGA_BYTE_TO_BYTE;

    std::size_t max_size;
    File file;

    MaxSizeWriter(std::size_t size = DEFAULT_MAX_SIZE) : max_size(size)
    { /* EMPTY. */ }

    virtual ~MaxSizeWriter()
    {
        if (ready()) {
            flush();
            close();
        }
    }

    virtual bool ready() const override
    {
        return file.isOpen();
    }

    virtual bool open(char const * path) override
    {
        auto const parent = Path(path).getParent();
        if (!parent.exists()) {
            parent.createDir();
            if (!parent.isDirectory()) {
                return false;
            }
        }
        using namespace libtbag::filesystem::details;
        if (file.open(path, File::Flags(FILE_OPEN_FLAG_WRITE_ONLY | FILE_OPEN_CREATE))) {
            file.seek(0);
            return true;
        }
        return false;
    }

    virtual void flush() override
    {
        // EMPTY.
    }

    virtual void close() override
    {
        file.close();
    }

    virtual Err write(char const * buffer, int size, int * written) override
    {
        assert(buffer != nullptr);
        assert(written != nullptr);

        if (!file.isOpen()) {
            return E_OPEN;
        }

        if (file.offset() >= max_size) {
            *written = 0;
            return E_SMALLBUF;
        }

        assert(file.offset() < max_size);
        auto const MAXIMUM_WRITE_SIZE = file.offset() + size;

        if (MAXIMUM_WRITE_SIZE == max_size) {
            *written = file.write(buffer, size);
            if (*written > 0 && *written == size) {
                return E_CONTINUE;
            } else {
                return E_WRERR;
            }

        } else if (MAXIMUM_WRITE_SIZE < max_size) {
            *written = file.write(buffer, size);
            if (*written > 0 && *written == size) {
                return E_SUCCESS;
            } else {
                return E_WRERR;
            }

        } else {
            assert(MAXIMUM_WRITE_SIZE > max_size);
            assert(max_size > file.offset());
            *written = file.write(buffer, max_size - file.offset());
            if (*written > 0) {
                return E_SMALLBUF;
            } else {
                return E_WRERR;
            }
        }

        TBAG_INACCESSIBLE_BLOCK_ASSERT();
        return E_UNKNOWN;
    }
};

/**
 * Rules of redefine the path.
 *
 * @author zer0
 * @date   2017-11-20
 */
struct UpdaterInterface
{
    UpdaterInterface() { /* EMPTY. */ }
    virtual ~UpdaterInterface() { /* EMPTY. */ }

    virtual Path update(Path const & prev) = 0;
};

/**
 * Count based updater.
 *
 * @author zer0
 * @date   2017-11-20
 */
struct CounterUpdater : public UpdaterInterface
{
    TBAG_CONSTEXPR static char const * const DEFAULT_PREFIX = "";
    TBAG_CONSTEXPR static char const * const DEFAULT_SUFFIX = ".log";

    std::string prefix;
    std::string suffix;
    std::size_t count;

    CounterUpdater(std::string const & p = DEFAULT_PREFIX, std::string const & s = DEFAULT_SUFFIX, std::size_t c = 0)
            : prefix(p), suffix(s), count(c)
    { /* EMPTY. */ }
    virtual ~CounterUpdater()
    { /* EMPTY. */ }

    virtual Path update(Path const & prev) override
    {
        Path next;
        do {
            next = (prefix + std::to_string(count) + suffix);
            count++;
        } while (next.exists());
        return next;
    }
};

/**
 * Time based updater.
 *
 * @author zer0
 * @date   2017-11-20
 */
struct TimeFormatUpdater : public UpdaterInterface
{
    TBAG_CONSTEXPR static char const * const DEFAULT_TIME_FORMAT_STRING = "$py$pm$pd_$ph$pi$ps.log";

    std::string format;

    TimeFormatUpdater() : format(DEFAULT_TIME_FORMAT_STRING)
    { /* EMPTY. */ }
    TimeFormatUpdater(std::string const & f) : format(f)
    { /* EMPTY. */ }
    virtual ~TimeFormatUpdater()
    { /* EMPTY. */ }

    virtual Path update(Path const & prev) override
    {
        return Path(libtbag::time::TimePoint::now().fformat(format));
    }

    /**
     * @remarks
     *

     *   /prefix/file.log -> /prefix/file-$py$pm$pdT$ph$pi$ps_$pl$pc.log
     *

     */
    static std::string getDefaultTimeFormatString(Path const & path)
    {
        std::string const PREFIX = path.getNameWithoutExtension();
        std::string const FORMAT = "-$py$pm$pdT$ph$pi$ps_$pl$pc";
        std::string const SUFFIX = path.getExtensionName();
        return (path.getParent() / (PREFIX + FORMAT + SUFFIX)).toString();
    }
};

/**
 * Cleaning an expired path.
 *
 * @author zer0
 * @date   2019-07-08
 */
struct CleanerInterface
{
    CleanerInterface() { /* EMPTY. */ }
    virtual ~CleanerInterface() { /* EMPTY. */ }

    virtual void clean(Path const & path) = 0;
};

/**
 * Archiving an expired path.
 *
 * @author zer0
 * @date   2019-07-08
 */
struct ArchiveCleaner : public CleanerInterface
{
    TBAG_CONSTEXPR static char const * const DEFAULT_ARCHIVE_SUFFIX = ".zip";

    std::string archive_suffix;
    bool remove_source_file;

    ArchiveCleaner() : archive_suffix(DEFAULT_ARCHIVE_SUFFIX), remove_source_file(true)
    { /* EMPTY. */ }
    ArchiveCleaner(std::string const & suffix) : archive_suffix(suffix), remove_source_file(true)
    { /* EMPTY. */ }
    ArchiveCleaner(std::string const & suffix, bool remove) : archive_suffix(suffix), remove_source_file(remove)
    { /* EMPTY. */ }
    virtual ~ArchiveCleaner()
    { /* EMPTY. */ }

    virtual void clean(Path const & path) override
    {
        using namespace libtbag::archive;
        auto const success_count = compressArchive(path.toString() + archive_suffix,
                                                   { path.toString() },
                                                   getCompressFormatFromOutputFileName(archive_suffix));
        assert(success_count == 1);
        if (remove_source_file) {
            path.remove();
        }
    }
};

/**
 * RotatePath class prototype.
 *
 * @author zer0
 * @date   2017-07-31
 *
 * @remarks
 *  - absolute time.
 *  - file size.
 */
struct TBAG_API RotatePath
{
    using Environments = libtbag::string::Environments;

    using SharedWriter = std::shared_ptr<WriterInterface>;
    using SharedUpdater = std::shared_ptr<UpdaterInterface>;
    using SharedCleaner = std::shared_ptr<CleanerInterface>;

    struct InitParams
    {
        SharedWriter writer;
        SharedUpdater updater;
        SharedCleaner cleaner;
    };

    TBAG_CONSTEXPR static char const * const WRITER_KEY_SIZE = "size";
    TBAG_CONSTEXPR static char const * const UPDATER_KEY_COUNTER = "counter";
    TBAG_CONSTEXPR static char const * const UPDATER_KEY_TIME = "time";
    TBAG_CONSTEXPR static char const * const CLEANER_KEY_ARCHIVE = "archive";

    /**
     * @remarks
     *  Examples:
     *  - size and counter: size=1024m counter=/prefix/path/log,.log,0
     *  - size and time and archive: size=1024m time=/prefix/path/file-$py$pm$pdT$ph$pi$ps.log archive=.zip
     */
    static InitParams createParams(std::string const & arguments, Environments const & envs);
    static InitParams createParams(std::string const & arguments);

    Path path;

    SharedWriter writer;
    SharedUpdater updater;
    SharedCleaner cleaner;

    RotatePath();
    RotatePath(std::string const & arguments);

    explicit RotatePath(InitParams const & params);
    explicit RotatePath(Path const & p, InitParams const & params);
    explicit RotatePath(Path const & p);
    explicit RotatePath(Path const & p, SharedWriter const & w, SharedUpdater const & u);
    explicit RotatePath(SharedWriter const & w, SharedUpdater const & u);
    explicit RotatePath(Path const & p, SharedWriter const & w, SharedUpdater const & u, SharedCleaner const & c);
    explicit RotatePath(SharedWriter const & w, SharedUpdater const & u, SharedCleaner const & c);

    ~RotatePath();

    inline bool isReady() const TBAG_NOEXCEPT_SPECIFIER(
            TBAG_NOEXCEPT_OPERATOR((bool)writer) &&
            TBAG_NOEXCEPT_OPERATOR((bool)updater))
    { return writer && updater; }

    void init(std::string const & args, Environments const & envs);
    void init(std::string const & args);

    void update();
    void flush();

    Err write(char const * buffer, std::size_t size);
};

} // namespace filesystem

// --------------------
NAMESPACE_LIBTBAG_CLOSE
// --------------------

#endif // __INCLUDE_LIBTBAG__LIBTBAG_FILESYSTEM_ROTATEPATH_HPP__

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
 */

#pragma once

#include <uavcan/node/abstract_node.hpp>
#include <memory>
#include "../transport/can/can.hpp"

struct TestNode : public uavcan::INode
{
    uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 8, uavcan::MemPoolBlockSize> pool;
    uavcan::PoolManager<1> poolmgr;
    uavcan::MarshalBufferProvider<> buffer_provider;
    uavcan::OutgoingTransferRegistry<8> otr;
    uavcan::Scheduler scheduler;

    TestNode(uavcan::ICanDriver& can_driver, uavcan::ISystemClock& clock_driver, uavcan::NodeID self_node_id)
    : otr(poolmgr)
    , scheduler(can_driver, poolmgr, clock_driver, otr)
    {
        poolmgr.addPool(&pool);
        setNodeID(self_node_id);
    }

    void registerInternalFailure(const char* msg)
    {
        std::cout << "TestNode internal failure: " << msg << std::endl;
    }

    uavcan::PoolManager<1>& getAllocator() { return poolmgr; }
    uavcan::Scheduler& getScheduler() { return scheduler; }
    const uavcan::Scheduler& getScheduler() const { return scheduler; }
    uavcan::IMarshalBufferProvider& getMarshalBufferProvider() { return buffer_provider; }
};

struct PairableCanDriver : public uavcan::ICanDriver, public uavcan::ICanIface
{
    uavcan::ISystemClock& clock;
    PairableCanDriver* other;
    std::queue<uavcan::CanFrame> read_queue;

    PairableCanDriver(uavcan::ISystemClock& clock)
    : clock(clock)
    , other(NULL)
    { }

    void linkTogether(PairableCanDriver* with)
    {
        this->other = with;
        with->other = this;
    }

    uavcan::ICanIface* getIface(int iface_index)
    {
        if (iface_index == 0)
            return this;
        return NULL;
    }

    int getNumIfaces() const { return 1; }

    int select(int& inout_write_iface_mask, int& inout_read_iface_mask, uavcan::MonotonicTime blocking_deadline)
    {
        assert(other);
        if (inout_read_iface_mask == 1)
            inout_read_iface_mask = read_queue.size() ? 1 : 0;

        if (inout_read_iface_mask || inout_write_iface_mask)
            return 1;

        while (clock.getMonotonic() < blocking_deadline)
            usleep(1000);

        return 0;
    }

    int send(const uavcan::CanFrame& frame, uavcan::MonotonicTime)
    {
        assert(other);
        other->read_queue.push(frame);
        return 1;
    }

    int receive(uavcan::CanFrame& out_frame, uavcan::MonotonicTime& out_ts_monotonic, uavcan::UtcTime& out_ts_utc)
    {
        assert(other);
        assert(read_queue.size());
        out_frame = read_queue.front();
        read_queue.pop();
        out_ts_monotonic = clock.getMonotonic();
        out_ts_utc = clock.getUtc();
        return 1;
    }

    int configureFilters(const uavcan::CanFilterConfig*, int) { return -1; }
    int getNumFilters() const { return 0; }
    uint64_t getNumErrors() const { return 0; }
};

struct InterlinkedTestNodes
{
    SystemClockDriver clock;
    PairableCanDriver can_a;
    PairableCanDriver can_b;
    TestNode a;
    TestNode b;

    InterlinkedTestNodes(uavcan::NodeID nid_first, uavcan::NodeID nid_second)
    : can_a(clock)
    , can_b(clock)
    , a(can_a, clock, nid_first)
    , b(can_b, clock, nid_second)
    {
        can_a.linkTogether(&can_b);
    }

    InterlinkedTestNodes()
    : can_a(clock)
    , can_b(clock)
    , a(can_a, clock, 1)
    , b(can_b, clock, 2)
    {
        can_a.linkTogether(&can_b);
    }

    int spinBoth(uavcan::MonotonicDuration duration)
    {
        const uavcan::MonotonicDuration duration_per_node = duration * 0.5;
        const int ret = a.spin(duration_per_node);
        if (ret < 0)
            return ret;
        return b.spin(duration_per_node);
    }
};
<commit_msg>Tests: fixed spin() for InterlinkedTestNodes<commit_after>/*
 * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
 */

#pragma once

#include <uavcan/node/abstract_node.hpp>
#include <memory>
#include "../transport/can/can.hpp"

struct TestNode : public uavcan::INode
{
    uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 8, uavcan::MemPoolBlockSize> pool;
    uavcan::PoolManager<1> poolmgr;
    uavcan::MarshalBufferProvider<> buffer_provider;
    uavcan::OutgoingTransferRegistry<8> otr;
    uavcan::Scheduler scheduler;

    TestNode(uavcan::ICanDriver& can_driver, uavcan::ISystemClock& clock_driver, uavcan::NodeID self_node_id)
    : otr(poolmgr)
    , scheduler(can_driver, poolmgr, clock_driver, otr)
    {
        poolmgr.addPool(&pool);
        setNodeID(self_node_id);
    }

    void registerInternalFailure(const char* msg)
    {
        std::cout << "TestNode internal failure: " << msg << std::endl;
    }

    uavcan::PoolManager<1>& getAllocator() { return poolmgr; }
    uavcan::Scheduler& getScheduler() { return scheduler; }
    const uavcan::Scheduler& getScheduler() const { return scheduler; }
    uavcan::IMarshalBufferProvider& getMarshalBufferProvider() { return buffer_provider; }
};

struct PairableCanDriver : public uavcan::ICanDriver, public uavcan::ICanIface
{
    uavcan::ISystemClock& clock;
    PairableCanDriver* other;
    std::queue<uavcan::CanFrame> read_queue;

    PairableCanDriver(uavcan::ISystemClock& clock)
    : clock(clock)
    , other(NULL)
    { }

    void linkTogether(PairableCanDriver* with)
    {
        this->other = with;
        with->other = this;
    }

    uavcan::ICanIface* getIface(int iface_index)
    {
        if (iface_index == 0)
            return this;
        return NULL;
    }

    int getNumIfaces() const { return 1; }

    int select(int& inout_write_iface_mask, int& inout_read_iface_mask, uavcan::MonotonicTime blocking_deadline)
    {
        assert(other);
        if (inout_read_iface_mask == 1)
            inout_read_iface_mask = read_queue.size() ? 1 : 0;

        if (inout_read_iface_mask || inout_write_iface_mask)
            return 1;

        while (clock.getMonotonic() < blocking_deadline)
            usleep(1000);

        return 0;
    }

    int send(const uavcan::CanFrame& frame, uavcan::MonotonicTime)
    {
        assert(other);
        other->read_queue.push(frame);
        return 1;
    }

    int receive(uavcan::CanFrame& out_frame, uavcan::MonotonicTime& out_ts_monotonic, uavcan::UtcTime& out_ts_utc)
    {
        assert(other);
        assert(read_queue.size());
        out_frame = read_queue.front();
        read_queue.pop();
        out_ts_monotonic = clock.getMonotonic();
        out_ts_utc = clock.getUtc();
        return 1;
    }

    int configureFilters(const uavcan::CanFilterConfig*, int) { return -1; }
    int getNumFilters() const { return 0; }
    uint64_t getNumErrors() const { return 0; }
};

struct InterlinkedTestNodes
{
    SystemClockDriver clock;
    PairableCanDriver can_a;
    PairableCanDriver can_b;
    TestNode a;
    TestNode b;

    InterlinkedTestNodes(uavcan::NodeID nid_first, uavcan::NodeID nid_second)
    : can_a(clock)
    , can_b(clock)
    , a(can_a, clock, nid_first)
    , b(can_b, clock, nid_second)
    {
        can_a.linkTogether(&can_b);
    }

    InterlinkedTestNodes()
    : can_a(clock)
    , can_b(clock)
    , a(can_a, clock, 1)
    , b(can_b, clock, 2)
    {
        can_a.linkTogether(&can_b);
    }

    int spinBoth(uavcan::MonotonicDuration duration)
    {
        assert(duration.isPositive());
        unsigned int nspins2 = duration.toMSec() / 2 + 1;
        assert(nspins2 > 0);
        while (nspins2 --> 0)
        {
            int ret = -1;
            ret = a.spin(uavcan::MonotonicDuration::fromMSec(1));
            if (ret < 0)
                return ret;
            ret = b.spin(uavcan::MonotonicDuration::fromMSec(1));
            if (ret < 0)
                return ret;
        }
        return 0;
    }
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include "IndexWriter.h"
#include <fnord-fts/AnalyzerAdapter.h>

using namespace fnord;

namespace cm {

RefPtr<IndexWriter> IndexWriter::openIndex(
    const String& index_path,
    const String& conf_path) {
  if (!FileUtil::exists(index_path) || !FileUtil::isDirectory(index_path)) {
    RAISEF(kIllegalArgumentError, "invalid index path: $0", index_path);
  }

  /* set up feature schema */
  FeatureSchema feature_schema;
  feature_schema.registerFeature("shop_id", 1, 1);
  feature_schema.registerFeature("category1", 2, 1);
  feature_schema.registerFeature("category2", 3, 1);
  feature_schema.registerFeature("category3", 4, 1);
  feature_schema.registerFeature("title~de", 5, 2);

  /* open mdb */
  auto db_path = FileUtil::joinPaths(index_path, "db");
  FileUtil::mkdir_p(db_path);
  auto db = mdb::MDB::open(db_path);
  db->setMaxSize(1000000 * 512000);

  /* open docs store */
  auto docs_path = FileUtil::joinPaths(index_path, "docs");
  FileUtil::mkdir_p(docs_path);
  RefPtr<DocStore> docs(new DocStore(docs_path));

  /* open lucene */
  RefPtr<fnord::fts::Analyzer> analyzer(new fnord::fts::Analyzer(conf_path));
  auto adapter = std::make_shared<fnord::fts::AnalyzerAdapter>(analyzer);

  auto fts_path = FileUtil::joinPaths(index_path, "fts");
  auto fts =
      fts::newLucene<fts::IndexWriter>(
          fts::FSDirectory::open(StringUtil::convertUTF8To16(fts_path)),
          adapter,
          true,
          fts::IndexWriter::MaxFieldLengthLIMITED);

  return RefPtr<IndexWriter>(new IndexWriter(feature_schema, db, docs, fts));
}

IndexWriter::IndexWriter(
    FeatureSchema schema,
    RefPtr<mdb::MDB> db,
    RefPtr<DocStore> docs,
    std::shared_ptr<fts::IndexWriter> fts) :
    schema_(schema),
    db_(db),
    db_txn_(db_->startTransaction()),
    feature_idx_(new FeatureIndexWriter(&schema_)),
    fts_(fts) {}

IndexWriter::~IndexWriter() {
  if (db_txn_.get()) {
    db_txn_->commit();
  }

  fts_->close();
}

void IndexWriter::updateDocument(const IndexRequest& index_request) {
  stat_documents_indexed_total_.incr(1);
  auto docid = index_request.item.docID();
  feature_idx_->updateDocument(index_request, db_txn_.get());
  auto doc = feature_idx_->findDocument(docid, db_txn_.get());
  rebuildFTS(doc);
  stat_documents_indexed_success_.incr(1);
}

void IndexWriter::commit() {
  db_txn_->commit();
  db_txn_ = db_->startTransaction();
  fts_->commit();
}

void IndexWriter::rebuildFTS(DocID docid) {
  auto doc = feature_idx_->findDocument(docid, db_txn_.get());
  rebuildFTS(doc);
}

void IndexWriter::rebuildFTS(RefPtr<Document> doc) {
  stat_documents_indexed_fts_.incr(1);
  auto fts_doc = fts::newLucene<fts::Document>();

  fnord::logDebug(
      "cm.indexwriter",
      "Rebuilding FTS Index for docid=$0",
      doc->docID().docid);

  HashMap<String, String> fts_fields_anal;
  for (const auto& f : doc->fields()) {

    /* title~LANG */
    if (StringUtil::beginsWith(f.first, "title~")) {
      auto k = f.first;
      StringUtil::replaceAll(&k, "title~","title~");
      fts_fields_anal[k] += " ";
      fts_fields_anal[k] += f.second;
    }

    /* description~LANG */
    if (StringUtil::beginsWith(f.first, "description~")) {
      auto k = f.first;
      StringUtil::replaceAll(&k, "description~","text~");
      fts_fields_anal[k] += " ";
      fts_fields_anal[k] += f.second;
    }

    /* tags_as_text~LANG */
    if (StringUtil::beginsWith(f.first, "tags_as_text~")) {
      auto k = f.first;
      StringUtil::replaceAll(&k, "tags_as_text~","text~");
      fts_fields_anal[k] += " ";
      fts_fields_anal[k] += f.second;
    }

  }

  fts_doc->add(
        fts::newLucene<fts::Field>(
            L"docid",
            StringUtil::convertUTF8To16(doc->docID().docid),
            fts::Field::STORE_YES,
            fts::Field::INDEX_NO));

  for (const auto& f : fts_fields_anal) {
    fts_doc->add(
        fts::newLucene<fts::Field>(
            StringUtil::convertUTF8To16(f.first),
            StringUtil::convertUTF8To16(f.second),
            fts::Field::STORE_NO,
            fts::Field::INDEX_ANALYZED));
  }

  fts_->addDocument(fts_doc);
}

void IndexWriter::rebuildFTS() {
  //docs_->listDocuments([this] (const DocID& docid) -> bool {
  //  rebuildFTS(docid);
  //  return true;
  //});
}

RefPtr<mdb::MDBTransaction> IndexWriter::dbTransaction() {
  return db_txn_;
}

void IndexWriter::exportStats(const String& prefix) {
  exportStat(
      StringUtil::format("$0/documents_indexed_total", prefix),
      &stat_documents_indexed_total_,
      fnord::stats::ExportMode::EXPORT_DELTA);

  exportStat(
      StringUtil::format("$0/documents_indexed_success", prefix),
      &stat_documents_indexed_success_,
      fnord::stats::ExportMode::EXPORT_DELTA);

  exportStat(
      StringUtil::format("$0/documents_indexed_error", prefix),
      &stat_documents_indexed_error_,
      fnord::stats::ExportMode::EXPORT_DELTA);

  exportStat(
      StringUtil::format("$0/documents_indexed_fts", prefix),
      &stat_documents_indexed_fts_,
      fnord::stats::ExportMode::EXPORT_DELTA);
}

} // namespace cm
<commit_msg>full index schema<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include "IndexWriter.h"
#include <fnord-fts/AnalyzerAdapter.h>

using namespace fnord;

namespace cm {

RefPtr<IndexWriter> IndexWriter::openIndex(
    const String& index_path,
    const String& conf_path) {
  if (!FileUtil::exists(index_path) || !FileUtil::isDirectory(index_path)) {
    RAISEF(kIllegalArgumentError, "invalid index path: $0", index_path);
  }

  /* set up feature schema */
  FeatureSchema feature_schema;
  feature_schema.registerFeature("shop_id", 1, 1);
  feature_schema.registerFeature("category1", 2, 1);
  feature_schema.registerFeature("category2", 3, 1);
  feature_schema.registerFeature("category3", 4, 1);
  feature_schema.registerFeature("price_cents", 8, 1);

  feature_schema.registerFeature("title~de", 5, 2);
  feature_schema.registerFeature("description~de", 6, 2);
  feature_schema.registerFeature("tags_as_text~de", 7, 2);

  feature_schema.registerFeature("shop_rating_alt", 9, 3);
  feature_schema.registerFeature("shop_products_count", 10, 3);
  feature_schema.registerFeature("shop_orders_count", 11, 3);
  feature_schema.registerFeature("shop_rating_count", 12, 3);
  feature_schema.registerFeature("shop_rating_avg", 13, 3);

  /* open mdb */
  auto db_path = FileUtil::joinPaths(index_path, "db");
  FileUtil::mkdir_p(db_path);
  auto db = mdb::MDB::open(db_path);
  db->setMaxSize(1000000 * 512000);

  /* open docs store */
  auto docs_path = FileUtil::joinPaths(index_path, "docs");
  FileUtil::mkdir_p(docs_path);
  RefPtr<DocStore> docs(new DocStore(docs_path));

  /* open lucene */
  RefPtr<fnord::fts::Analyzer> analyzer(new fnord::fts::Analyzer(conf_path));
  auto adapter = std::make_shared<fnord::fts::AnalyzerAdapter>(analyzer);

  auto fts_path = FileUtil::joinPaths(index_path, "fts");
  auto fts =
      fts::newLucene<fts::IndexWriter>(
          fts::FSDirectory::open(StringUtil::convertUTF8To16(fts_path)),
          adapter,
          true,
          fts::IndexWriter::MaxFieldLengthLIMITED);

  return RefPtr<IndexWriter>(new IndexWriter(feature_schema, db, docs, fts));
}

IndexWriter::IndexWriter(
    FeatureSchema schema,
    RefPtr<mdb::MDB> db,
    RefPtr<DocStore> docs,
    std::shared_ptr<fts::IndexWriter> fts) :
    schema_(schema),
    db_(db),
    db_txn_(db_->startTransaction()),
    feature_idx_(new FeatureIndexWriter(&schema_)),
    fts_(fts) {}

IndexWriter::~IndexWriter() {
  if (db_txn_.get()) {
    db_txn_->commit();
  }

  fts_->close();
}

void IndexWriter::updateDocument(const IndexRequest& index_request) {
  stat_documents_indexed_total_.incr(1);
  auto docid = index_request.item.docID();
  feature_idx_->updateDocument(index_request, db_txn_.get());
  auto doc = feature_idx_->findDocument(docid, db_txn_.get());
  rebuildFTS(doc);
  stat_documents_indexed_success_.incr(1);
}

void IndexWriter::commit() {
  db_txn_->commit();
  db_txn_ = db_->startTransaction();
  fts_->commit();
}

void IndexWriter::rebuildFTS(DocID docid) {
  auto doc = feature_idx_->findDocument(docid, db_txn_.get());
  rebuildFTS(doc);
}

void IndexWriter::rebuildFTS(RefPtr<Document> doc) {
  stat_documents_indexed_fts_.incr(1);
  auto fts_doc = fts::newLucene<fts::Document>();

  fnord::logDebug(
      "cm.indexwriter",
      "Rebuilding FTS Index for docid=$0",
      doc->docID().docid);

  HashMap<String, String> fts_fields_anal;
  for (const auto& f : doc->fields()) {

    /* title~LANG */
    if (StringUtil::beginsWith(f.first, "title~")) {
      auto k = f.first;
      StringUtil::replaceAll(&k, "title~","title~");
      fts_fields_anal[k] += " ";
      fts_fields_anal[k] += f.second;
    }

    /* description~LANG */
    if (StringUtil::beginsWith(f.first, "description~")) {
      auto k = f.first;
      StringUtil::replaceAll(&k, "description~","text~");
      fts_fields_anal[k] += " ";
      fts_fields_anal[k] += f.second;
    }

    /* tags_as_text~LANG */
    if (StringUtil::beginsWith(f.first, "tags_as_text~")) {
      auto k = f.first;
      StringUtil::replaceAll(&k, "tags_as_text~","text~");
      fts_fields_anal[k] += " ";
      fts_fields_anal[k] += f.second;
    }

  }

  fts_doc->add(
        fts::newLucene<fts::Field>(
            L"docid",
            StringUtil::convertUTF8To16(doc->docID().docid),
            fts::Field::STORE_YES,
            fts::Field::INDEX_NO));

  for (const auto& f : fts_fields_anal) {
    fts_doc->add(
        fts::newLucene<fts::Field>(
            StringUtil::convertUTF8To16(f.first),
            StringUtil::convertUTF8To16(f.second),
            fts::Field::STORE_NO,
            fts::Field::INDEX_ANALYZED));
  }

  fts_->addDocument(fts_doc);
}

void IndexWriter::rebuildFTS() {
  //docs_->listDocuments([this] (const DocID& docid) -> bool {
  //  rebuildFTS(docid);
  //  return true;
  //});
}

RefPtr<mdb::MDBTransaction> IndexWriter::dbTransaction() {
  return db_txn_;
}

void IndexWriter::exportStats(const String& prefix) {
  exportStat(
      StringUtil::format("$0/documents_indexed_total", prefix),
      &stat_documents_indexed_total_,
      fnord::stats::ExportMode::EXPORT_DELTA);

  exportStat(
      StringUtil::format("$0/documents_indexed_success", prefix),
      &stat_documents_indexed_success_,
      fnord::stats::ExportMode::EXPORT_DELTA);

  exportStat(
      StringUtil::format("$0/documents_indexed_error", prefix),
      &stat_documents_indexed_error_,
      fnord::stats::ExportMode::EXPORT_DELTA);

  exportStat(
      StringUtil::format("$0/documents_indexed_fts", prefix),
      &stat_documents_indexed_fts_,
      fnord::stats::ExportMode::EXPORT_DELTA);
}

} // namespace cm
<|endoftext|>