<commit_before>#include "Octree.hpp"
#include <Core/AScene.hh>
#include <Components/MeshRenderer.hh>
#include <Core/EntityFilter.hpp>
#include <Components/CameraComponent.hpp>
#include <Utils/Frustum.hpp>
#include <chrono>
#include <Utils/DependenciesInjector.hpp>
#include <Core/RenderThread.hpp>
#include <Utils/ThreadQueueCommands.hpp>

namespace AGE
{
	Octree::Octree()
	{
	}

	Octree::~Octree(void)
	{
		_commandQueue.emplace<TMQ::CloseQueue>();
		_commandQueue.releaseReadability();
	}

	bool Octree::_init()
	{
		_octreeDrawList = AGE::Vector<DrawableCollection>();
		return true;
	}

	bool Octree::_initInNewThread()
	{
		return true;
	}

	bool Octree::_release()
	{
		return true;
	}

	bool Octree::_releaseInNewThread()
	{
		return true;
	}

	const OctreeKey &Octree::addCullableElement()
	{
		OctreeKey res;
		res.type = OctreeKey::Type::Cullable;
		if (!_freeUserObjects.empty())
		{
			res.id = _freeUserObjects.front();
			_freeUserObjects.pop();
		}
		else
		{
			res.id = _userObjectCounter++;
		}

		_commandQueue.emplace<OctreeCommand::CreateDrawable>(res);
		return res;
	}

	void Octree::removeElement(const OctreeKey &key)
	{
		assert(!key.invalid());
		switch (key.type)
		{
		case(OctreeKey::Type::Camera) :
		{
			_freeCameraObjects.push(key.id);
			_commandQueue.emplace<OctreeCommand::DeleteCamera>(key);
		}
									  break;
		case(OctreeKey::Type::Cullable) :
		{
			_freeUserObjects.push(key.id);
			_commandQueue.emplace<OctreeCommand::DeleteDrawable>(key);
		}
										break;
		default:
			break;
		}
	}

	const OctreeKey &Octree::addCameraElement()
	{
		OctreeKey res;
		res.type = OctreeKey::Type::Camera;
		if (!_freeCameraObjects.empty())
		{
			res.id = _freeCameraObjects.front();
			_freeCameraObjects.pop();
		}
		else
		{
			res.id = _cameraCounter++;
		}
		_commandQueue.emplace<OctreeCommand::CreateCamera>(res);

		return res;
	}

	void Octree::setPosition(const glm::vec3 &v, const OctreeKey &id)
	{
		_commandQueue.emplace<OctreeCommand::Position>(id, v);
	}
	void Octree::setOrientation(const glm::quat &v, const OctreeKey &id)
	{
		_commandQueue.emplace<OctreeCommand::Orientation>(id, v);
	}

	void Octree::setScale(const glm::vec3 &v, const OctreeKey &id)
	{
		_commandQueue.emplace<OctreeCommand::Scale>(id, v);
	}

	void Octree::setCameraInfos(const OctreeKey &id
		, const glm::mat4 &projection)
	{
		_commandQueue.emplace<OctreeCommand::CameraInfos>(id, projection);
	}

	void Octree::setPosition(const glm::vec3 &v, const std::array<OctreeKey, MAX_CPT_NUMBER> &ids)
	{
		for (auto &e : ids)
			setPosition(v, e);
	}

	void Octree::setOrientation(const glm::quat &v, const std::array<OctreeKey, MAX_CPT_NUMBER> &ids)
	{
		for (auto &e : ids)
			setOrientation(v, e);
	}

	void Octree::setScale(const glm::vec3 &v, const std::array<OctreeKey, MAX_CPT_NUMBER> &ids)
	{
		for (auto &e : ids)
			setScale(v, e);
	}

	void Octree::updateGeometry(const OctreeKey &key
		, const AGE::Vector<AGE::SubMeshInstance> &meshs
		, const AGE::Vector<AGE::MaterialInstance> &materials)
	{
		assert(!key.invalid() || key.type != OctreeKey::Type::Cullable);
		_commandQueue.emplace<OctreeCommand::Geometry>(key, meshs, materials);
	}

	//-----------------------------------------------------------------

	Octree::DRAWABLE_ID Octree::addDrawableObject(Octree::USER_OBJECT_ID uid)
	{
		DRAWABLE_ID res = DRAWABLE_ID(-1);
		CullableObject *co = nullptr;
		if (!_freeCullableObjects.empty())
		{
			res = _freeCullableObjects.front();
			_freeCullableObjects.pop();
			co = &(_cullableObjects[res]);
		}
		else
		{
			res = _cullableObjects.size();
			_cullableObjects.emplace_back(CullableObject());
			co = &(_cullableObjects.back());
		}
		co->id = res;
		co->active = true;
		return res;
	}

	void Octree::removeDrawableObject(DRAWABLE_ID id)
	{
		_freeCullableObjects.push(id);
		_cullableObjects[id].active = false;
		assert(id != (std::size_t)(-1));
	}

	bool Octree::_update()
	{
		_commandQueue.getDispatcher()
			.handle<OctreeCommand::CameraInfos>([&](const OctreeCommand::CameraInfos& msg)
		{
			CameraObject *co = nullptr;
			co = &_cameraObjects[msg.key.id];
			co->hasMoved = true;
			co->projection = msg.projection;
		})
			.handle<OctreeCommand::CreateCamera>([&](const OctreeCommand::CreateCamera& msg)
		{
			CameraObject *co = nullptr;
			if (msg.key.id >= _cameraObjects.size())
			{
				_cameraObjects.push_back(CameraObject());
				co = &_cameraObjects.back();
			}
			else
			{
				co = &_cameraObjects[msg.key.id];
			}
			co->key.id = msg.key.id;
			co->active = true;
		})
			.handle<OctreeCommand::CreateDrawable>([&](const OctreeCommand::CreateDrawable& msg)
		{
			UserObject *uo = nullptr;
			if (msg.key.id >= _userObjects.size())
			{
				_userObjects.push_back(UserObject());
				uo = &_userObjects.back();
			}
			else
			{
				uo = &_userObjects[msg.key.id];
			}
		})
			.handle<OctreeCommand::DeleteCamera>([&](const OctreeCommand::DeleteCamera& msg)
		{
			CameraObject *co = nullptr;
			co = &_cameraObjects[msg.key.id];
			co->active = false;
		})
			.handle<OctreeCommand::DeleteDrawable>([&](const OctreeCommand::DeleteDrawable& msg)
		{
			UserObject *uo = nullptr;
			uo = &this->_userObjects[msg.key.id];
			for (auto &e : uo->drawableCollection)
			{
				removeDrawableObject(e);
			}
			uo->drawableCollection.clear();
			uo->active = false;
		})
			.handle<OctreeCommand::Geometry>([this](const OctreeCommand::Geometry& msg)
		{
			UserObject *uo = nullptr;
			uo = &_userObjects[msg.key.id];
			//assert(uo->active == true);
			for (auto &e : uo->drawableCollection)
			{
				removeDrawableObject(e);
			}
			uo->drawableCollection.clear();
			for (std::size_t i = 0; i < msg.submeshInstances.size(); ++i)
			{
				auto id = addDrawableObject(msg.key.id);
				uo->drawableCollection.push_back(id);
				_cullableObjects[id].mesh = msg.submeshInstances[i];
				_cullableObjects[id].material = msg.materialInstances[i];
				_cullableObjects[id].position = uo->position;
				_cullableObjects[id].orientation = uo->orientation;
				_cullableObjects[id].scale = uo->scale;
			}
		})
			.handle<OctreeCommand::Position>([&](const OctreeCommand::Position& msg)
		{
			UserObject *uo = nullptr;
			CameraObject *co = nullptr;
			switch (msg.key.type)
			{
			case(OctreeKey::Type::Camera) :
				co = &_cameraObjects[msg.key.id];
				co->position = msg.position;
				co->hasMoved = true;
				break;
			case(OctreeKey::Type::Cullable) :
				uo = &_userObjects[msg.key.id];
				uo->position = msg.position;
				for (auto &e : uo->drawableCollection)
				{
					_cullableObjects[e].position = uo->position;
					_cullableObjects[e].hasMoved = true;
				}
				break;
			default:
				break;
			}
		})
			.handle<OctreeCommand::Scale>([&](const OctreeCommand::Scale& msg)
		{
			UserObject *uo = nullptr;
			CameraObject *co = nullptr;
			switch (msg.key.type)
			{
			case(OctreeKey::Type::Camera) :
				co = &_cameraObjects[msg.key.id];
				co->scale = msg.scale;
				co->hasMoved = true;
				break;
			case(OctreeKey::Type::Cullable) :
				uo = &_userObjects[msg.key.id];
				uo->scale = msg.scale;
				for (auto &e : uo->drawableCollection)
				{
					_cullableObjects[e].scale = uo->scale;
					_cullableObjects[e].hasMoved = true;
				}
				break;
			default:
				break;
			}
		})
			.handle<OctreeCommand::Orientation>([&](const OctreeCommand::Orientation& msg)
		{
			UserObject *uo = nullptr;
			CameraObject *co = nullptr;
			switch (msg.key.type)
			{
			case(OctreeKey::Type::Camera) :
				co = &_cameraObjects[msg.key.id];
				co->orientation = msg.orientation;
				co->hasMoved = true;
				break;
			case(OctreeKey::Type::Cullable) :
				uo = &_userObjects[msg.key.id];
				uo->orientation = msg.orientation;
				for (auto &e : uo->drawableCollection)
				{
					_cullableObjects[e].orientation = uo->orientation;
					_cullableObjects[e].hasMoved = true;
				}
				break;
			default:
				break;
			}
		})
			.handle<TMQ::CloseQueue>([&](const TMQ::CloseQueue& msg)
		{
			_isRunning = false;
			return false;
		}).handle<OctreeCommand::PrepareDrawLists>([&](OctreeCommand::PrepareDrawLists& msg)
		{
			static std::size_t cameraCounter = 0; cameraCounter = 0;

			for (auto &camera : _cameraObjects)
			{
				if (!camera.active)
					continue;
				Frustum frustum;
				auto transformation = glm::scale(glm::translate(glm::mat4(1), camera.position) * glm::toMat4(camera.orientation), camera.scale);
				frustum.setMatrix(camera.projection * transformation, true);

				_octreeDrawList.emplace_back();
				auto &drawList = _octreeDrawList.back();

				drawList.drawables.clear();

				drawList.transformation = transformation;
				drawList.projection = camera.projection;

				std::size_t drawed = 0; std::size_t total = 0;

				for (auto &e : _cullableObjects)
				{
					if (e.active)
						++total;
					else
						continue;
					if (frustum.pointIn(e.position) == true)
					{
						if (e.hasMoved)
						{
							e.transformation = glm::scale(glm::translate(glm::mat4(1), e.position) * glm::toMat4(e.orientation), e.scale);
							e.hasMoved = false;
						}
						drawList.drawables.emplace_back(e.mesh, e.material, e.transformation);
						++drawed;
					}
				}
				++cameraCounter;
			}

			auto renderThread = getDependencyManager().lock()->getInstance<AGE::RenderThread>();
			for (auto &e : this->_octreeDrawList)
			{
				renderThread->getCommandQueue().safeEmplace<TQC::VoidFunction>([=](){
					msg.function(e);
				});
			}
			_octreeDrawList.clear();
			renderThread->getCommandQueue().safeEmplace<RendCtxCommand::Flush>();
			renderThread->getCommandQueue().releaseReadability();

			//msg.result.set_value(std::move(_octreeDrawList));
			//_octreeDrawList.clear();
		});

		return true;
	}

}<commit_msg>warning : i just correct tow little warning by casting a value 64bit into 32 bit maybe it's not what you would like, can you confirm ?<commit_after>#include "Octree.hpp"
#include <Core/AScene.hh>
#include <Components/MeshRenderer.hh>
#include <Core/EntityFilter.hpp>
#include <Components/CameraComponent.hpp>
#include <Utils/Frustum.hpp>
#include <chrono>
#include <Utils/DependenciesInjector.hpp>
#include <Core/RenderThread.hpp>
#include <Utils/ThreadQueueCommands.hpp>

namespace AGE
{
	Octree::Octree()
	{
	}

