<commit_before>#include "animation_system.h"
#include "animation/animation.h"
#include "engine/base_proxy_allocator.h"
#include "engine/blob.h"
#include "engine/crc32.h"
#include "engine/engine.h"
#include "engine/json_serializer.h"
#include "engine/lua_wrapper.h"
#include "engine/profiler.h"
#include "engine/property_descriptor.h"
#include "engine/property_register.h"
#include "engine/resource_manager.h"
#include "engine/universe/universe.h"
#include "renderer/model.h"
#include "renderer/pose.h"
#include "renderer/render_scene.h"
#include <cfloat>

namespace Lumix
{

static const ComponentType ANIMABLE_TYPE = PropertyRegister::getComponentType("animable");
static const ResourceType ANIMATION_TYPE("animation");

namespace FS
{
class FileSystem;
};

class Animation;
class Engine;
class JsonSerializer;
class Universe;

enum class AnimationSceneVersion : int
{
	FIRST,
	REFACTOR,

	LATEST
};

struct AnimationSceneImpl : public AnimationScene
{
	friend struct AnimationSystemImpl;

	struct Animable
	{
		float time;
		float time_scale;
		float start_time;
		Animation* animation;
		Entity entity;
	};

	struct Mixer
	{
		struct Input
		{
			Animation* animation = nullptr;
			float time = 0.0f;
			float weight = 0.0f;
		};

		Input inputs[8];
		Entity entity;
	};

	AnimationSceneImpl(IPlugin& anim_system, Engine& engine, Universe& universe, IAllocator& allocator)
		: m_universe(universe)
		, m_engine(engine)
		, m_anim_system(anim_system)
		, m_animables(allocator)
		, m_mixers(allocator)
	{
		m_universe.entityDestroyed().bind<AnimationSceneImpl, &AnimationSceneImpl::onEntityDestroyed>(this);
		m_is_game_running = false;
		m_render_scene = static_cast<RenderScene*>(universe.getScene(crc32("renderer")));
		universe.registerComponentTypeScene(ANIMABLE_TYPE, this);
		ASSERT(m_render_scene);
	}

	~AnimationSceneImpl()
	{
		m_universe.entityDestroyed().unbind<AnimationSceneImpl, &AnimationSceneImpl::onEntityDestroyed>(this);
	}

	void onEntityDestroyed(Entity entity)
	{
		m_mixers.erase(entity);
	}

	void clear() override
	{
		for (Mixer& mixer : m_mixers)
		{
			for (auto& input : mixer.inputs)
			{
				unloadAnimation(input.animation);
			}
		}
		m_mixers.clear();

		for (Animable& animable : m_animables)
		{
			unloadAnimation(animable.animation);
		}
		m_animables.clear();
	}

	float getAnimationLength(int animation_idx)
	{
		auto* animation = static_cast<Animation*>(animation_idx > 0 ? m_engine.getLuaResource(animation_idx) : nullptr);
		if (animation) return animation->getLength();
		return 0;
	}

	void mixAnimation(Entity entity, int animation_idx, int input_idx, float time, float weight)
	{
		auto* animation = static_cast<Animation*>(animation_idx > 0 ? m_engine.getLuaResource(animation_idx) : nullptr);
		int mixer_idx = m_mixers.find(entity);
		if (mixer_idx < 0)
		{
			Mixer& mixer = m_mixers.insert(entity);
			mixer.entity = entity;
			mixer_idx = m_mixers.find(entity);
		}
		Mixer& mixer = m_mixers.at(mixer_idx);
		auto& input = mixer.inputs[input_idx];
		input.animation = animation;
		input.time = time;
		input.weight = weight;
	}

	float getAnimableTime(ComponentHandle cmp) override
	{
		return m_animables[{cmp.index}].time;
	}

	void setAnimableTime(ComponentHandle cmp, float time) override
	{
		m_animables[{cmp.index}].time = time;
	}

	Animation* getAnimableAnimation(ComponentHandle cmp) override
	{
		return m_animables[{cmp.index}].animation;
	}

	void startGame() override { m_is_game_running = true; }
	void stopGame() override { m_is_game_running = false; }
	Universe& getUniverse() override { return m_universe; }

	ComponentHandle getComponent(Entity entity, ComponentType type) override
	{
		if (type == ANIMABLE_TYPE)
		{
			if (m_animables.find(entity) < 0) return INVALID_COMPONENT;
			return {entity.index};
		}
		return INVALID_COMPONENT;
	}

	ComponentHandle createComponent(ComponentType type, Entity entity) override
	{
		if (type == ANIMABLE_TYPE) return createAnimable(entity);
		return INVALID_COMPONENT;
	}

	void unloadAnimation(Animation* animation)
	{
		if (!animation) return;

		animation->getResourceManager().unload(*animation);
	}

	void destroyComponent(ComponentHandle component, ComponentType type) override
	{
		if (type == ANIMABLE_TYPE)
		{
			Entity entity = {component.index};
			auto& animable = m_animables[entity];
			unloadAnimation(animable.animation);
			m_animables.erase(entity);
			m_universe.destroyComponent(entity, type, this, component);
		}
	}

	void serialize(OutputBlob& serializer) override
	{
		serializer.write((int32)m_animables.size());
		for (const Animable& animable : m_animables)
		{
			serializer.write(animable.entity);
			serializer.write(animable.time_scale);
			serializer.write(animable.start_time);
			serializer.writeString(animable.animation ? animable.animation->getPath().c_str() : "");
		}
	}

	int getVersion() const override { return (int)AnimationSceneVersion::LATEST; }

	void deserialize(InputBlob& serializer, int version) override
	{
		int32 count;
		serializer.read(count);
		m_animables.reserve(count);
		for (int i = 0; i < count; ++i)
		{
			Animable animable;
			serializer.read(animable.entity);
			bool free = false;
			if (version <= (int)AnimationSceneVersion::FIRST)
			{
				serializer.read(animable.time);
				serializer.read(free);
				animable.time_scale = 1;
				animable.start_time = 0;
			}
			else
			{
				uint32 flags = 0;
				if(version <= (int)AnimationSceneVersion::REFACTOR) serializer.read(flags);
				free = flags != 0;
				serializer.read(animable.time_scale);
				serializer.read(animable.start_time);
				animable.time = animable.start_time;
			}

			char path[MAX_PATH_LENGTH];
			serializer.readString(path, sizeof(path));
			animable.animation = path[0] == '\0' ? nullptr : loadAnimation(Path(path));
			if (!free)
			{
				m_animables.insert(animable.entity, animable);
				ComponentHandle cmp = {animable.entity.index};
				m_universe.addComponent(animable.entity, ANIMABLE_TYPE, this, cmp);
			}
		}
	}

	float getTimeScale(ComponentHandle cmp) { return m_animables[{cmp.index}].time_scale; }
	void setTimeScale(ComponentHandle cmp, float time_scale) { m_animables[{cmp.index}].time_scale = time_scale; }
	float getStartTime(ComponentHandle cmp) { return m_animables[{cmp.index}].start_time; }
	void setStartTime(ComponentHandle cmp, float time) { m_animables[{cmp.index}].start_time = time; }

	Path getAnimation(ComponentHandle cmp)
	{
		const auto& animable = m_animables[{cmp.index}];
		return animable.animation ? animable.animation->getPath() : Path("");
	}

	void setAnimation(ComponentHandle cmp, const Path& path)
	{
		auto& animable = m_animables[{cmp.index}];
		unloadAnimation(animable.animation);
		animable.animation = loadAnimation(path);
		animable.time = 0;
	}

	void updateMixer(Mixer& mixer, float time_delta)
	{
		ComponentHandle renderable = m_render_scene->getRenderableComponent(mixer.entity);
		if (renderable == INVALID_COMPONENT) return;

		auto* pose = m_render_scene->getPose(renderable);
		auto* model = m_render_scene->getRenderableModel(renderable);

		if (!pose) return;
		if (!model->isReady()) return;

		model->getPose(*pose);
		pose->computeRelative(*model);

		for (int i = 0; i < lengthOf(mixer.inputs); ++i)
		{
			Mixer::Input& input = mixer.inputs[i];
			if (!input.animation) break;
			ASSERT(input.animation->isReady());
			if (i == 0)
			{
				input.animation->getRelativePose(input.time, *pose, *model);
			}
			else
			{
				input.animation->getRelativePose(input.time, *pose, *model, input.weight);
			}
			input.animation = nullptr;
		}
		pose->computeAbsolute(*model);
	}

	void updateAnimable(Animable& animable, float time_delta)
	{
		if (!animable.animation || !animable.animation->isReady()) return;
		ComponentHandle renderable = m_render_scene->getRenderableComponent(animable.entity);
		if (renderable == INVALID_COMPONENT) return;

		auto* pose = m_render_scene->getPose(renderable);
		auto* model = m_render_scene->getRenderableModel(renderable);

		if (!pose) return;
		if (!model->isReady()) return;

		model->getPose(*pose);
		pose->computeRelative(*model);
		animable.animation->getRelativePose(animable.time, *pose, *model);
		pose->computeAbsolute(*model);

		float t = animable.time + time_delta * animable.time_scale;
		float l = animable.animation->getLength();
		while (t > l)
		{
			t -= l;
		}
		animable.time = t;
	}

	void updateAnimable(ComponentHandle cmp, float time_delta) override
	{
		Animable& animable = m_animables[{cmp.index}];
		updateAnimable(animable, time_delta);
	}

	void update(float time_delta, bool paused) override
	{
		PROFILE_FUNCTION();
		if (!m_is_game_running) return;

		for (Mixer& mixer : m_mixers)
		{
			AnimationSceneImpl::updateMixer(mixer, time_delta);
		}

		for (Animable& animable : m_animables)
		{
			AnimationSceneImpl::updateAnimable(animable, time_delta);
		}
	}

	Animation* loadAnimation(const Path& path)
	{
		ResourceManager& rm = m_engine.getResourceManager();
		return static_cast<Animation*>(rm.get(ANIMATION_TYPE)->load(path));
	}

	ComponentHandle createAnimable(Entity entity)
	{
		Animable& animable = m_animables.insert(entity);
		animable.time = 0;
		animable.animation = nullptr;
		animable.entity = entity;
		animable.time_scale = 1;
		animable.start_time = 0;

		ComponentHandle cmp = {entity.index};
		m_universe.addComponent(entity, ANIMABLE_TYPE, this, cmp);
		return cmp;
	}

	IPlugin& getPlugin() const override { return m_anim_system; }

	Universe& m_universe;
	IPlugin& m_anim_system;
	Engine& m_engine;
	AssociativeArray<Entity, Animable> m_animables;
	AssociativeArray<Entity, Mixer> m_mixers;
	RenderScene* m_render_scene;
	bool m_is_game_running;
};

struct AnimationSystemImpl : public IPlugin
{
	explicit AnimationSystemImpl(Engine& engine)
		: m_allocator(engine.getAllocator())
		, m_engine(engine)
		, animation_manager(m_allocator)
	{
		animation_manager.create(ANIMATION_TYPE, m_engine.getResourceManager());

		PropertyRegister::add("animable",
			LUMIX_NEW(m_allocator, ResourcePropertyDescriptor<AnimationSceneImpl>)("Animation",
				&AnimationSceneImpl::getAnimation,
				&AnimationSceneImpl::setAnimation,
				"Animation (*.ani)",
				ANIMATION_TYPE));
		PropertyRegister::add("animable",
			LUMIX_NEW(m_allocator, DecimalPropertyDescriptor<AnimationSceneImpl>)(
				"Start time", &AnimationSceneImpl::getStartTime, &AnimationSceneImpl::setStartTime, 0, FLT_MAX, 0.1f));
		PropertyRegister::add("animable",
			LUMIX_NEW(m_allocator, DecimalPropertyDescriptor<AnimationSceneImpl>)(
				"Time scale", &AnimationSceneImpl::getTimeScale, &AnimationSceneImpl::setTimeScale, 0, FLT_MAX, 0.1f));

		registerLuaAPI();
	}

	void registerLuaAPI()
	{
		lua_State* L = m_engine.getState();
		#define REGISTER_FUNCTION(name) \
		do {\
			auto f = &LuaWrapper::wrapMethod<AnimationSceneImpl, decltype(&AnimationSceneImpl::name), &AnimationSceneImpl::name>; \
			LuaWrapper::createSystemFunction(L, "Animation", #name, f); \
		} while(false) \

		REGISTER_FUNCTION(mixAnimation);
		REGISTER_FUNCTION(getAnimationLength);

		#undef REGISTER_FUNCTION
	}

	IScene* createScene(Universe& ctx) override
	{
		return LUMIX_NEW(m_allocator, AnimationSceneImpl)(*this, m_engine, ctx, m_allocator);
	}

	void destroyScene(IScene* scene) override { LUMIX_DELETE(m_allocator, scene); }

	const char* getName() const override { return "animation"; }

	Lumix::IAllocator& m_allocator;
	Engine& m_engine;
	AnimationManager animation_manager;

private:
	void operator=(const AnimationSystemImpl&);
	AnimationSystemImpl(const AnimationSystemImpl&);
};

LUMIX_PLUGIN_ENTRY(animation)
{
	return LUMIX_NEW(engine.getAllocator(), AnimationSystemImpl)(engine);
}
}
<commit_msg>fixed crash when mixing animation that does not exist<commit_after>#include "animation_system.h"
#include "animation/animation.h"
#include "engine/base_proxy_allocator.h"
#include "engine/blob.h"
#include "engine/crc32.h"
#include "engine/engine.h"
#include "engine/json_serializer.h"
#include "engine/lua_wrapper.h"
#include "engine/profiler.h"
#include "engine/property_descriptor.h"
#include "engine/property_register.h"
#include "engine/resource_manager.h"
#include "engine/universe/universe.h"
#include "renderer/model.h"
#include "renderer/pose.h"
#include "renderer/render_scene.h"
#include <cfloat>

namespace Lumix
{

static const ComponentType ANIMABLE_TYPE = PropertyRegister::getComponentType("animable");
static const ResourceType ANIMATION_TYPE("animation");

namespace FS
{
class FileSystem;
};

class Animation;
class Engine;
class JsonSerializer;
class Universe;

enum class AnimationSceneVersion : int
{
	FIRST,
	REFACTOR,

	LATEST
};

struct AnimationSceneImpl : public AnimationScene
{
	friend struct AnimationSystemImpl;

	struct Animable
	{
		float time;
		float time_scale;
		float start_time;
		Animation* animation;
		Entity entity;
	};

	struct Mixer
	{
		struct Input
		{
			Animation* animation = nullptr;
			float time = 0.0f;
			float weight = 0.0f;
		};

		Input inputs[8];
		Entity entity;
	};

	AnimationSceneImpl(IPlugin& anim_system, Engine& engine, Universe& universe, IAllocator& allocator)
		: m_universe(universe)
		, m_engine(engine)
		, m_anim_system(anim_system)
		, m_animables(allocator)
		, m_mixers(allocator)
	{
		m_universe.entityDestroyed().bind<AnimationSceneImpl, &AnimationSceneImpl::onEntityDestroyed>(this);
		m_is_game_running = false;
		m_render_scene = static_cast<RenderScene*>(universe.getScene(crc32("renderer")));
		universe.registerComponentTypeScene(ANIMABLE_TYPE, this);
		ASSERT(m_render_scene);
	}

	~AnimationSceneImpl()
	{
		m_universe.entityDestroyed().unbind<AnimationSceneImpl, &AnimationSceneImpl::onEntityDestroyed>(this);
	}

	void onEntityDestroyed(Entity entity)
	{
		m_mixers.erase(entity);
	}

	void clear() override
	{
		for (Mixer& mixer : m_mixers)
		{
			for (auto& input : mixer.inputs)
			{
				unloadAnimation(input.animation);
			}
		}
		m_mixers.clear();

		for (Animable& animable : m_animables)
		{
			unloadAnimation(animable.animation);
		}
		m_animables.clear();
	}

	float getAnimationLength(int animation_idx)
	{
		auto* animation = static_cast<Animation*>(animation_idx > 0 ? m_engine.getLuaResource(animation_idx) : nullptr);
		if (animation) return animation->getLength();
		return 0;
	}

	void mixAnimation(Entity entity, int animation_idx, int input_idx, float time, float weight)
	{
		auto* animation = static_cast<Animation*>(animation_idx > 0 ? m_engine.getLuaResource(animation_idx) : nullptr);
		int mixer_idx = m_mixers.find(entity);
		if (mixer_idx < 0)
		{
			Mixer& mixer = m_mixers.insert(entity);
			mixer.entity = entity;
			mixer_idx = m_mixers.find(entity);
		}
		Mixer& mixer = m_mixers.at(mixer_idx);
		auto& input = mixer.inputs[input_idx];
		input.animation = animation;
		input.time = time;
		input.weight = weight;
	}

	float getAnimableTime(ComponentHandle cmp) override
	{
		return m_animables[{cmp.index}].time;
	}

	void setAnimableTime(ComponentHandle cmp, float time) override
	{
		m_animables[{cmp.index}].time = time;
	}

	Animation* getAnimableAnimation(ComponentHandle cmp) override
	{
		return m_animables[{cmp.index}].animation;
	}

	void startGame() override { m_is_game_running = true; }
	void stopGame() override { m_is_game_running = false; }
	Universe& getUniverse() override { return m_universe; }

	ComponentHandle getComponent(Entity entity, ComponentType type) override
	{
		if (type == ANIMABLE_TYPE)
		{
			if (m_animables.find(entity) < 0) return INVALID_COMPONENT;
			return {entity.index};
		}
		return INVALID_COMPONENT;
	}

	ComponentHandle createComponent(ComponentType type, Entity entity) override
	{
		if (type == ANIMABLE_TYPE) return createAnimable(entity);
		return INVALID_COMPONENT;
	}

	void unloadAnimation(Animation* animation)
	{
		if (!animation) return;

		animation->getResourceManager().unload(*animation);
	}

	void destroyComponent(ComponentHandle component, ComponentType type) override
	{
		if (type == ANIMABLE_TYPE)
		{
			Entity entity = {component.index};
			auto& animable = m_animables[entity];
			unloadAnimation(animable.animation);
			m_animables.erase(entity);
			m_universe.destroyComponent(entity, type, this, component);
		}
	}

	void serialize(OutputBlob& serializer) override
	{
		serializer.write((int32)m_animables.size());
		for (const Animable& animable : m_animables)
		{
			serializer.write(animable.entity);
			serializer.write(animable.time_scale);
			serializer.write(animable.start_time);
			serializer.writeString(animable.animation ? animable.animation->getPath().c_str() : "");
		}
	}

	int getVersion() const override { return (int)AnimationSceneVersion::LATEST; }

	void deserialize(InputBlob& serializer, int version) override
	{
		int32 count;
		serializer.read(count);
		m_animables.reserve(count);
		for (int i = 0; i < count; ++i)
		{
			Animable animable;
			serializer.read(animable.entity);
			bool free = false;
			if (version <= (int)AnimationSceneVersion::FIRST)
			{
				serializer.read(animable.time);
				serializer.read(free);
				animable.time_scale = 1;
				animable.start_time = 0;
			}
			else
			{
				uint32 flags = 0;
				if(version <= (int)AnimationSceneVersion::REFACTOR) serializer.read(flags);
				free = flags != 0;
				serializer.read(animable.time_scale);
				serializer.read(animable.start_time);
				animable.time = animable.start_time;
			}

			char path[MAX_PATH_LENGTH];
			serializer.readString(path, sizeof(path));
			animable.animation = path[0] == '\0' ? nullptr : loadAnimation(Path(path));
			if (!free)
			{
				m_animables.insert(animable.entity, animable);
				ComponentHandle cmp = {animable.entity.index};
				m_universe.addComponent(animable.entity, ANIMABLE_TYPE, this, cmp);
			}
		}
	}

	float getTimeScale(ComponentHandle cmp) { return m_animables[{cmp.index}].time_scale; }
	void setTimeScale(ComponentHandle cmp, float time_scale) { m_animables[{cmp.index}].time_scale = time_scale; }
	float getStartTime(ComponentHandle cmp) { return m_animables[{cmp.index}].start_time; }
	void setStartTime(ComponentHandle cmp, float time) { m_animables[{cmp.index}].start_time = time; }

	Path getAnimation(ComponentHandle cmp)
	{
		const auto& animable = m_animables[{cmp.index}];
		return animable.animation ? animable.animation->getPath() : Path("");
	}

	void setAnimation(ComponentHandle cmp, const Path& path)
	{
		auto& animable = m_animables[{cmp.index}];
		unloadAnimation(animable.animation);
		animable.animation = loadAnimation(path);
		animable.time = 0;
	}

	void updateMixer(Mixer& mixer, float time_delta)
	{
		ComponentHandle renderable = m_render_scene->getRenderableComponent(mixer.entity);
		if (renderable == INVALID_COMPONENT) return;

		auto* pose = m_render_scene->getPose(renderable);
		auto* model = m_render_scene->getRenderableModel(renderable);

		if (!pose) return;
		if (!model->isReady()) return;

		model->getPose(*pose);
		pose->computeRelative(*model);

		for (int i = 0; i < lengthOf(mixer.inputs); ++i)
		{
			Mixer::Input& input = mixer.inputs[i];
			if (!input.animation || !input.animation->isReady()) break;
			if (i == 0)
			{
				input.animation->getRelativePose(input.time, *pose, *model);
			}
			else
			{
				input.animation->getRelativePose(input.time, *pose, *model, input.weight);
			}
			input.animation = nullptr;
		}
		pose->computeAbsolute(*model);
	}