	Octree::~Octree(void)
	{
		_commandQueue.emplace<TMQ::CloseQueue>();
		_commandQueue.releaseReadability();
	}

	bool Octree::_init()
	{
		_octreeDrawList = AGE::Vector<DrawableCollection>();
		return true;
	}

	bool Octree::_initInNewThread()
	{
		return true;
	}

	bool Octree::_release()
	{
		return true;
	}

	bool Octree::_releaseInNewThread()
	{
		return true;
	}

	const OctreeKey &Octree::addCullableElement()
	{
		OctreeKey res;
		res.type = OctreeKey::Type::Cullable;
		if (!_freeUserObjects.empty())
		{
			res.id = _freeUserObjects.front();
			_freeUserObjects.pop();
		}
		else
		{
			res.id = uint32_t(_userObjectCounter++);
		}

		_commandQueue.emplace<OctreeCommand::CreateDrawable>(res);
		return res;
	}

	void Octree::removeElement(const OctreeKey &key)
	{
		assert(!key.invalid());
		switch (key.type)
		{
		case(OctreeKey::Type::Camera) :
		{
			_freeCameraObjects.push(key.id);
			_commandQueue.emplace<OctreeCommand::DeleteCamera>(key);
		}
									  break;
		case(OctreeKey::Type::Cullable) :
		{
			_freeUserObjects.push(key.id);
			_commandQueue.emplace<OctreeCommand::DeleteDrawable>(key);
		}
										break;
		default:
			break;
		}
	}

	const OctreeKey &Octree::addCameraElement()
	{
		OctreeKey res;
		res.type = OctreeKey::Type::Camera;
		if (!_freeCameraObjects.empty())
		{
			res.id = _freeCameraObjects.front();
			_freeCameraObjects.pop();
		}
		else
		{
			res.id = _cameraCounter++;
		}
		_commandQueue.emplace<OctreeCommand::CreateCamera>(res);

		return res;
	}

	void Octree::setPosition(const glm::vec3 &v, const OctreeKey &id)
	{
		_commandQueue.emplace<OctreeCommand::Position>(id, v);
	}
	void Octree::setOrientation(const glm::quat &v, const OctreeKey &id)
	{
		_commandQueue.emplace<OctreeCommand::Orientation>(id, v);
	}

	void Octree::setScale(const glm::vec3 &v, const OctreeKey &id)
	{
		_commandQueue.emplace<OctreeCommand::Scale>(id, v);
	}

	void Octree::setCameraInfos(const OctreeKey &id
		, const glm::mat4 &projection)
	{
		_commandQueue.emplace<OctreeCommand::CameraInfos>(id, projection);
	}

	void Octree::setPosition(const glm::vec3 &v, const std::array<OctreeKey, MAX_CPT_NUMBER> &ids)
	{
		for (auto &e : ids)
			setPosition(v, e);
	}

	void Octree::setOrientation(const glm::quat &v, const std::array<OctreeKey, MAX_CPT_NUMBER> &ids)
	{
		for (auto &e : ids)
			setOrientation(v, e);
	}

	void Octree::setScale(const glm::vec3 &v, const std::array<OctreeKey, MAX_CPT_NUMBER> &ids)
	{
		for (auto &e : ids)
			setScale(v, e);
	}

	void Octree::updateGeometry(const OctreeKey &key
		, const AGE::Vector<AGE::SubMeshInstance> &meshs
		, const AGE::Vector<AGE::MaterialInstance> &materials)
	{
		assert(!key.invalid() || key.type != OctreeKey::Type::Cullable);
		_commandQueue.emplace<OctreeCommand::Geometry>(key, meshs, materials);
	}

	//-----------------------------------------------------------------

	Octree::DRAWABLE_ID Octree::addDrawableObject(Octree::USER_OBJECT_ID uid)
	{
		DRAWABLE_ID res = DRAWABLE_ID(-1);
		CullableObject *co = nullptr;
		if (!_freeCullableObjects.empty())
		{
			res = _freeCullableObjects.front();
			_freeCullableObjects.pop();
			co = &(_cullableObjects[res]);
		}
		else
		{
			res = _cullableObjects.size();
			_cullableObjects.emplace_back(CullableObject());
			co = &(_cullableObjects.back());
		}
		co->id = res;
		co->active = true;
		return res;
	}

	void Octree::removeDrawableObject(DRAWABLE_ID id)
	{
		_freeCullableObjects.push(uint32_t(id));
		_cullableObjects[id].active = false;
		assert(id != (std::size_t)(-1));
	}

	bool Octree::_update()
	{
		_commandQueue.getDispatcher()
			.handle<OctreeCommand::CameraInfos>([&](const OctreeCommand::CameraInfos& msg)
		{
			CameraObject *co = nullptr;
			co = &_cameraObjects[msg.key.id];
			co->hasMoved = true;
			co->projection = msg.projection;
		})
			.handle<OctreeCommand::CreateCamera>([&](const OctreeCommand::CreateCamera& msg)
		{
			CameraObject *co = nullptr;
			if (msg.key.id >= _cameraObjects.size())
			{
				_cameraObjects.push_back(CameraObject());
				co = &_cameraObjects.back();
			}
			else
			{
				co = &_cameraObjects[msg.key.id];
			}
			co->key.id = msg.key.id;
			co->active = true;
		})
			.handle<OctreeCommand::CreateDrawable>([&](const OctreeCommand::CreateDrawable& msg)
		{
			UserObject *uo = nullptr;
			if (msg.key.id >= _userObjects.size())
			{
				_userObjects.push_back(UserObject());
				uo = &_userObjects.back();
			}
			else
			{
				uo = &_userObjects[msg.key.id];
			}
		})
			.handle<OctreeCommand::DeleteCamera>([&](const OctreeCommand::DeleteCamera& msg)
		{
			CameraObject *co = nullptr;
			co = &_cameraObjects[msg.key.id];
			co->active = false;
		})
			.handle<OctreeCommand::DeleteDrawable>([&](const OctreeCommand::DeleteDrawable& msg)
		{
			UserObject *uo = nullptr;
			uo = &this->_userObjects[msg.key.id];
			for (auto &e : uo->drawableCollection)
			{
				removeDrawableObject(e);
			}
			uo->drawableCollection.clear();
			uo->active = false;
		})
			.handle<OctreeCommand::Geometry>([this](const OctreeCommand::Geometry& msg)
		{
			UserObject *uo = nullptr;
			uo = &_userObjects[msg.key.id];
			//assert(uo->active == true);
			for (auto &e : uo->drawableCollection)
			{
				removeDrawableObject(e);
			}
			uo->drawableCollection.clear();
			for (std::size_t i = 0; i < msg.submeshInstances.size(); ++i)
			{
				auto id = addDrawableObject(msg.key.id);
				uo->drawableCollection.push_back(id);
				_cullableObjects[id].mesh = msg.submeshInstances[i];
				_cullableObjects[id].material = msg.materialInstances[i];
				_cullableObjects[id].position = uo->position;
				_cullableObjects[id].orientation = uo->orientation;
				_cullableObjects[id].scale = uo->scale;
			}
		})
			.handle<OctreeCommand::Position>([&](const OctreeCommand::Position& msg)
		{
			UserObject *uo = nullptr;
			CameraObject *co = nullptr;
			switch (msg.key.type)
			{
			case(OctreeKey::Type::Camera) :
				co = &_cameraObjects[msg.key.id];
				co->position = msg.position;
				co->hasMoved = true;
				break;
			case(OctreeKey::Type::Cullable) :
				uo = &_userObjects[msg.key.id];
				uo->position = msg.position;
				for (auto &e : uo->drawableCollection)
				{
					_cullableObjects[e].position = uo->position;
					_cullableObjects[e].hasMoved = true;
				}
				break;
			default:
				break;
			}
		})
			.handle<OctreeCommand::Scale>([&](const OctreeCommand::Scale& msg)
		{
			UserObject *uo = nullptr;
			CameraObject *co = nullptr;
			switch (msg.key.type)
			{
			case(OctreeKey::Type::Camera) :
				co = &_cameraObjects[msg.key.id];
				co->scale = msg.scale;
				co->hasMoved = true;
				break;
			case(OctreeKey::Type::Cullable) :
				uo = &_userObjects[msg.key.id];
				uo->scale = msg.scale;
				for (auto &e : uo->drawableCollection)
				{
					_cullableObjects[e].scale = uo->scale;
					_cullableObjects[e].hasMoved = true;
				}
				break;
			default:
				break;
			}
		})
			.handle<OctreeCommand::Orientation>([&](const OctreeCommand::Orientation& msg)
		{
			UserObject *uo = nullptr;
			CameraObject *co = nullptr;
			switch (msg.key.type)
			{
			case(OctreeKey::Type::Camera) :
				co = &_cameraObjects[msg.key.id];
				co->orientation = msg.orientation;
				co->hasMoved = true;
				break;
			case(OctreeKey::Type::Cullable) :
				uo = &_userObjects[msg.key.id];
				uo->orientation = msg.orientation;
				for (auto &e : uo->drawableCollection)
				{
					_cullableObjects[e].orientation = uo->orientation;
					_cullableObjects[e].hasMoved = true;
				}
				break;
			default:
				break;
			}
		})
			.handle<TMQ::CloseQueue>([&](const TMQ::CloseQueue& msg)
		{
			_isRunning = false;
			return false;
		}).handle<OctreeCommand::PrepareDrawLists>([&](OctreeCommand::PrepareDrawLists& msg)
		{
			static std::size_t cameraCounter = 0; cameraCounter = 0;

			for (auto &camera : _cameraObjects)
			{
				if (!camera.active)
					continue;
				Frustum frustum;
				auto transformation = glm::scale(glm::translate(glm::mat4(1), camera.position) * glm::toMat4(camera.orientation), camera.scale);
				frustum.setMatrix(camera.projection * transformation, true);

				_octreeDrawList.emplace_back();
				auto &drawList = _octreeDrawList.back();

				drawList.drawables.clear();

				drawList.transformation = transformation;
				drawList.projection = camera.projection;

				std::size_t drawed = 0; std::size_t total = 0;

				for (auto &e : _cullableObjects)
				{
					if (e.active)
						++total;
					else
						continue;
					if (frustum.pointIn(e.position) == true)
					{
						if (e.hasMoved)
						{
							e.transformation = glm::scale(glm::translate(glm::mat4(1), e.position) * glm::toMat4(e.orientation), e.scale);
							e.hasMoved = false;
						}
						drawList.drawables.emplace_back(e.mesh, e.material, e.transformation);
						++drawed;
					}
				}
				++cameraCounter;
			}

			auto renderThread = getDependencyManager().lock()->getInstance<AGE::RenderThread>();
			for (auto &e : this->_octreeDrawList)
			{
				renderThread->getCommandQueue().safeEmplace<TQC::VoidFunction>([=](){
					msg.function(e);
				});
			}
			_octreeDrawList.clear();
			renderThread->getCommandQueue().safeEmplace<RendCtxCommand::Flush>();
			renderThread->getCommandQueue().releaseReadability();

			//msg.result.set_value(std::move(_octreeDrawList));
			//_octreeDrawList.clear();
		});

		return true;
	}

}<|endoftext|>


<commit_before>// coding: utf-8
// ----------------------------------------------------------------------------
/* Copyright (c) 2009, Roboterclub Aachen e.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Roboterclub Aachen e.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ROBOTERCLUB AACHEN E.V. ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL ROBOTERCLUB AACHEN E.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
// ----------------------------------------------------------------------------

#ifndef	XPCC__ANGLE_HPP
#define	XPCC__ANGLE_HPP

#include <cmath>

// The circumference of a circle with diameter 1
#ifndef M_PI
#define M_PI  3.14159265358979323846
#endif

#ifndef M_PI_2
#define M_PI_2 (M_PI/2.0)
#endif

// The square root of 2.
#ifndef M_SQRT2
#define M_SQRT2  1.41421356237309504880
#endif

namespace xpcc
{
	/**
	 * \brief	Collection of functions for handling of angles
	 *
	 * Angles are always represented by float values in the range
	 * from -Pi to Pi.
	 *
	 * \ingroup	geometry
	 */
	class Angle
	{
	public:
		typedef float Type;

		/**
		 * \brief	Normalize angle
		 *
		 * Normalize the given angle to [-Pi,Pi] by repeatedly
		 * adding/subtracting 2*Pi.
		 */
		static float
		normalize(float angle);

		/**
		 * \brief	Reverse the angle
		 *
		 * Reverse the angle and keep it normalized to [-Pi,Pi].
		 *
		 * Equivalent to:
		 * \code
		 * float angle = xpcc::Angle::normalize(angle + M_PI);
		 * \endcode
		 */
		static float
		reverse(float angle);

		/**
		 * \brief	Find a perpendicular angle
		 *
		 * \param	angle
		 * \param	cw	If cw is true the angle is rotated clockwise.
		 * 				Ohterwise the angle is rotated anti clockwise.
		 */

		static float
		perpendicular(float angle, const bool cw);

		static inline float
		toRadian(float angle)
		{
			return (angle * M_PI) / 180.0;
		}

		static inline float
		toDegree(float angle)
		{
			return (angle * 180.0) / M_PI;
		}
	};
}