	void updateAnimable(Animable& animable, float time_delta)
	{
		if (!animable.animation || !animable.animation->isReady()) return;
		ComponentHandle renderable = m_render_scene->getRenderableComponent(animable.entity);
		if (renderable == INVALID_COMPONENT) return;

		auto* pose = m_render_scene->getPose(renderable);
		auto* model = m_render_scene->getRenderableModel(renderable);

		if (!pose) return;
		if (!model->isReady()) return;

		model->getPose(*pose);
		pose->computeRelative(*model);
		animable.animation->getRelativePose(animable.time, *pose, *model);
		pose->computeAbsolute(*model);

		float t = animable.time + time_delta * animable.time_scale;
		float l = animable.animation->getLength();
		while (t > l)
		{
			t -= l;
		}
		animable.time = t;
	}

	void updateAnimable(ComponentHandle cmp, float time_delta) override
	{
		Animable& animable = m_animables[{cmp.index}];
		updateAnimable(animable, time_delta);
	}

	void update(float time_delta, bool paused) override
	{
		PROFILE_FUNCTION();
		if (!m_is_game_running) return;

		for (Mixer& mixer : m_mixers)
		{
			AnimationSceneImpl::updateMixer(mixer, time_delta);
		}

		for (Animable& animable : m_animables)
		{
			AnimationSceneImpl::updateAnimable(animable, time_delta);
		}
	}

	Animation* loadAnimation(const Path& path)
	{
		ResourceManager& rm = m_engine.getResourceManager();
		return static_cast<Animation*>(rm.get(ANIMATION_TYPE)->load(path));
	}

	ComponentHandle createAnimable(Entity entity)
	{
		Animable& animable = m_animables.insert(entity);
		animable.time = 0;
		animable.animation = nullptr;
		animable.entity = entity;
		animable.time_scale = 1;
		animable.start_time = 0;

		ComponentHandle cmp = {entity.index};
		m_universe.addComponent(entity, ANIMABLE_TYPE, this, cmp);
		return cmp;
	}

	IPlugin& getPlugin() const override { return m_anim_system; }

	Universe& m_universe;
	IPlugin& m_anim_system;
	Engine& m_engine;
	AssociativeArray<Entity, Animable> m_animables;
	AssociativeArray<Entity, Mixer> m_mixers;
	RenderScene* m_render_scene;
	bool m_is_game_running;
};

struct AnimationSystemImpl : public IPlugin
{
	explicit AnimationSystemImpl(Engine& engine)
		: m_allocator(engine.getAllocator())
		, m_engine(engine)
		, animation_manager(m_allocator)
	{
		animation_manager.create(ANIMATION_TYPE, m_engine.getResourceManager());

		PropertyRegister::add("animable",
			LUMIX_NEW(m_allocator, ResourcePropertyDescriptor<AnimationSceneImpl>)("Animation",
				&AnimationSceneImpl::getAnimation,
				&AnimationSceneImpl::setAnimation,
				"Animation (*.ani)",
				ANIMATION_TYPE));
		PropertyRegister::add("animable",
			LUMIX_NEW(m_allocator, DecimalPropertyDescriptor<AnimationSceneImpl>)(
				"Start time", &AnimationSceneImpl::getStartTime, &AnimationSceneImpl::setStartTime, 0, FLT_MAX, 0.1f));
		PropertyRegister::add("animable",
			LUMIX_NEW(m_allocator, DecimalPropertyDescriptor<AnimationSceneImpl>)(
				"Time scale", &AnimationSceneImpl::getTimeScale, &AnimationSceneImpl::setTimeScale, 0, FLT_MAX, 0.1f));

		registerLuaAPI();
	}

	void registerLuaAPI()
	{
		lua_State* L = m_engine.getState();
		#define REGISTER_FUNCTION(name) \
		do {\
			auto f = &LuaWrapper::wrapMethod<AnimationSceneImpl, decltype(&AnimationSceneImpl::name), &AnimationSceneImpl::name>; \
			LuaWrapper::createSystemFunction(L, "Animation", #name, f); \
		} while(false) \

		REGISTER_FUNCTION(mixAnimation);
		REGISTER_FUNCTION(getAnimationLength);

		#undef REGISTER_FUNCTION
	}

	IScene* createScene(Universe& ctx) override
	{
		return LUMIX_NEW(m_allocator, AnimationSceneImpl)(*this, m_engine, ctx, m_allocator);
	}

	void destroyScene(IScene* scene) override { LUMIX_DELETE(m_allocator, scene); }

	const char* getName() const override { return "animation"; }

	Lumix::IAllocator& m_allocator;
	Engine& m_engine;
	AnimationManager animation_manager;

private:
	void operator=(const AnimationSystemImpl&);
	AnimationSystemImpl(const AnimationSystemImpl&);
};

LUMIX_PLUGIN_ENTRY(animation)
{
	return LUMIX_NEW(engine.getAllocator(), AnimationSystemImpl)(engine);
}
}
<|endoftext|>


<commit_before>// RosterSystem.cpp
// Created by Evan Almonte
//
#include "RosterSystem.hpp"
#include "Roster.hpp"
#include "RSFileManager.hpp"
#include "Student.hpp"
#include "Utilities.hpp"
#include <iostream>

using std::cout;
using std::cin;
using std::string;

namespace RosterSystemUtils {
	int getMenuOptSelection (int start, int end);
	char getLoginOptSelection ( );
}
const string RosterSystem::menuOpts[numOfMenuOpts] = {
	"Create a new Roster",
	"Drop a Roster",
	"Display Roster Information",
	"Display All Rosters",
	"Select a Roster"
};
const string RosterSystem::selectOpts[numOfSelectOpts] = {
	"Add a New Student",
	"Remove a Student",
	"Update a Student",
	"List All Students"
};

RosterSystem::RosterSystem( ) : loginStatus(NOT_LOGGED), rListSz(0), rListCap(0), rosterList(nullptr),
                                eListSz(0), eListCap(0), enrollmentList(nullptr) {}

RosterSystem::~RosterSystem( ) {
	for (int i = 0; i < rListSz; ++i) {
		delete rosterList[i];
	}
	delete[] rosterList;
	for (int i = 0; i < eListSz; ++i) {
		delete enrollmentList[i];
	}
	delete[] enrollmentList;
}

void RosterSystem::loginMenu( ) {
	clearScreen ( );
	loginStatus = USER;
	cout << "********************************\n";
	cout << "  Welcome To SchoolManager v.1  \n";
	cout << "********************************\n";
	cout << "A) Supervisor Mode\n";
	cout << "B) User Mode\n";
	cout << "C) Exit\n";
	cout << "Please choose an option: ";
	char choice = RosterSystemUtils::getLoginOptSelection ( );
	switch(choice) {
		case 'A':
		case 'a': {
			RosterSystem::RSFileManager userDatabase ("Database.txt", false);
			loginStatus = userDatabase.attemptLogin ( );
		}
		case 'B':
		case 'b':
			mainMenu ( );
			loginMenu ( );
			break;
		case 'C':
		case 'c':
			break;
		default:
			cout << "Invalid choice please try again.\n";
			cout << "Press ENTER to continue . . .";
			cin.get ( );
			loginMenu ( );
	}
}

void RosterSystem::mainMenu( ) {
	clearScreen ( );
	cout << "******************************\n";
	cout << "          Main Menu           \n";
	cout << "******************************\n";
	int startOption = 1, endOption;
	if (loginStatus == SUPERVISOR) {
		endOption = numOfMenuOpts;
		displayAdminMenu();
	} else {
		endOption = 1;
		displayUserMenu();
	}
	cout << "Please choose an option(q to Quit): ";
	int choice = RosterSystemUtils::getMenuOptSelection(startOption, endOption);

	if (choice == 1 && loginStatus != SUPERVISOR) {
		choice = 5;
	}

	switch (choice) {
		case 1:
			addRoster();
			break;
		case 2:
			{
				string courseNumber;
				cout << "Please enter the rosters course number: ";
				getline(cin, courseNumber);
				removeRoster(courseNumber);
			}
			break;
		case 3:
			{
				cout << "Please enter the rosters course number: ";
				string courseNumber;
				getline(cin, courseNumber);
				displayRoster(courseNumber);
			}
			break;
		case 4:
			displayAllRosters();
			break;
		case 5:
			{
				string courseNumber;
				cout << "Please enter the rosters course number: ";
				getline(cin, courseNumber);
				selectRoster(courseNumber);
			}
			break;
		default:
			cout << "Exiting to login menu.\n";
			return;
	}
	cout << "Press ENTER to continue . . .";
	cin.get();
	mainMenu();
}

void RosterSystem::displayAdminMenu( ) {
	for (int i = 0; i < numOfMenuOpts; ++i) {
		cout << i + 1 << ") " << menuOpts[i] << "\n";
	}
}

void RosterSystem::displayUserMenu( ) {
	int i = numOfMenuOpts - 1;
	cout << i + 1 << ") " << menuOpts[numOfMenuOpts - 1] << "\n";
}

void RosterSystem::addToEnrollmentList(Student* aStudent) {
	if (eListSz == eListCap) {
		growEnrollmentList();
	}
	enrollmentList[eListSz++] = aStudent;
}

void RosterSystem::addToEnrollmentAndRoster(Roster& selectedRoster) {
	clearScreen();
	if (eListSz == eListCap) {
		growEnrollmentList();
	}
	Student* aStudent = new Student;
	cout << "******************************\n";
	cout << "      New Student Added       \n";
	cout << "******************************\n";
	cin >> *aStudent;
	enrollmentList[eListSz++] = aStudent;
	selectedRoster.addStudent(aStudent);
}

int RosterSystem::findRoster(std::string courseCode) const {
	if (rListSz == 0) {
		cout << "There are no rosters in the system.\n";
		return EMPTY_LIST;
	}
	int foundIndex = NOT_FOUND;
	for (int i = 0; i < rListSz; ++i) {
		if (courseCode == rosterList[i]->getCourseCode()) {
			foundIndex = i;
		}
	}
	if(foundIndex == NOT_FOUND) {
		cout << "No roster with a course number of: " << courseCode << " was found.\n";
	}
	return foundIndex;
}

void RosterSystem::addRoster( ) {
	clearScreen();
	if (rListSz == rListCap) {
		growRosterList();
	}
	Roster* rosterToAdd = new Roster;
	cout << "******************************\n";
	cout << "      New Roster Created      \n";
	cout << "******************************\n";
	cin >> *rosterToAdd;
	rosterList[rListSz++] = rosterToAdd;
}

void RosterSystem::selectRoster(std::string courseNumber) {
	int location = findRoster (courseNumber);
	if (location == NOT_FOUND || location == EMPTY_LIST) {
		return;
	}
	Roster* rosterToEdit = rosterList[location];
	do {
		if (loginStatus == SUPERVISOR) {
			adminSelectOpts(*rosterToEdit);
		} else {
			userSelectOpts(*rosterToEdit);
		}
		cout << "Would you like to continue editing the same roster(Y/N)? ";
	} while (getYesOrNo());
}

void RosterSystem::removeRoster(std::string courseCode) {
	int location = findRoster(courseCode);
	if (location == NOT_FOUND || location == EMPTY_LIST) {
		return;
	}
	clearScreen ( );
	//		Displays a title as:
	//*******************************
	//  CourseName Has Been Deleted
	//*******************************
	int lengthOfName = rosterList[location]->getCourseName ( ).length ( );
	string numOfAsterixs = "**";
	for (int i = 0; i < lengthOfName; ++i) {
		numOfAsterixs += "*";
	}
	cout << numOfAsterixs << "*******************\n";
	cout << "  " << rosterList[location]->getCourseName() << " Has Been Deleted  \n";
	cout << numOfAsterixs << "*******************\n";
	//Shifts all rosters over to the left.
	delete rosterList[location];
	for (; location < rListSz - 1; ++location) {
		rosterList[location] = rosterList[location + 1];
	};
	rosterList[rListSz - 1] = nullptr;
	--rListSz;
}

void RosterSystem::growEnrollmentList( ) {
	int newCap = eListCap * 2 + 1;
	Student** tempList = new Student*[newCap];
	for (int i = 0; i < eListSz; ++i) {
		tempList[i] = enrollmentList[i];
	}
	delete[] enrollmentList;
	enrollmentList = tempList;
}

void RosterSystem::growRosterList( ) {
	int newCap = rListCap * 2 + 1;
	Roster** tempList = new Roster*[newCap];
	for (int i = 0; i < rListSz; ++i) {
		tempList[i] = rosterList[i];
	}
	delete[] rosterList;
	rListCap = newCap;
	rosterList = tempList;
}

void RosterSystem::adminSelectOpts(Roster& selectedRoster) {
	clearScreen();
	cout << "******************************\n";
	cout << "      Editing Roster          \n";
	cout << "******************************\n";
	cout << "Course: " << selectedRoster.getCourseName() << "\n";
	cout << "Code: " << selectedRoster.getCourseCode() << "\n";
	for (int i = 0; i < numOfSelectOpts; ++i) {
		cout << static_cast<char>('a' + i) << ") " << selectOpts[i] << "\n";
	}
	string userChoice;
	cout << "Please choose an option(a-d): ";
	getline(cin, userChoice);

	switch (userChoice[0]) {
		case 'A':
		case 'a':
			{
				addToEnrollmentAndRoster(selectedRoster);
			}
			break;
		case 'B':
		case 'b':
			{
				cout << "Please enter the student's last name: ";
				string lastName;
				getline(cin, lastName);
				selectedRoster.removeStudent(lastName);
			}
			break;
		case 'C':
		case 'c':
			{
				cout << "Please enter the student's last name: ";
				string lastName;
				getline(cin, lastName);
				selectedRoster.editStudent(lastName);
			}
			break;
		case 'D':
		case 'd':
			selectedRoster.listAllStudents();
			break;
		default:
			cout << "Invalid option selected.\n";
	}
}

void RosterSystem::displayRoster(std::string courseNumber) const {
	clearScreen ( );
	int location = findRoster(courseNumber);
	if (location == NOT_FOUND || location == EMPTY_LIST) {
		return;
	}
	Roster* rosterToDisplay = rosterList[location];
	rosterToDisplay->displayInfo();
}

void RosterSystem::displayAllRosters( ) const {
	clearScreen ( );
	if(rListSz == 0) {
		cout << "There are no rosters in the system.\n";
		return;
	}
	cout << "******************************\n";
	cout << "      Display All Rosters     \n";
	cout << "******************************\n";
	for (int i = 0; i < rListSz; ++i) {
		rosterList[i]->displayInfo();
		cout << "\n";
	}
}

void RosterSystem::userSelectOpts(Roster& selectedRoster) {
	clearScreen();
	cout << "******************************\n";
	cout << "      Editing Roster          \n";
	cout << "******************************\n";
	cout << "Course: " << selectedRoster.getCourseName() << "\n";
	cout << "Code: " << selectedRoster.getCourseCode() << "\n";

	//Subtract 1 due to the fact that "user" does not have access to the last option.
	for (int i = 0; i < numOfSelectOpts - 1; ++i) {
		cout << static_cast<char>('a' + i) << ") " << selectOpts[i] << "\n";
	}
	string userChoice;
	cout << "Please choose an option(a-c): ";
	getline(cin, userChoice);

	switch (userChoice[0]) {
		case 'A':
		case 'a':
			{
				addToEnrollmentAndRoster(selectedRoster);
			}
			break;
		case 'B':
		case 'b':
			{
				cout << "Please enter the student's last name: ";
				string lastName;
				getline(cin, lastName);
				selectedRoster.removeStudent(lastName);
			}
			break;
		case 'C':
		case 'c':
			{
				cout << "Please enter the student's last name: ";
				string lastName;
				getline(cin, lastName);
				selectedRoster.editStudent(lastName);
			}
			break;
		default:
			cout << "Invalid option selected.\n";
	}
}

namespace RosterSystemUtils {
	int getMenuOptSelection (int start, int end) {
		string choice;
		do {
			getline (cin, choice);
		} while (choice[0] - '0' < start || choice[0] - '0' > end &&
				 choice != "q" && choice != "Q");
		int selection = choice[0] - '0';
		return selection;
	}

	char getLoginOptSelection ( ) {
		string choice;
		do {
			getline (cin, choice);
		} while (choice != "A" && choice != "B" && choice != "C" &&
				 choice != "a" && choice != "b" && choice != "c");
		return choice[0];

	}
}<commit_msg>Adds case C to the login menu which exports the rosters to a file<commit_after>// RosterSystem.cpp
// Created by Evan Almonte
//
#include "RosterSystem.hpp"
#include "Roster.hpp"
#include "RSFileManager.hpp"
#include "Student.hpp"
#include "Utilities.hpp"
#include <iostream>

using std::cout;
using std::cin;
using std::string;

namespace RosterSystemUtils {
	int getMenuOptSelection (int start, int end);
	char getLoginOptSelection ( );
}
const string RosterSystem::menuOpts[numOfMenuOpts] = {
	"Create a new Roster",
	"Drop a Roster",
	"Display Roster Information",
	"Display All Rosters",
	"Select a Roster"
};
const string RosterSystem::selectOpts[numOfSelectOpts] = {
	"Add a New Student",
	"Remove a Student",
	"Update a Student",
	"List All Students"
};

RosterSystem::RosterSystem( ) : loginStatus(NOT_LOGGED), rListSz(0), rListCap(0), rosterList(nullptr),
                                eListSz(0), eListCap(0), enrollmentList(nullptr) {}

RosterSystem::~RosterSystem( ) {
	for (int i = 0; i < rListSz; ++i) {
		delete rosterList[i];
	}
	delete[] rosterList;
	for (int i = 0; i < eListSz; ++i) {
		delete enrollmentList[i];
	}
	delete[] enrollmentList;
}

void RosterSystem::loginMenu( ) {
	clearScreen ( );
	loginStatus = USER;
	cout << "********************************\n";
	cout << "  Welcome To SchoolManager v.1  \n";
	cout << "********************************\n";
	cout << "A) Supervisor Mode\n";
	cout << "B) User Mode\n";
	cout << "C) Exit\n";
	cout << "Please choose an option: ";
	char choice = RosterSystemUtils::getLoginOptSelection ( );
	switch(choice) {
		case 'A':
		case 'a': {
			RSFileManager database ("Database.txt", false);
			loginStatus = database.attemptLogin ( );
		}
		case 'B':
		case 'b':
			mainMenu ( );
			loginMenu ( );
			break;
		case 'C':
		case 'c': {
			RSFileManager writeRosters ("RosterFile.txt", true);
			writeRosters.exportRosters (rosterList, rListSz);
		}
			break;
		default:
			cout << "Invalid choice please try again.\n";
			cout << "Press ENTER to continue . . .";
			cin.get ( );
			loginMenu ( );
	}
}

void RosterSystem::mainMenu( ) {
	clearScreen ( );
	cout << "******************************\n";
	cout << "          Main Menu           \n";
	cout << "******************************\n";
	int startOption = 1, endOption;
	if (loginStatus == SUPERVISOR) {
		endOption = numOfMenuOpts;
		displayAdminMenu();
	} else {
		endOption = 1;
		displayUserMenu();
	}
	cout << "Please choose an option(q to Quit): ";
	int choice = RosterSystemUtils::getMenuOptSelection(startOption, endOption);

	if (choice == 1 && loginStatus != SUPERVISOR) {
		choice = 5;
	}

	switch (choice) {
		case 1:
			addRoster();
			break;
		case 2:
			{
				string courseNumber;
				cout << "Please enter the rosters course number: ";
				getline(cin, courseNumber);
				removeRoster(courseNumber);
			}
			break;
		case 3:
			{
				cout << "Please enter the rosters course number: ";
				string courseNumber;
				getline(cin, courseNumber);
				displayRoster(courseNumber);
			}
			break;
		case 4:
			displayAllRosters();
			break;
		case 5:
			{
				string courseNumber;
				cout << "Please enter the rosters course number: ";
				getline(cin, courseNumber);
				selectRoster(courseNumber);
			}
			break;
		default:
			cout << "Exiting to login menu.\n";
			return;
	}
	cout << "Press ENTER to continue . . .";
	cin.get();
	mainMenu();
}

void RosterSystem::displayAdminMenu( ) {
	for (int i = 0; i < numOfMenuOpts; ++i) {
		cout << i + 1 << ") " << menuOpts[i] << "\n";
	}
}

void RosterSystem::displayUserMenu( ) {
	int i = numOfMenuOpts - 1;
	cout << i + 1 << ") " << menuOpts[numOfMenuOpts - 1] << "\n";
}

void RosterSystem::addToEnrollmentList(Student* aStudent) {
	if (eListSz == eListCap) {
		growEnrollmentList();
	}
	enrollmentList[eListSz++] = aStudent;
}

void RosterSystem::addToEnrollmentAndRoster(Roster& selectedRoster) {
	clearScreen();
	if (eListSz == eListCap) {
		growEnrollmentList();
	}
	Student* studentToAdd = new Student();
	cout << "******************************\n";
	cout << "      New Student Added       \n";
	cout << "******************************\n";
	cin >> *studentToAdd;
	enrollmentList[eListSz++] = studentToAdd;
	selectedRoster.addStudent(studentToAdd);
}

int RosterSystem::findRoster(std::string courseCode) const {
	if (rListSz == 0) {
		cout << "There are no rosters in the system.\n";
		return EMPTY_LIST;
	}
	int foundIndex = NOT_FOUND;
	for (int i = 0; i < rListSz; ++i) {
		if (courseCode == rosterList[i]->getCourseCode()) {
			foundIndex = i;
		}
	}
	if(foundIndex == NOT_FOUND) {
		cout << "No roster with a course number of: " << courseCode << " was found.\n";
	}
	return foundIndex;
}

void RosterSystem::addRoster( ) {
	clearScreen();
	if (rListSz == rListCap) {
		growRosterList();
	}
	Roster* rosterToAdd = new Roster;
	cout << "******************************\n";
	cout << "      New Roster Created      \n";
	cout << "******************************\n";
	cin >> *rosterToAdd;
	rosterList[rListSz++] = rosterToAdd;
}

void RosterSystem::selectRoster(std::string courseNumber) {
	int location = findRoster (courseNumber);
	if (location == NOT_FOUND || location == EMPTY_LIST) {
		return;
	}
	Roster* rosterToEdit = rosterList[location];
	do {
		if (loginStatus == SUPERVISOR) {
			adminSelectOpts(*rosterToEdit);
		} else {
			userSelectOpts(*rosterToEdit);
		}
		cout << "Would you like to continue editing the same roster(Y/N)? ";
	} while (getYesOrNo());
}

void RosterSystem::removeRoster(std::string courseCode) {
	int location = findRoster(courseCode);
	if (location == NOT_FOUND || location == EMPTY_LIST) {
		return;
	}
	clearScreen ( );
	//		Displays a title as:
	//*******************************
	//  CourseName Has Been Deleted
	//*******************************
	int lengthOfName = rosterList[location]->getCourseName ( ).length ( );
	string numOfAsterixs = "**";
	for (int i = 0; i < lengthOfName; ++i) {
		numOfAsterixs += "*";
	}
	cout << numOfAsterixs << "*******************\n";
	cout << "  " << rosterList[location]->getCourseName() << " Has Been Deleted  \n";
	cout << numOfAsterixs << "*******************\n";
	//Shifts all rosters over to the left.
	delete rosterList[location];
	for (; location < rListSz - 1; ++location) {
		rosterList[location] = rosterList[location + 1];
	};
	rosterList[rListSz - 1] = nullptr;
	--rListSz;
}

void RosterSystem::growEnrollmentList( ) {
	int newCap = eListCap * 2 + 1;
	Student** tempList = new Student*[newCap];
	for (int i = 0; i < eListSz; ++i) {
		tempList[i] = enrollmentList[i];
	}
	for (int i = eListSz; i < eListCap; i++) {
		enrollmentList[i] = nullptr;
	}
	delete[] enrollmentList;
	eListCap = newCap;
	enrollmentList = tempList;
}

void RosterSystem::growRosterList( ) {
	int newCap = rListCap * 2 + 1;
	Roster** tempList = new Roster*[newCap];
	for (int i = 0; i < rListSz; ++i) {
		tempList[i] = rosterList[i];
	}
	for (int i = rListSz; i < rListCap; i++) {
		rosterList[i] = nullptr;
	}
	delete[] rosterList;
	rListCap = newCap;
	rosterList = tempList;
}

void RosterSystem::adminSelectOpts(Roster& selectedRoster) {
	clearScreen();
	cout << "******************************\n";
	cout << "      Editing Roster          \n";
	cout << "******************************\n";
	cout << "Course: " << selectedRoster.getCourseName() << "\n";
	cout << "Code: " << selectedRoster.getCourseCode() << "\n";
	for (int i = 0; i < numOfSelectOpts; ++i) {
		cout << static_cast<char>('a' + i) << ") " << selectOpts[i] << "\n";
	}
	string userChoice;
	cout << "Please choose an option(a-d): ";
	getline(cin, userChoice);

	switch (userChoice[0]) {
		case 'A':
		case 'a':
			{
				addToEnrollmentAndRoster(selectedRoster);
			}
			break;
		case 'B':
		case 'b':
			{
				cout << "Please enter the student's last name: ";
				string lastName;
				getline(cin, lastName);
				selectedRoster.removeStudent(lastName);
			}
			break;
		case 'C':
		case 'c':
			{
				cout << "Please enter the student's last name: ";
				string lastName;
				getline(cin, lastName);
				selectedRoster.editStudent(lastName);
			}
			break;
		case 'D':
		case 'd':
			selectedRoster.listAllStudents();
			break;
		default:
			cout << "Invalid option selected.\n";
	}
}

void RosterSystem::displayRoster(std::string courseNumber) const {
	clearScreen ( );
	int location = findRoster(courseNumber);
	if (location == NOT_FOUND || location == EMPTY_LIST) {
		return;
	}
	Roster* rosterToDisplay = rosterList[location];
	rosterToDisplay->displayInfo();
}

void RosterSystem::displayAllRosters( ) const {
	clearScreen ( );
	if(rListSz == 0) {
		cout << "There are no rosters in the system.\n";
		return;
	}
	cout << "******************************\n";
	cout << "      Display All Rosters     \n";
	cout << "******************************\n";
	for (int i = 0; i < rListSz; ++i) {
		rosterList[i]->displayInfo();
		cout << "\n";
	}
}

void RosterSystem::userSelectOpts(Roster& selectedRoster) {
	clearScreen();
	cout << "******************************\n";
	cout << "      Editing Roster          \n";
	cout << "******************************\n";
	cout << "Course: " << selectedRoster.getCourseName() << "\n";
	cout << "Code: " << selectedRoster.getCourseCode() << "\n";

	//Subtract 1 due to the fact that "user" does not have access to the last option.
	for (int i = 0; i < numOfSelectOpts - 1; ++i) {
		cout << static_cast<char>('a' + i) << ") " << selectOpts[i] << "\n";
	}
	string userChoice;
	cout << "Please choose an option(a-c): ";
	getline(cin, userChoice);

	switch (userChoice[0]) {
		case 'A':
		case 'a':
			{
				addToEnrollmentAndRoster(selectedRoster);
			}
			break;
		case 'B':
		case 'b':
			{
				cout << "Please enter the student's last name: ";
				string lastName;
				getline(cin, lastName);
				selectedRoster.removeStudent(lastName);
			}
			break;
		case 'C':
		case 'c':
			{
				cout << "Please enter the student's last name: ";
				string lastName;
				getline(cin, lastName);
				selectedRoster.editStudent(lastName);
			}
			break;
		default:
			cout << "Invalid option selected.\n";
	}
}

namespace RosterSystemUtils {
	int getMenuOptSelection (int start, int end) {
		string choice;
		do {
			getline (cin, choice);
		} while (choice[0] - '0' < start || choice[0] - '0' > end &&
				 choice != "q" && choice != "Q");
		int selection = choice[0] - '0';
		return selection;
	}