#endif	// XPCC__ANGLE_HPP
<commit_msg>Add some constants derived from Pi<commit_after>// coding: utf-8
// ----------------------------------------------------------------------------
/* Copyright (c) 2009, Roboterclub Aachen e.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Roboterclub Aachen e.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ROBOTERCLUB AACHEN E.V. ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL ROBOTERCLUB AACHEN E.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
// ----------------------------------------------------------------------------

#ifndef	XPCC__ANGLE_HPP
#define	XPCC__ANGLE_HPP

#include <cmath>
#include <math.h>

// The circumference of a circle with diameter 1
#ifndef M_PI
#define M_PI  3.14159265358979323846
#endif

#ifndef M_PI_2
#define M_PI_2 1.57079632679489661923
#endif

#ifndef M_PI_4
#define M_PI_4 0.78539816339744830962
#endif

#ifndef M_TWOPI
#define M_TWOPI (M_PI * 2.0)
#endif

#ifndef M_1_PI
#define M_1_PI 0.31830988618379067154
#endif

#ifdef M_2_PI
#define M_2_PI 0.63661977236758134308
#endif

// The square root of 2.
#ifndef M_SQRT2
#define M_SQRT2  1.41421356237309504880
#endif

namespace xpcc
{
	/**
	 * \brief	Collection of functions for handling of angles
	 *
	 * Angles are always represented by float values in the range
	 * from -Pi to Pi.
	 *
	 * \ingroup	geometry
	 */
	class Angle
	{
	public:
		typedef float Type;

		/**
		 * \brief	Normalize angle
		 *
		 * Normalize the given angle to [-Pi,Pi] by repeatedly
		 * adding/subtracting 2*Pi.
		 */
		static float
		normalize(float angle);

		/**
		 * \brief	Reverse the angle
		 *
		 * Reverse the angle and keep it normalized to [-Pi,Pi].
		 *
		 * Equivalent to:
		 * \code
		 * float angle = xpcc::Angle::normalize(angle + M_PI);
		 * \endcode
		 */
		static float
		reverse(float angle);

		/**
		 * \brief	Find a perpendicular angle
		 *
		 * \param	angle
		 * \param	cw	If cw is true the angle is rotated clockwise.
		 * 				Ohterwise the angle is rotated anti clockwise.
		 */

		static float
		perpendicular(float angle, const bool cw);

		static inline float
		toRadian(float angle)
		{
			return (angle * M_PI) / 180.0;
		}

		static inline float
		toDegree(float angle)
		{
			return (angle * 180.0) / M_PI;
		}
	};
}

#endif	// XPCC__ANGLE_HPP
<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// cache_test.cpp
//
// Identification: tests/common/cache_test.cpp
//
// Copyright (c) 201CACHE_SIZE, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "gtest/gtest.h"
#include "backend/common/cache.h"
#include "backend/common/logger.h"
#include "backend/planner/mock_plan.h"

#include <unordered_set>

namespace peloton {
namespace test {

//===--------------------------------------------------------------------===//
// Cache Test
//===--------------------------------------------------------------------===//

#define CACHE_SIZE 5

/**
 * Test basic functionality
 *
 */
TEST(CacheTest, Basic) {

  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  EXPECT_EQ(0, cache.size());
}

/**
 *  Test find operation
 *
 */
TEST(CacheTest, Find) {
  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  EXPECT_EQ(cache.end(), cache.find(1));
}

/**
 * Test insert operation
 *
 */
TEST(CacheTest, Insert) {
  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  MockPlan plans[CACHE_SIZE];

  cache.insert(std::make_pair(0, plans));

  auto cache_it = cache.find(0);

  EXPECT_EQ(*cache_it, plans);

  for (int i = 1; i < CACHE_SIZE; i++)
    cache.insert(std::make_pair(i, plans + i));

  EXPECT_EQ(CACHE_SIZE, cache.size());
}

/**
 * Test iterator function
 */
TEST(CacheTest, Iterator) {
  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  MockPlan plans[CACHE_SIZE];
  std::unordered_set<planner::AbstractPlan *> set;

  for (int i = 0; i < CACHE_SIZE; i++)
    cache.insert(std::make_pair(i, plans + i));

  set.insert(cache.begin(), cache.end());

  EXPECT_EQ(CACHE_SIZE, set.size());
  for (int i = 0; i < CACHE_SIZE; i++) {
    EXPECT_NE(set.end(), set.find(plans + i));
  }
}

/**
 * Test eviction
 *
 * Try to insert 2 times of the capacity of the cache
 * The cache should keep the most recent half
 */
TEST(CacheTest, EvictionByInsert) {
  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  MockPlan plans[CACHE_SIZE * 2];
  std::unordered_set<planner::AbstractPlan *> set;

  for (int i = 0; i < 2 * CACHE_SIZE; i++)
    cache.insert(std::make_pair(i, plans + i));

  set.insert(cache.begin(), cache.end());

  EXPECT_EQ(CACHE_SIZE, set.size());
  for (int i = 0; i < CACHE_SIZE; i++) {
    EXPECT_EQ(set.end(), set.find(plans + i));
  }

  for (int i = CACHE_SIZE; i < 2 * CACHE_SIZE; i++) {
    EXPECT_NE(set.end(), set.find(plans + i));
  }
}

/**
 * Test eviction
 *
 * Try to insert 2 times of the capacity of the cache
 * The cache should keep the most recent half
 */
TEST(CacheTest, EvictionWithAccessing) {
  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  MockPlan plans[CACHE_SIZE * 2];
  std::unordered_set<planner::AbstractPlan *> set;

  int i = 0;
  /* insert 0,1,2,3,4,5,6,7
   *
   * The cache should keep 3,4,5,6,7
   * */
  for (; i < CACHE_SIZE * 1.5; i++)
    cache.insert(std::make_pair(i, plans + i));

  set.insert(cache.begin(), cache.end());

  EXPECT_EQ(CACHE_SIZE, set.size());

  int diff = CACHE_SIZE / 2;

  /* read 4, 3
   * */
  for (int idx = CACHE_SIZE - 1; idx > CACHE_SIZE - diff - 1; idx--) {
    auto it = cache.find(idx);
    EXPECT_NE(it, cache.end());
    EXPECT_EQ(*it, plans + idx);
  }

  /* Insert 8, 9 */
  for (; i < CACHE_SIZE * 2; i++)
    cache.insert(std::make_pair(i, plans + i));

  set.clear();
  set.insert(cache.begin(), cache.end());

  for (auto it : set) {
    LOG_INFO("%lu", it - reinterpret_cast<planner::AbstractPlan*>(&plans[0]));
  }

  EXPECT_EQ(CACHE_SIZE, set.size());

  i = 0;
  for (; i < CACHE_SIZE - diff; i++) {
    EXPECT_EQ(set.end(), set.find(plans + i));
  }

  for (; i < CACHE_SIZE; i++) {
    EXPECT_NE(set.end(), set.find(plans + i));
  }

  for (; i < CACHE_SIZE + diff; i++) {
    EXPECT_EQ(set.end(), set.find(plans + i));
  }

  for (; i < CACHE_SIZE * 2; i++) {
    EXPECT_NE(set.end(), set.find(plans + i));
  }

  i = 0;
  for (; i < CACHE_SIZE; i++) {
    cache.insert(std::make_pair(i, plans + i));
  }

  set.clear();
  set.insert(cache.begin(), cache.end());

  EXPECT_EQ(CACHE_SIZE, cache.size());

  i = 0;
  for (; i < CACHE_SIZE; i++) {
    EXPECT_NE(set.end(), set.find(plans + i));
  }
}

/**
 * Test eviction
 *
 * Try to insert 2 times of the capacity of the cache
 * The cache should keep the most recent half
 */
TEST(CacheTest, Updating) {
  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  MockPlan plans[CACHE_SIZE * 2];

  std::unordered_set<planner::AbstractPlan *> set;

  /* insert 0,1,2,3,4,5,6,7
   *
   * The cache should keep 3,4,5,6,7
   * */
  int i = 0;
  for (; i < CACHE_SIZE * 1.5; i++)
    cache.insert(std::make_pair(i, plans + i));

  set.insert(cache.begin(), cache.end());

  EXPECT_EQ(CACHE_SIZE, set.size());

  int diff = CACHE_SIZE / 2;

  MockPlan plans2[diff];

  /* update 4, 3
   * */
  for (int idx = CACHE_SIZE - 1, j = 0; idx > CACHE_SIZE - diff - 1; idx--, j++) {
    cache.insert(std::make_pair(idx, plans2 + j));
  }

  for (; i < CACHE_SIZE * 2; i++)
    cache.insert(std::make_pair(i, plans + i));

  set.clear();
  set.insert(cache.begin(), cache.end());

  for (auto it : set) {
    LOG_INFO("%lu", it - reinterpret_cast<planner::AbstractPlan*>(&plans[0]));
  }

  EXPECT_EQ(CACHE_SIZE, set.size());

  i = 0;
  for (; i < CACHE_SIZE - diff; i++) {
    EXPECT_EQ(set.end(), set.find(plans + i));
  }

  for (int j = 0; i < CACHE_SIZE; i++, j++) {
    EXPECT_EQ(set.end(), set.find(plans + i));
    EXPECT_NE(set.end(), set.find(plans2 + j));
  }

  for (; i < CACHE_SIZE + diff; i++) {
    EXPECT_EQ(set.end(), set.find(plans + i));
  }

  for (; i < CACHE_SIZE * 2; i++) {
    EXPECT_NE(set.end(), set.find(plans + i));
  }
}

}  // End test namespace
}  // End peloton namespace
<commit_msg>add test for empty<commit_after>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// cache_test.cpp
//
// Identification: tests/common/cache_test.cpp
//
// Copyright (c) 201CACHE_SIZE, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "gtest/gtest.h"
#include "backend/common/cache.h"
#include "backend/common/logger.h"
#include "backend/planner/mock_plan.h"

#include <unordered_set>