	char getLoginOptSelection ( ) {
		string choice;
		do {
			getline (cin, choice);
		} while (choice != "A" && choice != "B" && choice != "C" &&
				 choice != "a" && choice != "b" && choice != "c");
		return choice[0];

	}
}<|endoftext|>


<commit_before>/*************************************************************************
> File Name: Deck.cpp
> Project Name: Hearthstone++
> Author: Chan-Ho Chris Ohk
> Purpose: Deck class that stores a list of cards.
> Created Time: 2017/10/13
> Copyright (c) 2017, Chan-Ho Chris Ohk
*************************************************************************/
#include <Models/Deck.h>

#include <algorithm>
#include <iostream>

namespace Hearthstonepp
{
	Deck::Deck() :
		m_name("Empty"), m_class(CardClass::INVALID), m_numOfCards(0)
	{

	}

	Deck::Deck(std::string name, const CardClass playerClass) :
		m_name(std::move(name)), m_class(playerClass), m_numOfCards(0)
	{

	}

	std::string Deck::GetName() const
	{
		return m_name;
	}

	CardClass Deck::GetClass() const
	{
		return m_class;
	}

	unsigned int Deck::GetNumOfCards() const
	{
		return m_numOfCards;
	}

	size_t Deck::GetUniqueNumOfCards() const
	{
		return m_cards.size();
	}

	unsigned int Deck::GetNumCardInDeck(std::string cardID)
	{
		auto isCardExistInDeck = std::find_if(m_cards.begin(), m_cards.end(),
			[&cardID](const std::pair<std::string, int>& elem) { return elem.first == cardID; });

		if (isCardExistInDeck != m_cards.end())
		{
			return (*isCardExistInDeck).second;
		}

		return 0;
	}

	std::vector<const Card*> Deck::GetPrimitiveDeck() const
	{
		Cards* cards = Cards::GetInstance();

		std::vector<const Card*> deck;
		for (const auto& pair : m_cards)
		{
			const Card* card = cards->FindCardByID(pair.first);
			for (int i = 0; i < pair.second; ++i)
			{
				deck.push_back(card);
			}
		}

		return deck;
	}

	std::pair<std::string, int> Deck::GetCard(size_t idx) const
	{
		return m_cards.at(idx);
	}

	void Deck::ShowCardList() const
	{
		int idx = 1;

		for (auto& cardInfo : m_cards)
		{
			const Card* card = Cards::GetInstance()->FindCardByID(cardInfo.first);
			if (card == nullptr)
			{
				continue;
			}

			std::cout << idx << ". ";
			card->ShowBriefInfo();
			std::cout << "(" << cardInfo.second << " card(s))\n";

			idx++;
		}

	}

	int Deck::AddCard(std::string cardID, int numCardToAdd)
	{
		const Card* card = Cards::GetInstance()->FindCardByID(cardID);
		CardClass cls = card->GetCardClass();
		if (cls != GetClass() && cls != CardClass::NEUTRAL || card->GetMaxAllowedInDeck() < numCardToAdd)
		{
			return -1;
		}

		auto isCardExistInDeck = std::find_if(m_cards.begin(), m_cards.end(),
			[&cardID](const std::pair<std::string, int>& elem) { return elem.first == cardID; });

		if (isCardExistInDeck != m_cards.end()) // card is in deck
		{
			if (card->GetMaxAllowedInDeck() < (*isCardExistInDeck).second + numCardToAdd)
				return -1;

			(*isCardExistInDeck).second += numCardToAdd;
		}
		else
		{
			m_cards.emplace_back(std::make_pair(cardID, numCardToAdd));
		}

		m_numOfCards += numCardToAdd;

		return 0;
	}

	int Deck::DeleteCard(std::string cardID, const int numCardToDelete)
	{
		auto isCardExistInDeck = std::find_if(m_cards.begin(), m_cards.end(),
			[&cardID](const std::pair<std::string, int>& elem) { return elem.first == cardID; });

		if (isCardExistInDeck != m_cards.end())
		{
			if ((*isCardExistInDeck).second - numCardToDelete == 0)
			{
				m_cards.erase(isCardExistInDeck);
			}
			else if ((*isCardExistInDeck).second - numCardToDelete < 0)
			{
				return -1;
			}

			(*isCardExistInDeck).second -= numCardToDelete;
			m_numOfCards -= numCardToDelete;

			return 0;
		}

		return -1;
	}
}<commit_msg>Modi Deck.cpp - fix parentheses, sign-compare<commit_after>/*************************************************************************
> File Name: Deck.cpp
> Project Name: Hearthstone++
> Author: Chan-Ho Chris Ohk
> Purpose: Deck class that stores a list of cards.
> Created Time: 2017/10/13
> Copyright (c) 2017, Chan-Ho Chris Ohk
*************************************************************************/
#include <Models/Deck.h>

#include <algorithm>
#include <iostream>

namespace Hearthstonepp
{
	Deck::Deck() :
		m_name("Empty"), m_class(CardClass::INVALID), m_numOfCards(0)
	{

	}

	Deck::Deck(std::string name, const CardClass playerClass) :
		m_name(std::move(name)), m_class(playerClass), m_numOfCards(0)
	{

	}

	std::string Deck::GetName() const
	{
		return m_name;
	}

	CardClass Deck::GetClass() const
	{
		return m_class;
	}

	unsigned int Deck::GetNumOfCards() const
	{
		return m_numOfCards;
	}

	size_t Deck::GetUniqueNumOfCards() const
	{
		return m_cards.size();
	}

	unsigned int Deck::GetNumCardInDeck(std::string cardID)
	{
		auto isCardExistInDeck = std::find_if(m_cards.begin(), m_cards.end(),
			[&cardID](const std::pair<std::string, int>& elem) { return elem.first == cardID; });

		if (isCardExistInDeck != m_cards.end())
		{
			return (*isCardExistInDeck).second;
		}

		return 0;
	}

	std::vector<const Card*> Deck::GetPrimitiveDeck() const
	{
		Cards* cards = Cards::GetInstance();

		std::vector<const Card*> deck;
		for (const auto& pair : m_cards)
		{
			const Card* card = cards->FindCardByID(pair.first);
			for (int i = 0; i < pair.second; ++i)
			{
				deck.push_back(card);
			}
		}

		return deck;
	}

	std::pair<std::string, int> Deck::GetCard(size_t idx) const
	{
		return m_cards.at(idx);
	}

	void Deck::ShowCardList() const
	{
		int idx = 1;

		for (auto& cardInfo : m_cards)
		{
			const Card* card = Cards::GetInstance()->FindCardByID(cardInfo.first);
			if (card == nullptr)
			{
				continue;
			}

			std::cout << idx << ". ";
			card->ShowBriefInfo();
			std::cout << "(" << cardInfo.second << " card(s))\n";

			idx++;
		}

	}

	int Deck::AddCard(std::string cardID, int numCardToAdd)
	{
		const Card* card = Cards::GetInstance()->FindCardByID(cardID);
		CardClass cls = card->GetCardClass();
		if ((cls != GetClass() && cls != +CardClass::NEUTRAL) || int(card->GetMaxAllowedInDeck()) < numCardToAdd)
		{
			return -1;
		}

		auto isCardExistInDeck = std::find_if(m_cards.begin(), m_cards.end(),
			[&cardID](const std::pair<std::string, int>& elem) { return elem.first == cardID; });

		if (isCardExistInDeck != m_cards.end()) // card is in deck
		{
			if (int(card->GetMaxAllowedInDeck()) < (*isCardExistInDeck).second + numCardToAdd)
				return -1;

			(*isCardExistInDeck).second += numCardToAdd;
		}
		else
		{
			m_cards.emplace_back(std::make_pair(cardID, numCardToAdd));
		}

		m_numOfCards += numCardToAdd;

		return 0;
	}