namespace peloton {
namespace test {

//===--------------------------------------------------------------------===//
// Cache Test
//===--------------------------------------------------------------------===//

#define CACHE_SIZE 5

/**
 * Test basic functionality
 *
 */
TEST(CacheTest, Basic) {

  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  EXPECT_EQ(0, cache.size());
  EXPECT_EQ(true, cache.empty());
}

/**
 *  Test find operation
 *
 */
TEST(CacheTest, Find) {
  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  EXPECT_EQ(cache.end(), cache.find(1));
}

/**
 * Test insert operation
 *
 */
TEST(CacheTest, Insert) {
  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  MockPlan plans[CACHE_SIZE];

  cache.insert(std::make_pair(0, plans));

  auto cache_it = cache.find(0);

  EXPECT_EQ(*cache_it, plans);

  for (int i = 1; i < CACHE_SIZE; i++)
    cache.insert(std::make_pair(i, plans + i));

  EXPECT_EQ(CACHE_SIZE, cache.size());
  EXPECT_EQ(false, cache.empty());
}

/**
 * Test iterator function
 */
TEST(CacheTest, Iterator) {
  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  MockPlan plans[CACHE_SIZE];
  std::unordered_set<planner::AbstractPlan *> set;

  for (int i = 0; i < CACHE_SIZE; i++)
    cache.insert(std::make_pair(i, plans + i));

  set.insert(cache.begin(), cache.end());

  EXPECT_EQ(CACHE_SIZE, set.size());
  EXPECT_EQ(false, cache.empty());
  for (int i = 0; i < CACHE_SIZE; i++) {
    EXPECT_NE(set.end(), set.find(plans + i));
  }
}

/**
 * Test eviction
 *
 * Try to insert 2 times of the capacity of the cache
 * The cache should keep the most recent half
 */
TEST(CacheTest, EvictionByInsert) {
  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  MockPlan plans[CACHE_SIZE * 2];
  std::unordered_set<planner::AbstractPlan *> set;

  for (int i = 0; i < 2 * CACHE_SIZE; i++)
    cache.insert(std::make_pair(i, plans + i));

  set.insert(cache.begin(), cache.end());

  EXPECT_EQ(CACHE_SIZE, set.size());
  EXPECT_EQ(false, cache.empty());
  for (int i = 0; i < CACHE_SIZE; i++) {
    EXPECT_EQ(set.end(), set.find(plans + i));
  }

  for (int i = CACHE_SIZE; i < 2 * CACHE_SIZE; i++) {
    EXPECT_NE(set.end(), set.find(plans + i));
  }
}

/**
 * Test eviction
 *
 * Try to insert 2 times of the capacity of the cache
 * The cache should keep the most recent half
 */
TEST(CacheTest, EvictionWithAccessing) {
  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  MockPlan plans[CACHE_SIZE * 2];
  std::unordered_set<planner::AbstractPlan *> set;

  int i = 0;
  /* insert 0,1,2,3,4,5,6,7
   *
   * The cache should keep 3,4,5,6,7
   * */
  for (; i < CACHE_SIZE * 1.5; i++)
    cache.insert(std::make_pair(i, plans + i));

  set.insert(cache.begin(), cache.end());

  EXPECT_EQ(CACHE_SIZE, set.size());
  EXPECT_EQ(false, cache.empty());

  int diff = CACHE_SIZE / 2;

  /* read 4, 3
   * */
  for (int idx = CACHE_SIZE - 1; idx > CACHE_SIZE - diff - 1; idx--) {
    auto it = cache.find(idx);
    EXPECT_NE(it, cache.end());
    EXPECT_EQ(*it, plans + idx);
  }

  /* Insert 8, 9 */
  for (; i < CACHE_SIZE * 2; i++)
    cache.insert(std::make_pair(i, plans + i));

  set.clear();
  set.insert(cache.begin(), cache.end());

  for (auto it : set) {
    LOG_INFO("%lu", it - reinterpret_cast<planner::AbstractPlan*>(&plans[0]));
  }

  EXPECT_EQ(CACHE_SIZE, set.size());
  EXPECT_EQ(false, cache.empty());

  i = 0;
  for (; i < CACHE_SIZE - diff; i++) {
    EXPECT_EQ(set.end(), set.find(plans + i));
  }

  for (; i < CACHE_SIZE; i++) {
    EXPECT_NE(set.end(), set.find(plans + i));
  }

  for (; i < CACHE_SIZE + diff; i++) {
    EXPECT_EQ(set.end(), set.find(plans + i));
  }

  for (; i < CACHE_SIZE * 2; i++) {
    EXPECT_NE(set.end(), set.find(plans + i));
  }

  i = 0;
  for (; i < CACHE_SIZE; i++) {
    cache.insert(std::make_pair(i, plans + i));
  }

  set.clear();
  set.insert(cache.begin(), cache.end());

  EXPECT_EQ(CACHE_SIZE, cache.size());
  EXPECT_EQ(false, cache.empty());

  i = 0;
  for (; i < CACHE_SIZE; i++) {
    EXPECT_NE(set.end(), set.find(plans + i));
  }
}

/**
 * Test eviction
 *
 * Try to insert 2 times of the capacity of the cache
 * The cache should keep the most recent half
 */
TEST(CacheTest, Updating) {
  Cache<uint32_t, planner::AbstractPlan *> cache(CACHE_SIZE);

  MockPlan plans[CACHE_SIZE * 2];

  std::unordered_set<planner::AbstractPlan *> set;

  /* insert 0,1,2,3,4,5,6,7
   *
   * The cache should keep 3,4,5,6,7
   * */
  int i = 0;
  for (; i < CACHE_SIZE * 1.5; i++)
    cache.insert(std::make_pair(i, plans + i));

  set.insert(cache.begin(), cache.end());

  EXPECT_EQ(CACHE_SIZE, set.size());
  EXPECT_EQ(false, cache.empty());

  int diff = CACHE_SIZE / 2;

  MockPlan plans2[diff];

  /* update 4, 3
   * */
  for (int idx = CACHE_SIZE - 1, j = 0; idx > CACHE_SIZE - diff - 1; idx--, j++) {
    cache.insert(std::make_pair(idx, plans2 + j));
  }

  for (; i < CACHE_SIZE * 2; i++)
    cache.insert(std::make_pair(i, plans + i));

  set.clear();
  set.insert(cache.begin(), cache.end());

  for (auto it : set) {
    LOG_INFO("%lu", it - reinterpret_cast<planner::AbstractPlan*>(&plans[0]));
  }

  EXPECT_EQ(CACHE_SIZE, set.size());
  EXPECT_EQ(false, cache.empty());

  i = 0;
  for (; i < CACHE_SIZE - diff; i++) {
    EXPECT_EQ(set.end(), set.find(plans + i));
  }

  for (int j = 0; i < CACHE_SIZE; i++, j++) {
    EXPECT_EQ(set.end(), set.find(plans + i));
    EXPECT_NE(set.end(), set.find(plans2 + j));
  }

  for (; i < CACHE_SIZE + diff; i++) {
    EXPECT_EQ(set.end(), set.find(plans + i));
  }

  for (; i < CACHE_SIZE * 2; i++) {
    EXPECT_NE(set.end(), set.find(plans + i));
  }
}

}  // End test namespace
}  // End peloton namespace
<|endoftext|>


<commit_before>/*
Copyright libCellML Contributors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "gtest/gtest.h"

#include <iostream>
#include <libcellml>

/*
 * The tests in this file are here to catch any branches of code that
 * are not picked up by the main tests testing the API of the library
 */
TEST(Coverage, connectionComment)
{
    const std::string in =
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        "<model xmlns=\"http://www.cellml.org/cellml/2.0#\" name=\"modelA\">\n"
        "  <component name=\"componentA\">\n"
        "    <variable name=\"variable1\" units=\"dimensionless\"/>\n"
        "  </component>\n"
        "  <connection component_1=\"component1\">\n"
        "    <!-- Comment on connection. -->\n"
        "    <map_variables variable_1=\"variable1\" variable_2=\"variable2\"><!-- Comment in a map variables. --></map_variables>\n"
        "  </connection>\n"
        "</model>\n";

    libcellml::Parser p;
    p.parseModel(in);
    EXPECT_EQ(size_t(4), p.errorCount());
}

TEST(Coverage, import)
{
    const std::string e;
    libcellml::ImportSource i;
    libcellml::ImportSource im;

    im = std::move(i);

    // Copy constructor
    libcellml::ImportSource ic(im);

    const std::string a = ic.id();
    EXPECT_EQ(e, a);
}

TEST(Coverage, importWithNonHrefXlink)
{
    const std::string e =
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        "<model xmlns=\"http://www.cellml.org/cellml/2.0#\">\n"
        "  <import xlink:href=\"some-other-model.xml\" xlink:type=\"simple\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"
        "    <component component_ref=\"component_in_that_model\" name=\"component_in_this_model\"/>\n"
        "    <!-- Comment in an import block -->\n"
        "  </import>\n"
        "  <component name=\"component_bob\">\n"
        "    <variable name=\"variable_bob\" units=\"dimensionless\"/>\n"
        "  </component>\n"
        "  <connection component_2=\"component_in_this_model\" component_1=\"component_bob\">\n"
        "    <map_variables variable_2=\"variable_import\" variable_1=\"variable_bob\"/>\n"
        "  </connection>\n"
        "</model>\n";

    // Parse
    libcellml::Parser parser;
    parser.parseModel(e);
    EXPECT_EQ(size_t(0), parser.errorCount());
    //    EXPECT_EQ("bob", parser.error(0)->description());
}

TEST(Coverage, printer)
{
    libcellml::Printer p;
    libcellml::Printer pm;

    pm = std::move(p);

    // Copy constructor
    libcellml::Printer pc(pm);

    size_t error_count = pc.errorCount();
    EXPECT_EQ(size_t(0), error_count);
}

TEST(Coverage, units)
{
    const std::string e = "<units name=\"dimensionless\"/>\n";
    libcellml::Units u;
    libcellml::Units um;

    u.setName("dimensionless");

    um = std::move(u);

    // Copy constructor
    libcellml::Units uc(um);

    EXPECT_EQ("dimensionless", uc.name());
}

TEST(Coverage, when)
{
    const std::string randomValue = "4738";

    libcellml::When w;
    libcellml::When wm;
    libcellml::Reset r;

    w.setValue(randomValue);
    wm = std::move(w);

    libcellml::When wc(wm);

    libcellml::WhenPtr wp = std::make_shared<libcellml::When>(wc);
    r.addWhen(wp);

    EXPECT_EQ(randomValue, wc.value());
}

TEST(Coverage, unitsGetVariations)
{
    libcellml::Model m;

    libcellml::UnitsPtr u = std::make_shared<libcellml::Units>();
    u->setName("a_unit");

    u->addUnit(libcellml::Units::StandardUnit::AMPERE, "micro");
    m.addUnits(u);

    libcellml::UnitsPtr un = m.units(0);
    EXPECT_EQ("a_unit", un->name());
    libcellml::UnitsPtr uSn = static_cast<const libcellml::Model>(m).units(0);
    EXPECT_EQ("a_unit", uSn->name());

    libcellml::UnitsPtr uns = m.units("a_unit");
    EXPECT_EQ("a_unit", uns->name());
    libcellml::UnitsPtr uSns = static_cast<const libcellml::Model>(m).units("a_unit");
    EXPECT_EQ("a_unit", uSns->name());

    EXPECT_EQ(nullptr, m.units("b_unit"));
    EXPECT_EQ(nullptr, m.units(4));
}

TEST(Coverage, prefixToString)
{
    libcellml::Model m;
    libcellml::Printer printer;

    std::vector<std::string> prefixString =
        {"atto",
         "centi",
         "deca",
         "deci",
         "exa",
         "femto",
         "giga",
         "hecto",
         "kilo",
         "mega",
         "micro",
         "milli",
         "nano",
         "peta",
         "pico",
         "tera",
         "yocto",
         "yotta",
         "zepto",
         "zetta"};
    std::vector<libcellml::Prefix> prefixEnum =
        {libcellml::Prefix::ATTO,
         libcellml::Prefix::CENTI,
         libcellml::Prefix::DECA,
         libcellml::Prefix::DECI,
         libcellml::Prefix::EXA,
         libcellml::Prefix::FEMTO,
         libcellml::Prefix::GIGA,
         libcellml::Prefix::HECTO,
         libcellml::Prefix::KILO,
         libcellml::Prefix::MEGA,
         libcellml::Prefix::MICRO,
         libcellml::Prefix::MILLI,
         libcellml::Prefix::NANO,
         libcellml::Prefix::PETA,
         libcellml::Prefix::PICO,
         libcellml::Prefix::TERA,
         libcellml::Prefix::YOCTO,
         libcellml::Prefix::YOTTA,
         libcellml::Prefix::ZEPTO,
         libcellml::Prefix::ZETTA};
    for (std::vector<std::string>::size_type i = 0; i != prefixString.size(); ++i) {
        libcellml::UnitsPtr u = std::make_shared<libcellml::Units>();
        u->setName("abcdefg");
        u->addUnit("empty", prefixEnum[i]);

        m.addUnits(u);

        const std::string a = printer.printModel(m);
        std::size_t found = a.find(prefixString[i]);
        EXPECT_NE(std::string::npos, found);
        m.removeAllUnits();
    }
}