	int Deck::DeleteCard(std::string cardID, const int numCardToDelete)
	{
		auto isCardExistInDeck = std::find_if(m_cards.begin(), m_cards.end(),
			[&cardID](const std::pair<std::string, int>& elem) { return elem.first == cardID; });

		if (isCardExistInDeck != m_cards.end())
		{
			if ((*isCardExistInDeck).second - numCardToDelete == 0)
			{
				m_cards.erase(isCardExistInDeck);
			}
			else if ((*isCardExistInDeck).second - numCardToDelete < 0)
			{
				return -1;
			}

			(*isCardExistInDeck).second -= numCardToDelete;
			m_numOfCards -= numCardToDelete;

			return 0;
		}

		return -1;
	}
}<|endoftext|>


<commit_before>#include <fstream>

#include <pxcsession.h>

#include <util/tiny_logger.hpp>

int main()
{
    PXCSession *session = PXCSession::CreateInstance();
    PXCSession::ImplVersion ver = session->QueryVersion();
    TLOG(INFO) << ver.major << "." << ver.minor;
    session->Release();

    return EXIT_SUCCESS;
}
<commit_msg>Started implementing RealSense grabber<commit_after>#include <fstream>

#ifdef _MSC_VER
    #pragma warning(push)
    #pragma warning(disable: 4309)  // truncation of const value
#endif
#include <pxcsensemanager.h>
#ifdef _MSC_VER
    #pragma warning(pop)
#endif

#include <util/tiny_logger.hpp>

int main()
{
    /*PXCSession *session = PXCSession::CreateInstance();
    PXCSession::ImplVersion ver = session->QueryVersion();
    TLOG(INFO) << ver.major << "." << ver.minor;
    session->Release();*/

    PXCSenseManager *senseManager = PXCSenseManager::CreateInstance();
    senseManager->EnableStream(Intel::RealSense::StreamType::STREAM_TYPE_COLOR, 1920, 1080, 30.0f);
    senseManager->EnableStream(Intel::RealSense::StreamType::STREAM_TYPE_DEPTH, 628, 468, 30.0f);

    senseManager->Init();

    int numFrames = 0;

    while (senseManager->AcquireFrame(true) >= PXC_STATUS_NO_ERROR)
    {
        PXCCapture::Sample *sample = senseManager->QuerySample();
        const auto colorInfo = sample->color->QueryInfo(), depthInfo = sample->depth->QueryInfo();
        senseManager->ReleaseFrame();
        ++numFrames;
        TLOG(INFO) << "Captured color frame #" << numFrames << " " << colorInfo.format << " " << colorInfo.width << " " << colorInfo.height << " " << colorInfo.reserved;
        TLOG(INFO) << "Captured depth frame #" << numFrames << " " << depthInfo.format << " " << depthInfo.width << " " << depthInfo.height << " " << depthInfo.reserved;
    }

    senseManager->Release();

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#include "stdafx.h"
#include "AutowiringDebug.h"
#include "Autowired.h"
#include <iomanip>
#include <iostream>
#include <sstream>

using namespace autowiring;
using namespace autowiring::dbg;

#ifdef _MSC_VER
// On Windows, lambda functions start with the key string "class <"
bool autowiring::dbg::IsLambda(const std::type_info& ti) {
  return demangle(ti).compare(0, 7, "class <") == 0;
}
#else
// On other platforms, try to find lambda functions by the presence of forbidden characters
bool autowiring::dbg::IsLambda(const std::type_info& ti) {
  auto name = demangle(ti);
  for (auto cur : name)
    if (
      !('a' <= cur && cur <= 'z') &&
      !('A' <= cur && cur <= 'Z') &&
      !('0' <= cur && cur <= '9') &&
      cur != '_' &&
      cur != ':' &&
      cur != '<' &&
      cur != '>' &&
      cur != ',' &&
      cur != ' '
    )
      return true;
  return false;
}
#endif

static std::string TrimPrefix(std::string name) {
  // "class" or "struct" prefixes should be eliminated
  if (name.compare(0, 6, "class "))
    name = name.substr(5);
  if (name.compare(0, 7, "struct "))
    name = name.substr(6);
  return name;
}

static std::string DemangleWithAutoID(const std::type_info& ti) {
  auto retVal = demangle(ti);

  // prefix is at the beginning of the string, skip over it
  static const char prefix [] = "struct auto_id<";
  if (retVal.compare(0, sizeof(prefix) - 1, prefix) == 0) {
    size_t off = sizeof(prefix) - 1;
    retVal = retVal.substr(off, retVal.length() - off - 2);
  }
  return TrimPrefix(retVal);
}

std::string autowiring::dbg::ContextName(void) {
  AutoCurrentContext ctxt;
  return autowiring::demangle(ctxt->GetSigilType());
}

AutoPacket* autowiring::dbg::CurrentPacket(void) {
  Autowired<AutoPacketFactory> factory;
  if (!factory)
    return nullptr;
  return factory->CurrentPacket().get();
}

static const AutoFilterDescriptor* DescriptorByName(const char* name, const std::vector<AutoFilterDescriptor>& filters) {
  for (const auto& filter : filters) {
    auto type = filter.GetType();
    if (!type)
      continue;

    auto curName = TrimPrefix(demangle(type));
    if (!curName.compare(name))
      return &filter;
  }
  return nullptr;
}

const AutoFilterDescriptor* autowiring::dbg::DescriptorByName(const char* name) {
  Autowired<AutoPacketFactory> factory;
  if (!factory)
    return nullptr;

  return DescriptorByName(name, factory->GetAutoFilters());
}

std::string autowiring::dbg::AutoFilterInfo(const char* name) {
  Autowired<AutoPacketFactory> factory;
  if (!factory)
    return "No factory detected in this context";

  // Obtain all descriptors:
  const std::vector<AutoFilterDescriptor> descs = factory->GetAutoFilters();

  // Need the root descriptor first
  const AutoFilterDescriptor* desc = DescriptorByName(name, descs);
  if (!desc)
    return "Filter not found";

  std::ostringstream os;
  std::function<void(const AutoFilterDescriptor&, int)> fnCall = [&](const AutoFilterDescriptor& desc, size_t nLevels) {
    auto args = desc.GetAutoFilterArguments();

    for (size_t i = 0; i < desc.GetArity(); i++) {
      // Argument must be an input to the filter we want to know more about
      if (!args[i].is_input)
        continue;

      // Who provides this input?
      for (const auto& providerDesc : descs) {
        auto providerArg = providerDesc.GetArgumentType(args[i].ti);
        if (providerArg && providerArg->is_output) {
          // Need to print information about this provider:
          os << demangle(*args[i].ti) << ' ' << std::string(' ', nLevels) << demangle(*providerDesc.GetType()) << std::endl;

          // The current descriptor provides an input to the parent, recurse
          fnCall(providerDesc, nLevels + 1);
        }
      }
    }
  };

  // Root typename print first:
  os << demangle(desc->GetType()) << std::endl;
  fnCall(*desc, 1);

  auto retVal = os.str();
  return retVal;
}

std::vector<std::string> autowiring::dbg::ListRootDecorations(void) {
  Autowired<AutoPacketFactory> factory;
  if (!factory)
    return std::vector<std::string>{};

  // Obtain all descriptors:
  const std::vector<AutoFilterDescriptor> descs = factory->GetAutoFilters();

  // Get all baseline descriptor types, figure out what isn't satisfied:
  std::unordered_map<std::type_index, const std::type_info*> outputs;
  std::unordered_map<std::type_index, const std::type_info*> inputs;

  for (const auto& desc : descs) {
    auto args = desc.GetAutoFilterArguments();
    for (size_t i = 0; i < desc.GetArity(); i++)
      (args[i].is_output ? outputs : inputs)[*args[i].ti] = args[i].ti;
  }

  // Remove all inputs that exist in the outputs set:
  for (auto& output : outputs)
    inputs.erase(*output.second);

  // Any remaining inputs are unsatisfied
  std::vector<std::string> retVal;
  for (auto& input : inputs)
    if (input.second)
      retVal.push_back(DemangleWithAutoID(*input.second));
  return retVal;
}

void autowiring::dbg::DebugInit(void) {

}<commit_msg>Make TrimPrefix a noop on mac<commit_after>// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#include "stdafx.h"
#include "AutowiringDebug.h"
#include "Autowired.h"
#include <iomanip>
#include <iostream>
#include <sstream>

using namespace autowiring;
using namespace autowiring::dbg;

#ifdef _MSC_VER
// On Windows, lambda functions start with the key string "class <"
bool autowiring::dbg::IsLambda(const std::type_info& ti) {
  return demangle(ti).compare(0, 7, "class <") == 0;
}
#else
// On other platforms, try to find lambda functions by the presence of forbidden characters
bool autowiring::dbg::IsLambda(const std::type_info& ti) {
  auto name = demangle(ti);
  for (auto cur : name)
    if (
      !('a' <= cur && cur <= 'z') &&
      !('A' <= cur && cur <= 'Z') &&
      !('0' <= cur && cur <= '9') &&
      cur != '_' &&
      cur != ':' &&
      cur != '<' &&
      cur != '>' &&
      cur != ',' &&
      cur != ' '
    )
      return true;
  return false;
}
#endif

static std::string TrimPrefix(std::string name) {
#ifdef _MSC_VER
  // "class" or "struct" prefixes should be eliminated
  if (name.compare(0, 6, "class "))
    name = name.substr(5);
  if (name.compare(0, 7, "struct "))
    name = name.substr(6);
#endif

  return name;
}

static std::string DemangleWithAutoID(const std::type_info& ti) {
  auto retVal = demangle(ti);

  // prefix is at the beginning of the string, skip over it
  static const char prefix [] = "struct auto_id<";
  if (retVal.compare(0, sizeof(prefix) - 1, prefix) == 0) {
    size_t off = sizeof(prefix) - 1;
    retVal = retVal.substr(off, retVal.length() - off - 2);
  }
  return TrimPrefix(retVal);
}

std::string autowiring::dbg::ContextName(void) {
  AutoCurrentContext ctxt;
  return autowiring::demangle(ctxt->GetSigilType());
}

AutoPacket* autowiring::dbg::CurrentPacket(void) {
  Autowired<AutoPacketFactory> factory;
  if (!factory)
    return nullptr;
  return factory->CurrentPacket().get();
}

static const AutoFilterDescriptor* DescriptorByName(const char* name, const std::vector<AutoFilterDescriptor>& filters) {
  for (const auto& filter : filters) {
    auto type = filter.GetType();
    if (!type)
      continue;

    auto curName = TrimPrefix(demangle(type));
    if (!curName.compare(name))
      return &filter;
  }
  return nullptr;
}

const AutoFilterDescriptor* autowiring::dbg::DescriptorByName(const char* name) {
  Autowired<AutoPacketFactory> factory;
  if (!factory)
    return nullptr;

  return DescriptorByName(name, factory->GetAutoFilters());
}

std::string autowiring::dbg::AutoFilterInfo(const char* name) {
  Autowired<AutoPacketFactory> factory;
  if (!factory)
    return "No factory detected in this context";

  // Obtain all descriptors:
  const std::vector<AutoFilterDescriptor> descs = factory->GetAutoFilters();

  // Need the root descriptor first
  const AutoFilterDescriptor* desc = DescriptorByName(name, descs);
  if (!desc)
    return "Filter not found";

  std::ostringstream os;
  std::function<void(const AutoFilterDescriptor&, int)> fnCall = [&](const AutoFilterDescriptor& desc, size_t nLevels) {
    auto args = desc.GetAutoFilterArguments();

    for (size_t i = 0; i < desc.GetArity(); i++) {
      // Argument must be an input to the filter we want to know more about
      if (!args[i].is_input)
        continue;

      // Who provides this input?
      for (const auto& providerDesc : descs) {
        auto providerArg = providerDesc.GetArgumentType(args[i].ti);
        if (providerArg && providerArg->is_output) {
          // Need to print information about this provider:
          os << demangle(*args[i].ti) << ' ' << std::string(' ', nLevels) << demangle(*providerDesc.GetType()) << std::endl;

          // The current descriptor provides an input to the parent, recurse
          fnCall(providerDesc, nLevels + 1);
        }
      }
    }
  };

  // Root typename print first:
  os << demangle(desc->GetType()) << std::endl;
  fnCall(*desc, 1);

  auto retVal = os.str();
  return retVal;
}

std::vector<std::string> autowiring::dbg::ListRootDecorations(void) {
  Autowired<AutoPacketFactory> factory;
  if (!factory)
    return std::vector<std::string>{};

  // Obtain all descriptors:
  const std::vector<AutoFilterDescriptor> descs = factory->GetAutoFilters();

  // Get all baseline descriptor types, figure out what isn't satisfied:
  std::unordered_map<std::type_index, const std::type_info*> outputs;
  std::unordered_map<std::type_index, const std::type_info*> inputs;

  for (const auto& desc : descs) {
    auto args = desc.GetAutoFilterArguments();
    for (size_t i = 0; i < desc.GetArity(); i++)
      (args[i].is_output ? outputs : inputs)[*args[i].ti] = args[i].ti;
  }

  // Remove all inputs that exist in the outputs set:
  for (auto& output : outputs)
    inputs.erase(*output.second);

  // Any remaining inputs are unsatisfied
  std::vector<std::string> retVal;
  for (auto& input : inputs)
    if (input.second)
      retVal.push_back(DemangleWithAutoID(*input.second));
  return retVal;
}

void autowiring::dbg::DebugInit(void) {

}<|endoftext|>


<commit_before>/*****************************************************************************
  mergeBed.cpp

  (c) 2009 - Aaron Quinlan
  Hall Laboratory
  Department of Biochemistry and Molecular Genetics
  University of Virginia
  aaronquinlan@gmail.com

  Licenced under the GNU General Public License 2.0+ license.
******************************************************************************/
#include "lineFileUtilities.h"
#include "mergeBed.h"

// ===============
// = Constructor =
// ===============
BedMerge::BedMerge(string &bedFile, bool &numEntries, int &maxDistance, bool &forceStrand, bool &reportNames) {

	this->bedFile = bedFile;
	this->numEntries = numEntries;
	this->maxDistance = -1 * maxDistance;
	this->forceStrand = forceStrand;
	this->reportNames = reportNames;

	this->bed = new BedFile(bedFile);
}

// =================
// =   Destructor  =
// =================
BedMerge::~BedMerge(void) {
}

// =====================================================
// = Merge overlapping BED entries into a single entry =
// =====================================================
void BedMerge::MergeBed() {

	// load the "B" bed file into a map so
	// that we can easily compare "A" to it for overlaps
	bed->loadBedFileIntoMapNoBin();

	// loop through each chromosome and merge their BED entries
	for (masterBedMapNoBin::iterator m = bed->bedMapNoBin.begin(); m != bed->bedMapNoBin.end(); ++m) {

		// bedList is already sorted by start position.
		vector<BED> bedList = m->second;

		int minStart = INT_MAX;
		int maxEnd = 0;
		bool OIP = false;       // OIP = Overlap In Progress.  Lame, I realize.
		unsigned int prev = 0;
		unsigned int curr = 0;
		int mergeCount = 1;
		vector<string> names;

		// loop through the BED entries for this chromosome
		// and look for overlaps
		for (curr = 1; curr < bedList.size(); ++curr) {

			// Is there an overlap between the current and previous entries?
			if ( overlaps(bedList[prev].start, bedList[prev].end,
			 			bedList[curr].start, bedList[curr].end) >= this->maxDistance) {

				OIP = true;
				mergeCount++;
				minStart = min(bedList[prev].start, min(minStart, bedList[curr].start));
				maxEnd = max(bedList[prev].end, max(maxEnd, bedList[curr].end));

				//names.push_back(bedList[prev].name);
				names.push_back(bedList[curr].name);
			}
			else if ( overlaps(minStart, maxEnd,
							bedList[curr].start, bedList[curr].end) >= this->maxDistance) {
				mergeCount++;
				minStart = min(minStart, bedList[curr].start);
				maxEnd = max(maxEnd, bedList[curr].end);

				names.push_back(bedList[curr].name);
			}
			else {

				// was there an overlap befor the current entry broke it?
				if (OIP) {
					if (this->numEntries) {
						cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t" << mergeCount << endl;
					}
					else if (this->reportNames) {
						cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t";
						for (unsigned int n = 0; n < names.size(); ++n) {
							if (n < (names.size() - 1)) {cout << names[n] << ";";}
							else {cout << names[n];}
						}
						cout << endl;
					}
					else {
						cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << endl;
					}
				}
				else {
					if (this->numEntries) {
						cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << mergeCount << endl;
					}
					else if (this->reportNames) {
						cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << bedList[prev].name << endl;
					}
					else {
						cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << endl;
					}
				}

				// reset things for the next overlapping "block"
				OIP = false;
				mergeCount = 1;
				minStart = INT_MAX;
				maxEnd = 0;
				names.clear();
				names.push_back(bedList[prev].name);
			}
			prev = curr;
		}

		// clean up based on the last entry for the current chromosome
		if (OIP) {
			if (this->numEntries) {
				cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t" << mergeCount << endl;
			}
			else if (this->reportNames) {
				cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t";
				for (unsigned int n = 0; n < names.size(); ++n) {
					if (n < (names.size() - 1)) {cout << names[n] << ";";}
					else {cout << names[n];}
				}
				cout << endl;
			}
			else {
				cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << endl;
			}
		}
		else {
			if (this->numEntries) {
				cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end  << "\t" << mergeCount << endl;
			}
			else if (this->reportNames) {
				cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << bedList[prev].name << endl;
			}
			else {
				cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << endl;
			}
		}
	}
}

// ==================================================================================
// = Merge overlapping BED entries into a single entry, accounting for strandedness =
// ==================================================================================
void BedMerge::MergeBedStranded() {

	// load the "B" bed file into a map so
	// that we can easily compare "A" to it for overlaps
	bed->loadBedFileIntoMapNoBin();

	// loop through each chromosome and merge their BED entries
	for (masterBedMapNoBin::iterator m = bed->bedMapNoBin.begin(); m != bed->bedMapNoBin.end(); ++m) {

		// bedList is already sorted by start position.
		vector<BED> bedList = m->second;

		// make a list of the two strands to merge separately.
		vector<string> strands(2);
		strands[0] = "+";
		strands[1] = "-";

		// do two passes, one for each strand.
		for (unsigned int s = 0; s < strands.size(); s++) {

			int minStart = INT_MAX;
			int maxEnd = 0;
			bool OIP = false;       // OIP = Overlap In Progress.  Lame, I realize.
			int prev = -1;
			unsigned int curr = 0;
			int mergeCount = 1;
			int numOnStrand = 0;
			vector<string> names;

			// loop through the BED entries for this chromosome
			// and look for overlaps
			for (curr = 0; curr < bedList.size(); ++curr) {

				// if forcing strandedness, move on if the hit
				// is not on the current strand.

				if (bedList[curr].strand != strands[s]) {
					continue;		// continue force the next iteration of the for loop.
				}
				else {
					numOnStrand++;
				}

				// make sure prev points to an actual element on the
				// current strand
				if (prev < 0) {
					if (bedList[curr].strand == strands[s]) {
						prev = curr;
					}
					continue;
				}

				if ( overlaps(bedList[prev].start, bedList[prev].end,
				 			bedList[curr].start, bedList[curr].end) >= this->maxDistance) {

					OIP = true;
					mergeCount++;
					minStart = min(bedList[prev].start, min(minStart, bedList[curr].start));
					maxEnd = max(bedList[prev].end, max(maxEnd, bedList[curr].end));

					names.push_back(bedList[curr].name);
				}
				else if ( overlaps(minStart, maxEnd,
								bedList[curr].start, bedList[curr].end) >= this->maxDistance) {

					mergeCount++;
					minStart = min(minStart, bedList[curr].start);
					maxEnd = max(maxEnd, bedList[curr].end);

					names.push_back(bedList[curr].name);
				}
				else {

					// was there an overlap before the current entry broke it?
					if (OIP) {
						if (this->numEntries) {
							cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t" << mergeCount << "\t" << strands[s] << endl;
						}
						else if (this->reportNames) {
							cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t";
							for (unsigned int n = 0; n < names.size(); ++n) {
								if (n < (names.size() - 1)) {cout << names[n] << ";";}
								else {cout << names[n];}
							}
							cout << endl;
						}
						else {
							cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t" << strands[s] << endl;
						}
					}
					else {
						if ((this->numEntries) && (numOnStrand > 0)) {
							cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << mergeCount << "\t" << strands[s] << endl;
						}
						else if (this->reportNames) {
							cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << bedList[prev].name << endl;
						}
						else if (numOnStrand > 0) {
							cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << strands[s] << endl;
						}
					}

					// reset things for the next overlapping "block"
					OIP = false;
					mergeCount = 1;
					minStart = INT_MAX;
					maxEnd = 0;
					names.clear();

					// add the name of the
					names.push_back(bedList[prev].name);
				}
				prev = curr;
			}

			// clean up based on the last entry for the current chromosome
			if (OIP) {
				if (this->numEntries) {
					cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t" << mergeCount << "\t" << strands[s] << endl;
				}
				else if (this->reportNames) {
					cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t";
					for (unsigned int n = 0; n < names.size(); ++n) {
						if (n < (names.size() - 1)) {cout << names[n] << ";";}
						else {cout << names[n];}
					}
					cout << endl;
				}
				else {
					cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t" << strands[s] << endl;
				}
			}
			else {
				if ((this->numEntries) && (numOnStrand > 0)) {
					cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << mergeCount << "\t" << strands[s] << endl;
				}
				else if (this->reportNames) {
					cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << bedList[prev].name << endl;
				}
				else if (numOnStrand > 0) {
					cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << strands[s] << endl;
				}
			}
		}
	}
}
<commit_msg>Fixed bug in mergeBed that caused segfault when -nms and -s used together.<commit_after>/*****************************************************************************
  mergeBed.cpp

  (c) 2009 - Aaron Quinlan
  Hall Laboratory
  Department of Biochemistry and Molecular Genetics
  University of Virginia
  aaronquinlan@gmail.com

  Licenced under the GNU General Public License 2.0+ license.
******************************************************************************/
#include "lineFileUtilities.h"
#include "mergeBed.h"

// ===============
// = Constructor =
// ===============
BedMerge::BedMerge(string &bedFile, bool &numEntries, int &maxDistance, bool &forceStrand, bool &reportNames) {

	this->bedFile = bedFile;
	this->numEntries = numEntries;
	this->maxDistance = -1 * maxDistance;
	this->forceStrand = forceStrand;
	this->reportNames = reportNames;

	this->bed = new BedFile(bedFile);
}

// =================
// =   Destructor  =
// =================
BedMerge::~BedMerge(void) {
}

// =====================================================
// = Merge overlapping BED entries into a single entry =
// =====================================================
void BedMerge::MergeBed() {

	// load the "B" bed file into a map so
	// that we can easily compare "A" to it for overlaps
	bed->loadBedFileIntoMapNoBin();

	// loop through each chromosome and merge their BED entries
	for (masterBedMapNoBin::iterator m = bed->bedMapNoBin.begin(); m != bed->bedMapNoBin.end(); ++m) {

		// bedList is already sorted by start position.
		vector<BED> bedList = m->second;

		int minStart = INT_MAX;
		int maxEnd = 0;
		bool OIP = false;       // OIP = Overlap In Progress.  Lame, I realize.
		unsigned int prev = 0;
		unsigned int curr = 0;
		int mergeCount = 1;
		vector<string> names;

		// loop through the BED entries for this chromosome
		// and look for overlaps
		for (curr = 1; curr < bedList.size(); ++curr) {

			// Is there an overlap between the current and previous entries?
			if ( overlaps(bedList[prev].start, bedList[prev].end,
			 			bedList[curr].start, bedList[curr].end) >= this->maxDistance) {

				OIP = true;
				mergeCount++;
				minStart = min(bedList[prev].start, min(minStart, bedList[curr].start));
				maxEnd = max(bedList[prev].end, max(maxEnd, bedList[curr].end));

				//names.push_back(bedList[prev].name);
				names.push_back(bedList[curr].name);
			}
			else if ( overlaps(minStart, maxEnd,
							bedList[curr].start, bedList[curr].end) >= this->maxDistance) {
				mergeCount++;
				minStart = min(minStart, bedList[curr].start);
				maxEnd = max(maxEnd, bedList[curr].end);

				names.push_back(bedList[curr].name);
			}
			else {

				// was there an overlap befor the current entry broke it?
				if (OIP) {
					if (this->numEntries) {
						cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t" << mergeCount << endl;
					}
					else if (this->reportNames) {
						cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t";
						for (unsigned int n = 0; n < names.size(); ++n) {
							if (n < (names.size() - 1)) {cout << names[n] << ";";}
							else {cout << names[n];}
						}
						cout << endl;
					}
					else {
						cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << endl;
					}
				}
				else {
					if (this->numEntries) {
						cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << mergeCount << endl;
					}
					else if (this->reportNames) {
						cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << bedList[prev].name << endl;
					}
					else {
						cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << endl;
					}
				}

				// reset things for the next overlapping "block"
				OIP = false;
				mergeCount = 1;
				minStart = INT_MAX;
				maxEnd = 0;
				names.clear();
				names.push_back(bedList[prev].name);
			}
			prev = curr;
		}

		// clean up based on the last entry for the current chromosome
		if (OIP) {
			if (this->numEntries) {
				cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t" << mergeCount << endl;
			}
			else if (this->reportNames) {
				cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t";
				for (unsigned int n = 0; n < names.size(); ++n) {
					if (n < (names.size() - 1)) {cout << names[n] << ";";}
					else {cout << names[n];}
				}
				cout << endl;
			}
			else {
				cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << endl;
			}
		}
		else {
			if (this->numEntries) {
				cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end  << "\t" << mergeCount << endl;
			}
			else if (this->reportNames) {
				cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << bedList[prev].name << endl;
			}
			else {
				cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << endl;
			}
		}
	}
}

// ==================================================================================
// = Merge overlapping BED entries into a single entry, accounting for strandedness =
// ==================================================================================
void BedMerge::MergeBedStranded() {

	// load the "B" bed file into a map so
	// that we can easily compare "A" to it for overlaps
	bed->loadBedFileIntoMapNoBin();

	// loop through each chromosome and merge their BED entries
	for (masterBedMapNoBin::iterator m = bed->bedMapNoBin.begin(); m != bed->bedMapNoBin.end(); ++m) {

		// bedList is already sorted by start position.
		vector<BED> bedList = m->second;

		// make a list of the two strands to merge separately.
		vector<string> strands(2);
		strands[0] = "+";
		strands[1] = "-";

		// do two passes, one for each strand.
		for (unsigned int s = 0; s < strands.size(); s++) {

			int minStart = INT_MAX;
			int maxEnd = 0;
			bool OIP = false;       // OIP = Overlap In Progress.  Lame, I realize.
			int prev = -1;
			unsigned int curr = 0;
			int mergeCount = 1;
			int numOnStrand = 0;
			vector<string> names;

			// loop through the BED entries for this chromosome
			// and look for overlaps
			for (curr = 0; curr < bedList.size(); ++curr) {

				// if forcing strandedness, move on if the hit
				// is not on the current strand.

				if (bedList[curr].strand != strands[s]) {
					continue;		// continue force the next iteration of the for loop.
				}
				else {
					numOnStrand++;
				}

				// make sure prev points to an actual element on the
				// current strand
				if (prev < 0) {
					if (bedList[curr].strand == strands[s]) {
						prev = curr;
					}
					continue;
				}

				if ( overlaps(bedList[prev].start, bedList[prev].end,
				 			bedList[curr].start, bedList[curr].end) >= this->maxDistance) {

					OIP = true;
					mergeCount++;
					minStart = min(bedList[prev].start, min(minStart, bedList[curr].start));
					maxEnd = max(bedList[prev].end, max(maxEnd, bedList[curr].end));

					names.push_back(bedList[curr].name);
				}
				else if ( overlaps(minStart, maxEnd,
								bedList[curr].start, bedList[curr].end) >= this->maxDistance) {

					mergeCount++;
					minStart = min(minStart, bedList[curr].start);
					maxEnd = max(maxEnd, bedList[curr].end);

					names.push_back(bedList[curr].name);
				}
				else {

					// was there an overlap before the current entry broke it?
					if (OIP) {
						if (this->numEntries) {
							cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t" << mergeCount << "\t" << strands[s] << endl;
						}
						else if (this->reportNames) {
							cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t";
							for (unsigned int n = 0; n < names.size(); ++n) {
								if (n < (names.size() - 1)) {cout << names[n] << ";";}
								else {cout << names[n];}
							}
							cout << "\t" << strands[s] << endl;
						}
						else {
							cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t" << strands[s] << endl;
						}
					}
					else {
						if ((this->numEntries) && (numOnStrand > 0)) {
							cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << mergeCount << "\t" << strands[s] << endl;
						}
						else if (this->reportNames) {
							cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << bedList[prev].name << "\t" << strands[s] << endl;
						}
						else if (numOnStrand > 0) {
							cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << strands[s] << endl;
						}
					}

					// reset things for the next overlapping "block"
					OIP = false;
					mergeCount = 1;
					minStart = INT_MAX;
					maxEnd = 0;
					names.clear();

					// add the name of the
					names.push_back(bedList[prev].name);
				}
				prev = curr;
			}

			// clean up based on the last entry for the current chromosome
			if (OIP) {
				if (this->numEntries) {
					cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t" << mergeCount << "\t" << strands[s] << endl;
				}
				else if (this->reportNames) {
					cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t";
					for (unsigned int n = 0; n < names.size(); ++n) {
						if (n < (names.size() - 1)) {cout << names[n] << ";";}
						else {cout << names[n];}
					}
					cout << "\t" << strands[s] << endl;
				}
				else {
					cout << bedList[prev].chrom << "\t" << minStart << "\t" << maxEnd << "\t" << strands[s] << endl;
				}
			}
			else {
				if ((this->numEntries) && (numOnStrand > 0)) {
					cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << mergeCount << "\t" << strands[s] << endl;
				}
				else if ((this->reportNames) && (numOnStrand > 0)) {
					cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << bedList[prev].name << "\t" << strands[s] << endl;
				}
				else if (numOnStrand > 0) {
					cout << bedList[prev].chrom << "\t" << bedList[prev].start << "\t" << bedList[prev].end << "\t" << strands[s] << endl;
				}
			}
		}
	}
}
<|endoftext|>