TEST(Coverage, variable)
{
    const std::string e = "<variable units=\"dimensionless\" initial_value=\"1\" interface=\"public\"/>\n";
    const std::string dimensionless = "dimensionless";
    libcellml::Variable v;
    libcellml::Variable vm;
    libcellml::UnitsPtr u = std::make_shared<libcellml::Units>();

    v.setInitialValue(1.0);
    v.setInterfaceType("public");
    u->setName(dimensionless);
    v.setUnits(u);

    vm = std::move(v);

    // Copy constructor
    libcellml::Variable vc(vm);

    EXPECT_EQ(dimensionless, vc.units());
}

TEST(Coverage, component)
{
    const std::string math = "<1+1=2>\n";
    libcellml::Component c;
    libcellml::Component cm;
    libcellml::VariablePtr v = std::make_shared<libcellml::Variable>();

    c.setName("name");
    c.addVariable(v);
    c.setMath(math);

    EXPECT_EQ(size_t(1), c.variableCount());

    cm = std::move(c);
    EXPECT_EQ(size_t(1), cm.variableCount());

    // Copy constructor
    libcellml::Component cc(cm);
    EXPECT_EQ(size_t(1), cc.variableCount());
}

TEST(Coverage, error)
{
    libcellml::ErrorPtr err = std::make_shared<libcellml::Error>();
    libcellml::Error e;
    libcellml::Error em;
    const std::string description = "test";

    e.setDescription(description);
    e.setKind(libcellml::Error::Kind::XML);

    em = std::move(e);
    // Copy constructor
    libcellml::Error ec(em);

    EXPECT_EQ(description, ec.description());
    EXPECT_EQ(libcellml::Error::Kind::XML, ec.kind());
}
<commit_msg>Remove commented out line in coverage test.<commit_after>/*
Copyright libCellML Contributors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "gtest/gtest.h"

#include <iostream>
#include <libcellml>

/*
 * The tests in this file are here to catch any branches of code that
 * are not picked up by the main tests testing the API of the library
 */
TEST(Coverage, connectionComment)
{
    const std::string in =
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        "<model xmlns=\"http://www.cellml.org/cellml/2.0#\" name=\"modelA\">\n"
        "  <component name=\"componentA\">\n"
        "    <variable name=\"variable1\" units=\"dimensionless\"/>\n"
        "  </component>\n"
        "  <connection component_1=\"component1\">\n"
        "    <!-- Comment on connection. -->\n"
        "    <map_variables variable_1=\"variable1\" variable_2=\"variable2\"><!-- Comment in a map variables. --></map_variables>\n"
        "  </connection>\n"
        "</model>\n";

    libcellml::Parser p;
    p.parseModel(in);
    EXPECT_EQ(size_t(4), p.errorCount());
}

TEST(Coverage, import)
{
    const std::string e;
    libcellml::ImportSource i;
    libcellml::ImportSource im;

    im = std::move(i);

    // Copy constructor
    libcellml::ImportSource ic(im);

    const std::string a = ic.id();
    EXPECT_EQ(e, a);
}

TEST(Coverage, importWithNonHrefXlink)
{
    const std::string e =
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        "<model xmlns=\"http://www.cellml.org/cellml/2.0#\">\n"
        "  <import xlink:href=\"some-other-model.xml\" xlink:type=\"simple\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"
        "    <component component_ref=\"component_in_that_model\" name=\"component_in_this_model\"/>\n"
        "    <!-- Comment in an import block -->\n"
        "  </import>\n"
        "  <component name=\"component_bob\">\n"
        "    <variable name=\"variable_bob\" units=\"dimensionless\"/>\n"
        "  </component>\n"
        "  <connection component_2=\"component_in_this_model\" component_1=\"component_bob\">\n"
        "    <map_variables variable_2=\"variable_import\" variable_1=\"variable_bob\"/>\n"
        "  </connection>\n"
        "</model>\n";

    // Parse
    libcellml::Parser parser;
    parser.parseModel(e);
    EXPECT_EQ(size_t(0), parser.errorCount());
}

TEST(Coverage, printer)
{
    libcellml::Printer p;
    libcellml::Printer pm;

    pm = std::move(p);

    // Copy constructor
    libcellml::Printer pc(pm);

    size_t error_count = pc.errorCount();
    EXPECT_EQ(size_t(0), error_count);
}

TEST(Coverage, units)
{
    const std::string e = "<units name=\"dimensionless\"/>\n";
    libcellml::Units u;
    libcellml::Units um;

    u.setName("dimensionless");

    um = std::move(u);

    // Copy constructor
    libcellml::Units uc(um);

    EXPECT_EQ("dimensionless", uc.name());
}

TEST(Coverage, when)
{
    const std::string randomValue = "4738";

    libcellml::When w;
    libcellml::When wm;
    libcellml::Reset r;

    w.setValue(randomValue);
    wm = std::move(w);

    libcellml::When wc(wm);

    libcellml::WhenPtr wp = std::make_shared<libcellml::When>(wc);
    r.addWhen(wp);

    EXPECT_EQ(randomValue, wc.value());
}

TEST(Coverage, unitsGetVariations)
{
    libcellml::Model m;

    libcellml::UnitsPtr u = std::make_shared<libcellml::Units>();
    u->setName("a_unit");

    u->addUnit(libcellml::Units::StandardUnit::AMPERE, "micro");
    m.addUnits(u);

    libcellml::UnitsPtr un = m.units(0);
    EXPECT_EQ("a_unit", un->name());
    libcellml::UnitsPtr uSn = static_cast<const libcellml::Model>(m).units(0);
    EXPECT_EQ("a_unit", uSn->name());

    libcellml::UnitsPtr uns = m.units("a_unit");
    EXPECT_EQ("a_unit", uns->name());
    libcellml::UnitsPtr uSns = static_cast<const libcellml::Model>(m).units("a_unit");
    EXPECT_EQ("a_unit", uSns->name());

    EXPECT_EQ(nullptr, m.units("b_unit"));
    EXPECT_EQ(nullptr, m.units(4));
}

TEST(Coverage, prefixToString)
{
    libcellml::Model m;
    libcellml::Printer printer;

    std::vector<std::string> prefixString =
        {"atto",
         "centi",
         "deca",
         "deci",
         "exa",
         "femto",
         "giga",
         "hecto",
         "kilo",
         "mega",
         "micro",
         "milli",
         "nano",
         "peta",
         "pico",
         "tera",
         "yocto",
         "yotta",
         "zepto",
         "zetta"};
    std::vector<libcellml::Prefix> prefixEnum =
        {libcellml::Prefix::ATTO,
         libcellml::Prefix::CENTI,
         libcellml::Prefix::DECA,
         libcellml::Prefix::DECI,
         libcellml::Prefix::EXA,
         libcellml::Prefix::FEMTO,
         libcellml::Prefix::GIGA,
         libcellml::Prefix::HECTO,
         libcellml::Prefix::KILO,
         libcellml::Prefix::MEGA,
         libcellml::Prefix::MICRO,
         libcellml::Prefix::MILLI,
         libcellml::Prefix::NANO,
         libcellml::Prefix::PETA,
         libcellml::Prefix::PICO,
         libcellml::Prefix::TERA,
         libcellml::Prefix::YOCTO,
         libcellml::Prefix::YOTTA,
         libcellml::Prefix::ZEPTO,
         libcellml::Prefix::ZETTA};
    for (std::vector<std::string>::size_type i = 0; i != prefixString.size(); ++i) {
        libcellml::UnitsPtr u = std::make_shared<libcellml::Units>();
        u->setName("abcdefg");
        u->addUnit("empty", prefixEnum[i]);

        m.addUnits(u);

        const std::string a = printer.printModel(m);
        std::size_t found = a.find(prefixString[i]);
        EXPECT_NE(std::string::npos, found);
        m.removeAllUnits();
    }
}

TEST(Coverage, variable)
{
    const std::string e = "<variable units=\"dimensionless\" initial_value=\"1\" interface=\"public\"/>\n";
    const std::string dimensionless = "dimensionless";
    libcellml::Variable v;
    libcellml::Variable vm;
    libcellml::UnitsPtr u = std::make_shared<libcellml::Units>();

    v.setInitialValue(1.0);
    v.setInterfaceType("public");
    u->setName(dimensionless);
    v.setUnits(u);

    vm = std::move(v);

    // Copy constructor
    libcellml::Variable vc(vm);

    EXPECT_EQ(dimensionless, vc.units());
}

TEST(Coverage, component)
{
    const std::string math = "<1+1=2>\n";
    libcellml::Component c;
    libcellml::Component cm;
    libcellml::VariablePtr v = std::make_shared<libcellml::Variable>();

    c.setName("name");
    c.addVariable(v);
    c.setMath(math);

    EXPECT_EQ(size_t(1), c.variableCount());

    cm = std::move(c);
    EXPECT_EQ(size_t(1), cm.variableCount());

    // Copy constructor
    libcellml::Component cc(cm);
    EXPECT_EQ(size_t(1), cc.variableCount());
}

TEST(Coverage, error)
{
    libcellml::ErrorPtr err = std::make_shared<libcellml::Error>();
    libcellml::Error e;
    libcellml::Error em;
    const std::string description = "test";

    e.setDescription(description);
    e.setKind(libcellml::Error::Kind::XML);

    em = std::move(e);
    // Copy constructor
    libcellml::Error ec(em);

    EXPECT_EQ(description, ec.description());
    EXPECT_EQ(libcellml::Error::Kind::XML, ec.kind());
}
<|endoftext|>


<commit_before>#include <boost/preprocessor.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/tuple/to_seq.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <silicium/to_unique.hpp>

#if SILICIUM_COMPILER_GENERATES_MOVES
#	define SILICIUM_MOVABLE_MEMBER(struct_name, member_name) SILICIUM_DEFAULT_NOEXCEPT_MOVE(struct_name)
#else
#	define SILICIUM_MOVABLE_MEMBER(struct_name, member_name) \
	struct_name() : member_name() BOOST_NOEXCEPT {} \
	struct_name(struct_name &&other) BOOST_NOEXCEPT : member_name(std::move(other.member_name)) {} \
	struct_name &operator = (struct_name &&other) BOOST_NOEXCEPT { member_name = std::move(other.member_name); return *this; }
#endif

#define SILICIUM_DETAIL_MAKE_PARAMETER(z, n, array) BOOST_PP_COMMA_IF(n) BOOST_PP_ARRAY_ELEM(n, array) BOOST_PP_CAT(arg, n)
#define SILICIUM_DETAIL_MAKE_PARAMETERS(array) ( BOOST_PP_REPEAT(BOOST_PP_ARRAY_SIZE(array), SILICIUM_DETAIL_MAKE_PARAMETER, array) )

#define SILICIUM_DETAIL_MAKE_PURE_VIRTUAL_METHOD(r, data, elem) \
	virtual \
	BOOST_PP_TUPLE_ELEM(4, 2, elem) \
	BOOST_PP_TUPLE_ELEM(4, 0, elem) \
	SILICIUM_DETAIL_MAKE_PARAMETERS(BOOST_PP_TUPLE_ELEM(4, 1, elem)) \
	BOOST_PP_TUPLE_ELEM(4, 3, elem) \
	= 0;

#define SILICIUM_DETAIL_MAKE_INTERFACE(name, methods) struct name { \
	virtual ~name() {} \
	BOOST_PP_SEQ_FOR_EACH(SILICIUM_DETAIL_MAKE_PURE_VIRTUAL_METHOD, _, methods) \
};

#define SILICIUM_DETAIL_ERASER_METHOD_ARGUMENT(z, n, text) , BOOST_PP_CAT(_, n)

#define SILICIUM_DETAIL_MAKE_ERASER_METHOD(r, data, elem) \
	virtual \
	BOOST_PP_TUPLE_ELEM(4, 2, elem) \
	BOOST_PP_TUPLE_ELEM(4, 0, elem) \
	SILICIUM_DETAIL_MAKE_PARAMETERS(BOOST_PP_TUPLE_ELEM(4, 1, elem)) \
	BOOST_PP_TUPLE_ELEM(4, 3, elem) \
	SILICIUM_OVERRIDE { \
		return original. BOOST_PP_TUPLE_ELEM(4, 0, elem) ( \
			BOOST_PP_ENUM_PARAMS(BOOST_PP_ARRAY_SIZE(BOOST_PP_TUPLE_ELEM(4, 1, elem)), arg) \
		); \
	}

#define SILICIUM_DETAIL_MAKE_BOX_METHOD(r, data, elem) \
	BOOST_PP_TUPLE_ELEM(4, 2, elem) \
	BOOST_PP_TUPLE_ELEM(4, 0, elem) \
	SILICIUM_DETAIL_MAKE_PARAMETERS(BOOST_PP_TUPLE_ELEM(4, 1, elem)) \
	BOOST_PP_TUPLE_ELEM(4, 3, elem) { \
		assert(original); \
		return original -> BOOST_PP_TUPLE_ELEM(4, 0, elem) ( \
			BOOST_PP_ENUM_PARAMS(BOOST_PP_ARRAY_SIZE(BOOST_PP_TUPLE_ELEM(4, 1, elem)), arg) \
		); \
	}

#define SILICIUM_DETAIL_MAKE_ERASER(name, methods) template <class Original> struct name : interface { \
	Original original; \
	SILICIUM_MOVABLE_MEMBER(name, original) \
	explicit name(Original original) : original(std::move(original)) {} \
	BOOST_PP_SEQ_FOR_EACH(SILICIUM_DETAIL_MAKE_ERASER_METHOD, _, methods) \
};

#define SILICIUM_DETAIL_MAKE_BOX(name, methods) struct box { \
	std::unique_ptr<interface> original; \
	SILICIUM_MOVABLE_MEMBER(name, original) \
	explicit name(std::unique_ptr<interface> original) BOOST_NOEXCEPT : original(std::move(original)) {} \
	BOOST_PP_SEQ_FOR_EACH(SILICIUM_DETAIL_MAKE_BOX_METHOD, _, methods) \
};

#define SILICIUM_TRAIT(name, methods) struct name { \
	SILICIUM_DETAIL_MAKE_INTERFACE(interface, methods) \
	SILICIUM_DETAIL_MAKE_ERASER(eraser, methods) \
	SILICIUM_DETAIL_MAKE_BOX(box, methods) \
	template <class Original> \
	static eraser<typename std::decay<Original>::type> erase(Original &&original) { \
		return eraser<typename std::decay<Original>::type>{std::forward<Original>(original)}; \
	} \
	template <class Original> \
	static box make_box(Original &&original) { \
		return box{Si::to_unique(erase(std::forward<Original>(original)))}; \
	} \
};

typedef long element;

SILICIUM_TRAIT(
	Producer,
	((get, (0, ()), element))
)

struct test_producer
{
	element get()
	{
		return 42;
	}
};

BOOST_AUTO_TEST_CASE(trivial_trait)
{
	std::unique_ptr<Producer::interface> p = Si::to_unique(Producer::erase(test_producer{}));
	BOOST_REQUIRE(p);
	BOOST_CHECK_EQUAL(42, p->get());
}

template <class T>
SILICIUM_TRAIT(
	Container,
	((emplace_back, (1, (T)), void))
	((resize, (1, (size_t)), void))
	((resize, (2, (size_t, T const &)), void))
	((empty, (0, ()), bool, const))
	((size, (0, ()), size_t, const BOOST_NOEXCEPT))
)

BOOST_AUTO_TEST_CASE(templatized_trait)
{
	auto container = Container<int>::erase(std::vector<int>{});
	container.emplace_back(123);
	{
		std::vector<int> const expected{123};
		BOOST_CHECK(expected == container.original);
	}
	container.resize(2);
	{
		std::vector<int> const expected{123, 0};
		BOOST_CHECK(expected == container.original);
	}
	container.resize(3, 7);
	{
		std::vector<int> const expected{123, 0, 7};
		BOOST_CHECK(expected == container.original);
	}
}

BOOST_AUTO_TEST_CASE(trait_const_method)
{
	auto container = Container<int>::erase(std::vector<int>{});
	auto const &const_ref = container;
	BOOST_CHECK(const_ref.empty());
	container.original.resize(1);
	BOOST_CHECK(!const_ref.empty());
}

#if SILICIUM_COMPILER_HAS_WORKING_NOEXCEPT
BOOST_AUTO_TEST_CASE(trait_noexcept_method)
{
	auto container = Container<int>::erase(std::vector<int>{});
	auto const &const_ref = container;
	BOOST_CHECK_EQUAL(0, const_ref.size());
	container.original.resize(3);
	BOOST_CHECK_EQUAL(3, const_ref.size());
	BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(const_ref.size()));
}
#endif

BOOST_AUTO_TEST_CASE(trait_box)
{
	Container<int>::box container = Container<int>::make_box(std::vector<int>{});
	container.emplace_back(3);
	BOOST_CHECK(!container.empty());
	BOOST_CHECK_EQUAL(1, container.size());
}
<commit_msg>generate a default constructor for trait box and eraser<commit_after>#include <boost/preprocessor.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/tuple/to_seq.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <silicium/to_unique.hpp>

#if SILICIUM_COMPILER_GENERATES_MOVES
#	define SILICIUM_MOVABLE_MEMBER(struct_name, member_name) \
	struct_name() = default; \
	SILICIUM_DEFAULT_MOVE(struct_name)
#else
#	define SILICIUM_MOVABLE_MEMBER(struct_name, member_name) \
	struct_name() : member_name() BOOST_NOEXCEPT {} \
	struct_name(struct_name &&other) BOOST_NOEXCEPT : member_name(std::move(other.member_name)) {} \
	struct_name &operator = (struct_name &&other) BOOST_NOEXCEPT { member_name = std::move(other.member_name); return *this; }
#endif

#define SILICIUM_DETAIL_MAKE_PARAMETER(z, n, array) BOOST_PP_COMMA_IF(n) BOOST_PP_ARRAY_ELEM(n, array) BOOST_PP_CAT(arg, n)
#define SILICIUM_DETAIL_MAKE_PARAMETERS(array) ( BOOST_PP_REPEAT(BOOST_PP_ARRAY_SIZE(array), SILICIUM_DETAIL_MAKE_PARAMETER, array) )

#define SILICIUM_DETAIL_MAKE_PURE_VIRTUAL_METHOD(r, data, elem) \
	virtual \
	BOOST_PP_TUPLE_ELEM(4, 2, elem) \
	BOOST_PP_TUPLE_ELEM(4, 0, elem) \
	SILICIUM_DETAIL_MAKE_PARAMETERS(BOOST_PP_TUPLE_ELEM(4, 1, elem)) \
	BOOST_PP_TUPLE_ELEM(4, 3, elem) \
	= 0;

#define SILICIUM_DETAIL_MAKE_INTERFACE(name, methods) struct name { \
	virtual ~name() {} \
	BOOST_PP_SEQ_FOR_EACH(SILICIUM_DETAIL_MAKE_PURE_VIRTUAL_METHOD, _, methods) \
};

#define SILICIUM_DETAIL_ERASER_METHOD_ARGUMENT(z, n, text) , BOOST_PP_CAT(_, n)

#define SILICIUM_DETAIL_MAKE_ERASER_METHOD(r, data, elem) \
	virtual \
	BOOST_PP_TUPLE_ELEM(4, 2, elem) \
	BOOST_PP_TUPLE_ELEM(4, 0, elem) \
	SILICIUM_DETAIL_MAKE_PARAMETERS(BOOST_PP_TUPLE_ELEM(4, 1, elem)) \
	BOOST_PP_TUPLE_ELEM(4, 3, elem) \
	SILICIUM_OVERRIDE { \
		return original. BOOST_PP_TUPLE_ELEM(4, 0, elem) ( \
			BOOST_PP_ENUM_PARAMS(BOOST_PP_ARRAY_SIZE(BOOST_PP_TUPLE_ELEM(4, 1, elem)), arg) \
		); \
	}

#define SILICIUM_DETAIL_MAKE_BOX_METHOD(r, data, elem) \
	BOOST_PP_TUPLE_ELEM(4, 2, elem) \
	BOOST_PP_TUPLE_ELEM(4, 0, elem) \
	SILICIUM_DETAIL_MAKE_PARAMETERS(BOOST_PP_TUPLE_ELEM(4, 1, elem)) \
	BOOST_PP_TUPLE_ELEM(4, 3, elem) { \
		assert(original); \
		return original -> BOOST_PP_TUPLE_ELEM(4, 0, elem) ( \
			BOOST_PP_ENUM_PARAMS(BOOST_PP_ARRAY_SIZE(BOOST_PP_TUPLE_ELEM(4, 1, elem)), arg) \
		); \
	}

#define SILICIUM_DETAIL_MAKE_ERASER(name, methods) template <class Original> struct name : interface { \
	Original original; \
	SILICIUM_MOVABLE_MEMBER(name, original) \
	explicit name(Original original) : original(std::move(original)) {} \
	BOOST_PP_SEQ_FOR_EACH(SILICIUM_DETAIL_MAKE_ERASER_METHOD, _, methods) \
};

#define SILICIUM_DETAIL_MAKE_BOX(name, methods) struct box { \
	std::unique_ptr<interface> original; \
	SILICIUM_MOVABLE_MEMBER(name, original) \
	explicit name(std::unique_ptr<interface> original) BOOST_NOEXCEPT : original(std::move(original)) {} \
	BOOST_PP_SEQ_FOR_EACH(SILICIUM_DETAIL_MAKE_BOX_METHOD, _, methods) \
};

#define SILICIUM_TRAIT(name, methods) struct name { \
	SILICIUM_DETAIL_MAKE_INTERFACE(interface, methods) \
	SILICIUM_DETAIL_MAKE_ERASER(eraser, methods) \
	SILICIUM_DETAIL_MAKE_BOX(box, methods) \
	template <class Original> \
	static eraser<typename std::decay<Original>::type> erase(Original &&original) { \
		return eraser<typename std::decay<Original>::type>{std::forward<Original>(original)}; \
	} \
	template <class Original> \
	static box make_box(Original &&original) { \
		return box{Si::to_unique(erase(std::forward<Original>(original)))}; \
	} \
};

typedef long element;

SILICIUM_TRAIT(
	Producer,
	((get, (0, ()), element))
)

struct test_producer
{
	element get()
	{
		return 42;
	}
};

BOOST_AUTO_TEST_CASE(trivial_trait)
{
	std::unique_ptr<Producer::interface> p = Si::to_unique(Producer::erase(test_producer{}));
	BOOST_REQUIRE(p);
	BOOST_CHECK_EQUAL(42, p->get());
}

template <class T>
SILICIUM_TRAIT(
	Container,
	((emplace_back, (1, (T)), void))
	((resize, (1, (size_t)), void))
	((resize, (2, (size_t, T const &)), void))
	((empty, (0, ()), bool, const))
	((size, (0, ()), size_t, const BOOST_NOEXCEPT))
)

BOOST_AUTO_TEST_CASE(templatized_trait)
{
	auto container = Container<int>::erase(std::vector<int>{});
	container.emplace_back(123);
	{
		std::vector<int> const expected{123};
		BOOST_CHECK(expected == container.original);
	}
	container.resize(2);
	{
		std::vector<int> const expected{123, 0};
		BOOST_CHECK(expected == container.original);
	}
	container.resize(3, 7);
	{
		std::vector<int> const expected{123, 0, 7};
		BOOST_CHECK(expected == container.original);
	}
}

BOOST_AUTO_TEST_CASE(trait_const_method)
{
	auto container = Container<int>::erase(std::vector<int>{});
	auto const &const_ref = container;
	BOOST_CHECK(const_ref.empty());
	container.original.resize(1);
	BOOST_CHECK(!const_ref.empty());
}

#if SILICIUM_COMPILER_HAS_WORKING_NOEXCEPT
BOOST_AUTO_TEST_CASE(trait_noexcept_method)
{
	auto container = Container<int>::erase(std::vector<int>{});
	auto const &const_ref = container;
	BOOST_CHECK_EQUAL(0, const_ref.size());
	container.original.resize(3);
	BOOST_CHECK_EQUAL(3, const_ref.size());
	BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(const_ref.size()));
}
#endif

BOOST_AUTO_TEST_CASE(trait_box)
{
	Container<int>::box container = Container<int>::make_box(std::vector<int>{});
	container.emplace_back(3);
	BOOST_CHECK(!container.empty());
	BOOST_CHECK_EQUAL(1, container.size());
}
<|endoftext|>


<commit_before>#include "FanCalculatorScene.h"
#include "../mahjong-algorithm/stringify.h"
#include "../mahjong-algorithm/fan_calculator.h"
#include "../widget/TilePickWidget.h"
#include "../widget/ExtraInfoWidget.h"
#include "../widget/AlertView.h"
#include "../widget/TilesKeyboard.h"
#include "../FanTable/FanDefinition.h"

USING_NS_CC;

bool FanCalculatorScene::init() {
    if (UNLIKELY(!BaseScene::initWithTitle("国标麻将算番器"))) {
        return false;
    }

    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    // 选牌面板
    TilePickWidget *tilePicker = TilePickWidget::create();
    const Size &widgetSize = tilePicker->getContentSize();
    this->addChild(tilePicker);
    tilePicker->setPosition(Vec2(origin.x + visibleSize.width * 0.5f,
        origin.y + visibleSize.height - 35 - widgetSize.height * 0.5f));
    _tilePicker = tilePicker;

    // 其他信息的相关控件
    ExtraInfoWidget *extraInfo = ExtraInfoWidget::create();
    const Size &extraSize = extraInfo->getContentSize();
    this->addChild(extraInfo);
    extraInfo->setPosition(Vec2(origin.x + visibleSize.width * 0.5f,
        origin.y + visibleSize.height - 35 - widgetSize.height - 5 - extraSize.height * 0.5f));
    _extraInfo = extraInfo;

    // 直接输入
    ui::Button *button = ui::Button::create("source_material/btn_square_highlighted.png", "source_material/btn_square_selected.png");
    button->setScale9Enabled(true);
    button->setContentSize(Size(55.0f, 20.0f));
    button->setTitleFontSize(12);
    button->setTitleText("直接输入");
    extraInfo->addChild(button);
    button->setPosition(Vec2(visibleSize.width - 40, 100.0f));
    button->addClickEventListener([this](Ref *) { showInputAlert(nullptr); });

    // 番算按钮
    button = ui::Button::create("source_material/btn_square_highlighted.png", "source_material/btn_square_selected.png");
    button->setScale9Enabled(true);
    button->setContentSize(Size(55.0f, 20.0f));
    button->setTitleFontSize(12);
    button->setTitleText("算  番");
    extraInfo->addChild(button);
    button->setPosition(Vec2(visibleSize.width - 40, 10.0f));
    button->addClickEventListener([this](Ref *) { calculate(); });

    // 番种显示的Node
    Size areaSize(visibleSize.width, visibleSize.height - 35 - widgetSize.height - 5 - extraSize.height - 10);
    _fanAreaNode = Node::create();
    _fanAreaNode->setContentSize(areaSize);
    _fanAreaNode->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
    _fanAreaNode->setIgnoreAnchorPointForPosition(false);
    this->addChild(_fanAreaNode);
    _fanAreaNode->setPosition(Vec2(origin.x + visibleSize.width * 0.5f, origin.y + areaSize.height * 0.5f + 5));

    tilePicker->setFixedPacksChangedCallback([tilePicker, extraInfo]() {
        extraInfo->refreshByKong(tilePicker->isFixedPacksContainsKong());
    });

    tilePicker->setWinTileChangedCallback([tilePicker, extraInfo]() {
        ExtraInfoWidget::RefreshByWinTile rt;
        rt.getWinTile = std::bind(&TilePickWidget::getServingTile, tilePicker);
        rt.isStandingTilesContainsServingTile = std::bind(&TilePickWidget::isStandingTilesContainsServingTile, tilePicker);
        rt.countServingTileInFixedPacks = std::bind(&TilePickWidget::countServingTileInFixedPacks, tilePicker);
        rt.isFixedPacksContainsKong = std::bind(&TilePickWidget::isFixedPacksContainsKong, tilePicker);
        extraInfo->refreshByWinTile(rt);
    });

    return true;
}

static cocos2d::Node *createFanResultNode(const mahjong::fan_table_t &fan_table, int fontSize, float resultAreaWidth) {
    // 有n个番种，每行排2个
    ptrdiff_t n = mahjong::FAN_TABLE_SIZE - std::count(std::begin(fan_table), std::end(fan_table), 0);
    ptrdiff_t rows = (n >> 1) + (n & 1);  // 需要这么多行

    // 排列
    Node *node = Node::create();
    const int lineHeight = fontSize + 2;
    ptrdiff_t resultAreaHeight = lineHeight * rows;  // 每行间隔2像素
    resultAreaHeight += (5 + lineHeight) + 20;  // 总计+提示
    node->setContentSize(Size(resultAreaWidth, static_cast<float>(resultAreaHeight)));

    long fan = 0;
    for (int i = 0, j = 0; i < n; ++i) {
        while (fan_table[++j] == 0) continue;

        int f = mahjong::fan_value_table[j];
        long n = fan_table[j];
        fan += f * n;
        std::string str = (n == 1) ? Common::format<128>("%s %d番\n", mahjong::fan_name[j], f)
            : Common::format<128>("%s %d番x%ld\n", mahjong::fan_name[j], f, fan_table[j]);

        // 创建label，每行排2个
        Label *label = Label::createWithSystemFont(str, "Arial", static_cast<float>(fontSize));
        label->setColor(Color3B(0x60, 0x60, 0x60));
        node->addChild(label);
        label->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
        div_t ret = div(i, 2);
        label->setPosition(Vec2(ret.rem == 0 ? 0.0f : resultAreaWidth * 0.5f, static_cast<float>(resultAreaHeight - lineHeight * (ret.quot + 1))));

        // 创建与label同位置的button
        ui::Button *button = ui::Button::create();
        button->setScale9Enabled(true);
        button->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
        button->setPosition(label->getPosition());
        button->setContentSize(label->getContentSize());
        node->addChild(button);
        button->addClickEventListener([j](Ref *) {
            Director::getInstance()->pushScene(FanDefinitionScene::create(j));
        });
    }

    Label *label = Label::createWithSystemFont(Common::format<64>("总计：%ld番", fan), "Arial", static_cast<float>(fontSize));
    label->setColor(Color3B::BLACK);
    node->addChild(label);
    label->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
    label->setPosition(Vec2(0.0f, lineHeight * 0.5f + 20));

    label = Label::createWithSystemFont("点击番种名可查看番种介绍。", "Arial", 10);
    label->setColor(Color3B(51, 204, 255));
    node->addChild(label);
    label->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
    label->setPosition(Vec2(0.0f, 5));

    return node;
}

void FanCalculatorScene::showInputAlert(const char *prevInput) {
    Size visibleSize = Director::getInstance()->getVisibleSize();
    const float width = visibleSize.width * 0.8f - 10;

    Node *rootNode = Node::create();

    Label *label = Label::createWithSystemFont("使用说明：\n"
        "1." INPUT_GUIDE_STRING_1 "\n"
        "2." INPUT_GUIDE_STRING_2 "\n"
        "3." INPUT_GUIDE_STRING_3 "\n"
        "输入范例1：[EEEE][CCCC][FFFF][PPPP]NN\n"
        "输入范例2：1112345678999s9s\n"
        "输入范例3：WWWW 444s 45m678pFF6m\n", "Arial", 10);
    label->setColor(Color3B::BLACK);
    label->setDimensions(width, 0);
    rootNode->addChild(label);

    // 输入手牌
    ui::EditBox *editBox = ui::EditBox::create(Size(width - 10, 20.0f), ui::Scale9Sprite::create("source_material/btn_square_normal.png"));
    editBox->setInputFlag(ui::EditBox::InputFlag::SENSITIVE);
    editBox->setInputMode(ui::EditBox::InputMode::SINGLE_LINE);
    editBox->setReturnType(ui::EditBox::KeyboardReturnType::DONE);
    editBox->setFontColor(Color4B::BLACK);
    editBox->setFontSize(12);
    editBox->setPlaceholderFontColor(Color4B::GRAY);
    editBox->setPlaceHolder("输入手牌");
    if (prevInput != nullptr) {
        editBox->setText(prevInput);
    }
    TilesKeyboard::hookEditBox(editBox);
    rootNode->addChild(editBox);

    const Size &labelSize = label->getContentSize();
    rootNode->setContentSize(Size(width, labelSize.height + 30));
    editBox->setPosition(Vec2(width * 0.5f, 10));
    label->setPosition(Vec2(width * 0.5f, labelSize.height * 0.5f + 30));

    AlertView::showWithNode("直接输入", rootNode, [this, editBox]() {
        const char *input = editBox->getText();
        const char *errorStr = TilesKeyboard::parseInput(input,
            std::bind(&TilePickWidget::setData, _tilePicker, std::placeholders::_1, std::placeholders::_2));
        if (errorStr != nullptr) {
            const std::string str = input;
            AlertView::showWithMessage("直接输入牌", errorStr, 12, [this, str]() {
                showInputAlert(str.c_str());
            }, nullptr);
        }
    }, nullptr);
}

void FanCalculatorScene::calculate() {
    _fanAreaNode->removeAllChildren();

    const Size &fanAreaSize = _fanAreaNode->getContentSize();
    Vec2 pos(fanAreaSize.width * 0.5f, fanAreaSize.height * 0.5f);

    int flowerCnt = _extraInfo->getFlowerCount();
    if (flowerCnt > 8) {
        AlertView::showWithMessage("算番", "花牌数的范围为0~8", 12, nullptr, nullptr);
        return;
    }

    mahjong::calculate_param_t param;
    _tilePicker->getData(&param.hand_tiles, &param.win_tile);
    if (param.win_tile == 0) {
        AlertView::showWithMessage("算番", "牌张数错误", 12, nullptr, nullptr);
        return;
    }

    std::sort(param.hand_tiles.standing_tiles, param.hand_tiles.standing_tiles + param.hand_tiles.tile_count);

    param.flower_count = flowerCnt;
    mahjong::fan_table_t fan_table = { 0 };

    // 获取绝张、杠开、抢杠、海底信息
    mahjong::win_flag_t win_flag = _extraInfo->getWinFlag();

    // 获取圈风门风
    mahjong::wind_t prevalent_wind = _extraInfo->getPrevalentWind();
    mahjong::wind_t seat_wind = _extraInfo->getSeatWind();

    // 算番
    param.win_flag = win_flag;
    param.prevalent_wind = prevalent_wind;
    param.seat_wind = seat_wind;
    int fan = calculate_fan(&param, fan_table);

    if (fan == ERROR_NOT_WIN) {
        Label *errorLabel = Label::createWithSystemFont("诈和", "Arial", 14);
        errorLabel->setColor(Color3B::BLACK);
        _fanAreaNode->addChild(errorLabel);
        errorLabel->setPosition(pos);
        return;
    }
    if (fan == ERROR_WRONG_TILES_COUNT) {
        AlertView::showWithMessage("算番", "牌张数错误", 12, nullptr, nullptr);
        return;
    }
    if (fan == ERROR_TILE_COUNT_GREATER_THAN_4) {
        AlertView::showWithMessage("算番", "同一种牌最多只能使用4枚", 12, nullptr, nullptr);
        return;
    }

    Node *innerNode = createFanResultNode(fan_table, 14, fanAreaSize.width - 10);

    // 超出高度就使用ScrollView
    if (innerNode->getContentSize().height <= fanAreaSize.height) {
        _fanAreaNode->addChild(innerNode);
        innerNode->setAnchorPoint(Vec2(0.5f, 0.5f));
        innerNode->setPosition(pos);
    }
    else {
        ui::ScrollView *scrollView = ui::ScrollView::create();
        scrollView->setDirection(ui::ScrollView::Direction::VERTICAL);
        scrollView->setScrollBarPositionFromCorner(Vec2(2, 2));
        scrollView->setScrollBarWidth(4);
        scrollView->setScrollBarOpacity(0x99);
        scrollView->setContentSize(Size(fanAreaSize.width - 10, fanAreaSize.height));
        scrollView->setInnerContainerSize(innerNode->getContentSize());
        scrollView->addChild(innerNode);

        _fanAreaNode->addChild(scrollView);
        scrollView->setAnchorPoint(Vec2(0.5f, 0.5f));
        scrollView->setPosition(pos);
    }
}
<commit_msg>修复GCC编译警告<commit_after>#include "FanCalculatorScene.h"
#include "../mahjong-algorithm/stringify.h"
#include "../mahjong-algorithm/fan_calculator.h"
#include "../widget/TilePickWidget.h"
#include "../widget/ExtraInfoWidget.h"
#include "../widget/AlertView.h"
#include "../widget/TilesKeyboard.h"
#include "../FanTable/FanDefinition.h"

USING_NS_CC;

bool FanCalculatorScene::init() {
    if (UNLIKELY(!BaseScene::initWithTitle("国标麻将算番器"))) {
        return false;
    }

    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    // 选牌面板
    TilePickWidget *tilePicker = TilePickWidget::create();
    const Size &widgetSize = tilePicker->getContentSize();
    this->addChild(tilePicker);
    tilePicker->setPosition(Vec2(origin.x + visibleSize.width * 0.5f,
        origin.y + visibleSize.height - 35 - widgetSize.height * 0.5f));
    _tilePicker = tilePicker;

    // 其他信息的相关控件
    ExtraInfoWidget *extraInfo = ExtraInfoWidget::create();
    const Size &extraSize = extraInfo->getContentSize();
    this->addChild(extraInfo);
    extraInfo->setPosition(Vec2(origin.x + visibleSize.width * 0.5f,
        origin.y + visibleSize.height - 35 - widgetSize.height - 5 - extraSize.height * 0.5f));
    _extraInfo = extraInfo;

    // 直接输入
    ui::Button *button = ui::Button::create("source_material/btn_square_highlighted.png", "source_material/btn_square_selected.png");
    button->setScale9Enabled(true);
    button->setContentSize(Size(55.0f, 20.0f));
    button->setTitleFontSize(12);
    button->setTitleText("直接输入");
    extraInfo->addChild(button);
    button->setPosition(Vec2(visibleSize.width - 40, 100.0f));
    button->addClickEventListener([this](Ref *) { showInputAlert(nullptr); });

    // 番算按钮
    button = ui::Button::create("source_material/btn_square_highlighted.png", "source_material/btn_square_selected.png");
    button->setScale9Enabled(true);
    button->setContentSize(Size(55.0f, 20.0f));
    button->setTitleFontSize(12);
    button->setTitleText("算  番");
    extraInfo->addChild(button);
    button->setPosition(Vec2(visibleSize.width - 40, 10.0f));
    button->addClickEventListener([this](Ref *) { calculate(); });

    // 番种显示的Node
    Size areaSize(visibleSize.width, visibleSize.height - 35 - widgetSize.height - 5 - extraSize.height - 10);
    _fanAreaNode = Node::create();
    _fanAreaNode->setContentSize(areaSize);
    _fanAreaNode->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
    _fanAreaNode->setIgnoreAnchorPointForPosition(false);
    this->addChild(_fanAreaNode);
    _fanAreaNode->setPosition(Vec2(origin.x + visibleSize.width * 0.5f, origin.y + areaSize.height * 0.5f + 5));

    tilePicker->setFixedPacksChangedCallback([tilePicker, extraInfo]() {
        extraInfo->refreshByKong(tilePicker->isFixedPacksContainsKong());
    });

    tilePicker->setWinTileChangedCallback([tilePicker, extraInfo]() {
        ExtraInfoWidget::RefreshByWinTile rt;
        rt.getWinTile = std::bind(&TilePickWidget::getServingTile, tilePicker);
        rt.isStandingTilesContainsServingTile = std::bind(&TilePickWidget::isStandingTilesContainsServingTile, tilePicker);
        rt.countServingTileInFixedPacks = std::bind(&TilePickWidget::countServingTileInFixedPacks, tilePicker);
        rt.isFixedPacksContainsKong = std::bind(&TilePickWidget::isFixedPacksContainsKong, tilePicker);
        extraInfo->refreshByWinTile(rt);
    });

    return true;
}

static cocos2d::Node *createFanResultNode(const mahjong::fan_table_t &fan_table, int fontSize, float resultAreaWidth) {
    // 有n个番种，每行排2个
    ptrdiff_t n = mahjong::FAN_TABLE_SIZE - std::count(std::begin(fan_table), std::end(fan_table), 0);
    ptrdiff_t rows = (n >> 1) + (n & 1);  // 需要这么多行

    // 排列
    Node *node = Node::create();
    const int lineHeight = fontSize + 2;
    ptrdiff_t resultAreaHeight = lineHeight * rows;  // 每行间隔2像素
    resultAreaHeight += (5 + lineHeight) + 20;  // 总计+提示
    node->setContentSize(Size(resultAreaWidth, static_cast<float>(resultAreaHeight)));

    uint16_t fan = 0;
    for (int i = 0, j = 0; i < n; ++i) {
        while (fan_table[++j] == 0) continue;

        uint16_t f = mahjong::fan_value_table[j];
        uint16_t n = fan_table[j];
        fan += f * n;
        std::string str = (n == 1) ? Common::format<128>("%s %hd番\n", mahjong::fan_name[j], f)
            : Common::format<128>("%s %hd番x%hd\n", mahjong::fan_name[j], f, fan_table[j]);

        // 创建label，每行排2个
        Label *label = Label::createWithSystemFont(str, "Arial", static_cast<float>(fontSize));
        label->setColor(Color3B(0x60, 0x60, 0x60));
        node->addChild(label);
        label->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
        div_t ret = div(i, 2);
        label->setPosition(Vec2(ret.rem == 0 ? 0.0f : resultAreaWidth * 0.5f, static_cast<float>(resultAreaHeight - lineHeight * (ret.quot + 1))));

        // 创建与label同位置的button
        ui::Button *button = ui::Button::create();
        button->setScale9Enabled(true);
        button->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
        button->setPosition(label->getPosition());
        button->setContentSize(label->getContentSize());
        node->addChild(button);
        button->addClickEventListener([j](Ref *) {
            Director::getInstance()->pushScene(FanDefinitionScene::create(j));
        });
    }

    Label *label = Label::createWithSystemFont(Common::format<64>("总计：%ld番", fan), "Arial", static_cast<float>(fontSize));
    label->setColor(Color3B::BLACK);
    node->addChild(label);
    label->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
    label->setPosition(Vec2(0.0f, lineHeight * 0.5f + 20));

    label = Label::createWithSystemFont("点击番种名可查看番种介绍。", "Arial", 10);
    label->setColor(Color3B(51, 204, 255));
    node->addChild(label);
    label->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
    label->setPosition(Vec2(0.0f, 5));

    return node;
}

void FanCalculatorScene::showInputAlert(const char *prevInput) {
    Size visibleSize = Director::getInstance()->getVisibleSize();
    const float width = visibleSize.width * 0.8f - 10;

    Node *rootNode = Node::create();

    Label *label = Label::createWithSystemFont("使用说明：\n"
        "1." INPUT_GUIDE_STRING_1 "\n"
        "2." INPUT_GUIDE_STRING_2 "\n"
        "3." INPUT_GUIDE_STRING_3 "\n"
        "输入范例1：[EEEE][CCCC][FFFF][PPPP]NN\n"
        "输入范例2：1112345678999s9s\n"
        "输入范例3：WWWW 444s 45m678pFF6m\n", "Arial", 10);
    label->setColor(Color3B::BLACK);
    label->setDimensions(width, 0);
    rootNode->addChild(label);

    // 输入手牌
    ui::EditBox *editBox = ui::EditBox::create(Size(width - 10, 20.0f), ui::Scale9Sprite::create("source_material/btn_square_normal.png"));
    editBox->setInputFlag(ui::EditBox::InputFlag::SENSITIVE);
    editBox->setInputMode(ui::EditBox::InputMode::SINGLE_LINE);
    editBox->setReturnType(ui::EditBox::KeyboardReturnType::DONE);
    editBox->setFontColor(Color4B::BLACK);
    editBox->setFontSize(12);
    editBox->setPlaceholderFontColor(Color4B::GRAY);
    editBox->setPlaceHolder("输入手牌");
    if (prevInput != nullptr) {
        editBox->setText(prevInput);
    }
    TilesKeyboard::hookEditBox(editBox);
    rootNode->addChild(editBox);

    const Size &labelSize = label->getContentSize();
    rootNode->setContentSize(Size(width, labelSize.height + 30));
    editBox->setPosition(Vec2(width * 0.5f, 10));
    label->setPosition(Vec2(width * 0.5f, labelSize.height * 0.5f + 30));

    AlertView::showWithNode("直接输入", rootNode, [this, editBox]() {
        const char *input = editBox->getText();
        const char *errorStr = TilesKeyboard::parseInput(input,
            std::bind(&TilePickWidget::setData, _tilePicker, std::placeholders::_1, std::placeholders::_2));
        if (errorStr != nullptr) {
            const std::string str = input;
            AlertView::showWithMessage("直接输入牌", errorStr, 12, [this, str]() {
                showInputAlert(str.c_str());
            }, nullptr);
        }
    }, nullptr);
}

void FanCalculatorScene::calculate() {
    _fanAreaNode->removeAllChildren();

    const Size &fanAreaSize = _fanAreaNode->getContentSize();
    Vec2 pos(fanAreaSize.width * 0.5f, fanAreaSize.height * 0.5f);

    int flowerCnt = _extraInfo->getFlowerCount();
    if (flowerCnt > 8) {
        AlertView::showWithMessage("算番", "花牌数的范围为0~8", 12, nullptr, nullptr);
        return;
    }

    mahjong::calculate_param_t param;
    _tilePicker->getData(&param.hand_tiles, &param.win_tile);
    if (param.win_tile == 0) {
        AlertView::showWithMessage("算番", "牌张数错误", 12, nullptr, nullptr);
        return;
    }

    std::sort(param.hand_tiles.standing_tiles, param.hand_tiles.standing_tiles + param.hand_tiles.tile_count);

    param.flower_count = flowerCnt;
    mahjong::fan_table_t fan_table = { 0 };

    // 获取绝张、杠开、抢杠、海底信息
    mahjong::win_flag_t win_flag = _extraInfo->getWinFlag();

    // 获取圈风门风
    mahjong::wind_t prevalent_wind = _extraInfo->getPrevalentWind();
    mahjong::wind_t seat_wind = _extraInfo->getSeatWind();

    // 算番
    param.win_flag = win_flag;
    param.prevalent_wind = prevalent_wind;
    param.seat_wind = seat_wind;
    int fan = calculate_fan(&param, fan_table);

    if (fan == ERROR_NOT_WIN) {
        Label *errorLabel = Label::createWithSystemFont("诈和", "Arial", 14);
        errorLabel->setColor(Color3B::BLACK);
        _fanAreaNode->addChild(errorLabel);
        errorLabel->setPosition(pos);
        return;
    }
    if (fan == ERROR_WRONG_TILES_COUNT) {
        AlertView::showWithMessage("算番", "牌张数错误", 12, nullptr, nullptr);
        return;
    }
    if (fan == ERROR_TILE_COUNT_GREATER_THAN_4) {
        AlertView::showWithMessage("算番", "同一种牌最多只能使用4枚", 12, nullptr, nullptr);
        return;
    }

    Node *innerNode = createFanResultNode(fan_table, 14, fanAreaSize.width - 10);

    // 超出高度就使用ScrollView
    if (innerNode->getContentSize().height <= fanAreaSize.height) {
        _fanAreaNode->addChild(innerNode);
        innerNode->setAnchorPoint(Vec2(0.5f, 0.5f));
        innerNode->setPosition(pos);
    }
    else {
        ui::ScrollView *scrollView = ui::ScrollView::create();
        scrollView->setDirection(ui::ScrollView::Direction::VERTICAL);
        scrollView->setScrollBarPositionFromCorner(Vec2(2, 2));
        scrollView->setScrollBarWidth(4);
        scrollView->setScrollBarOpacity(0x99);
        scrollView->setContentSize(Size(fanAreaSize.width - 10, fanAreaSize.height));
        scrollView->setInnerContainerSize(innerNode->getContentSize());
        scrollView->addChild(innerNode);

        _fanAreaNode->addChild(scrollView);
        scrollView->setAnchorPoint(Vec2(0.5f, 0.5f));
        scrollView->setPosition(pos);
    }
}
<|endoftext|>