<commit_before>// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp

#include <NDK/BaseWidget.hpp>
#include <NDK/Canvas.hpp>
#include <NDK/Components/GraphicsComponent.hpp>
#include <NDK/Components/NodeComponent.hpp>
#include <NDK/World.hpp>
#include <algorithm>

namespace Ndk
{
	/*!
	* \ingroup NDK
	* \class Ndk::BaseWidget
	* \brief Abstract class serving as a base class for all widgets
	*/

	/*!
	* \brief Constructs a BaseWidget object using another widget as its parent
	*
	* \param parent Parent widget, must be valid and attached to a canvas
	*
	* Constructs a BaseWidget object using another widget as a base.
	* This will also register the widget to the canvas owning the top-most widget.
	*/
	BaseWidget::BaseWidget(BaseWidget* parent) :
	BaseWidget()
	{
		NazaraAssert(parent, "Invalid parent");
		NazaraAssert(parent->GetCanvas(), "Parent has no canvas");

		m_canvas = parent->GetCanvas();
		m_widgetParent = parent;
		m_world = m_canvas->GetWorld();

		RegisterToCanvas();
	}

	/*!
	* \brief Frees the widget, unregistering it from its canvas
	*/
	BaseWidget::~BaseWidget()
	{
		UnregisterFromCanvas();
	}

	/*!
	* \brief Clears keyboard focus if and only if this widget owns it.
	*/
	void BaseWidget::ClearFocus()
	{
		if (IsRegisteredToCanvas())
			m_canvas->ClearKeyboardOwner(m_canvasIndex);
	}

	/*!
	* \brief Destroy the widget, deleting it in the process.
	*
	* Calling this function immediately destroys the widget, freeing its memory.
	*/
	void BaseWidget::Destroy()
	{
		NazaraAssert(this != m_canvas, "Canvas cannot be destroyed by calling Destroy()");

		m_widgetParent->DestroyChild(this); //< This does delete us
	}

	/*!
	* \brief Enable or disables the widget background.
	*/
	void BaseWidget::EnableBackground(bool enable)
	{
		if (m_backgroundEntity.IsValid() == enable)
			return;

		if (enable)
		{
			m_backgroundSprite = Nz::Sprite::New();
			m_backgroundSprite->SetColor(m_backgroundColor);
			m_backgroundSprite->SetMaterial(Nz::Material::New((m_backgroundColor.IsOpaque()) ? "Basic2D" : "Translucent2D")); //< TODO: Use a shared material instead of creating one everytime

			m_backgroundEntity = CreateEntity();
			m_backgroundEntity->AddComponent<GraphicsComponent>().Attach(m_backgroundSprite, -1);
			m_backgroundEntity->AddComponent<NodeComponent>().SetParent(this);

			BaseWidget::Layout(); // Only layout background
		}
		else
		{
			DestroyEntity(m_backgroundEntity);
			m_backgroundEntity.Reset();
			m_backgroundSprite.Reset();
		}
	}

	/*!
	* \brief Checks if this widget has keyboard focus
	* \return true if widget has keyboard focus, false otherwise
	*/
	bool BaseWidget::HasFocus() const
	{
		if (!IsRegisteredToCanvas())
			return false;

		return m_canvas->IsKeyboardOwner(m_canvasIndex);
	}

	void BaseWidget::Resize(const Nz::Vector2f& size)
	{
		// Adjust new size
		Nz::Vector2f newSize = size;
		newSize.Maximize(m_minimumSize);
		newSize.Minimize(m_maximumSize);

		NotifyParentResized(newSize);
		m_size = newSize;

		Layout();
	}

	void BaseWidget::SetBackgroundColor(const Nz::Color& color)
	{
		m_backgroundColor = color;

		if (m_backgroundSprite)
		{
			m_backgroundSprite->SetColor(color);
			m_backgroundSprite->GetMaterial()->Configure((color.IsOpaque()) ? "Basic2D" : "Translucent2D"); //< Our sprite has its own material (see EnableBackground)
		}
	}

	void BaseWidget::SetCursor(Nz::SystemCursor systemCursor)
	{
		m_cursor = systemCursor;

		if (IsRegisteredToCanvas())
			m_canvas->NotifyWidgetCursorUpdate(m_canvasIndex);
	}

	void BaseWidget::SetFocus()
	{
		if (IsRegisteredToCanvas())
			m_canvas->SetKeyboardOwner(m_canvasIndex);
	}

	void BaseWidget::SetParent(BaseWidget* widget)
	{
		Canvas* oldCanvas = m_canvas;
		Canvas* newCanvas = widget->GetCanvas();

		// Changing a widget canvas is a problem because of the canvas entities
		NazaraAssert(oldCanvas == newCanvas, "Transferring a widget between canvas is not yet supported");

		Node::SetParent(widget);
		m_widgetParent = widget;

		Layout();
	}

	void BaseWidget::SetRenderingRect(const Nz::Rectf& renderingRect)
	{
		m_renderingRect = renderingRect;

		UpdatePositionAndSize();
	}

	void BaseWidget::Show(bool show)
	{
		if (m_visible != show)
		{
			m_visible = show;

			if (m_visible)
				RegisterToCanvas();
			else
				UnregisterFromCanvas();

			for (WidgetEntity& entity : m_entities)
			{
				if (entity.isEnabled)
				{
					entity.handle->Enable(show); //< This will override isEnabled
					entity.isEnabled = true;
				}
			}

			for (const auto& widgetPtr : m_children)
				widgetPtr->Show(show);
		}
	}

	const EntityHandle& BaseWidget::CreateEntity()
	{
		const EntityHandle& newEntity = m_world->CreateEntity();
		newEntity->Enable(m_visible);

		m_entities.emplace_back();
		WidgetEntity& newWidgetEntity = m_entities.back();
		newWidgetEntity.handle = newEntity;
		newWidgetEntity.onDisabledSlot.Connect(newEntity->OnEntityDisabled, [this](Entity* entity)
		{
			auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; });
			NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget");

			it->isEnabled = false;
		});

		newWidgetEntity.onEnabledSlot.Connect(newEntity->OnEntityEnabled, [this](Entity* entity)
		{
			auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; });
			NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget");

			it->isEnabled = true;
		});

		return newEntity;
	}

	void BaseWidget::DestroyEntity(Entity* entity)
	{
		auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; });
		NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget");

		m_entities.erase(it);
	}

	void BaseWidget::Layout()
	{
		if (m_backgroundSprite)
			m_backgroundSprite->SetSize(m_size.x, m_size.y);

		UpdatePositionAndSize();
	}

	void BaseWidget::InvalidateNode()
	{
		Node::InvalidateNode();

		UpdatePositionAndSize();
	}

	bool BaseWidget::IsFocusable() const
	{
		return false;
	}

	void BaseWidget::OnFocusLost()
	{
	}

	void BaseWidget::OnFocusReceived()
	{
	}

	bool BaseWidget::OnKeyPressed(const Nz::WindowEvent::KeyEvent& key)
	{
		return false;
	}

	void BaseWidget::OnKeyReleased(const Nz::WindowEvent::KeyEvent& /*key*/)
	{
	}

	void BaseWidget::OnMouseEnter()
	{
	}

	void BaseWidget::OnMouseMoved(int /*x*/, int /*y*/, int /*deltaX*/, int /*deltaY*/)
	{
	}

	void BaseWidget::OnMouseButtonPress(int /*x*/, int /*y*/, Nz::Mouse::Button /*button*/)
	{
	}

	void BaseWidget::OnMouseButtonRelease(int /*x*/, int /*y*/, Nz::Mouse::Button /*button*/)
	{
	}

	void BaseWidget::OnMouseWheelMoved(int /*x*/, int /*y*/, float /*delta*/)
	{
	}

	void BaseWidget::OnMouseExit()
	{
	}

	void BaseWidget::OnParentResized(const Nz::Vector2f& /*newSize*/)
	{
	}

	void BaseWidget::OnTextEntered(char32_t /*character*/, bool /*repeated*/)
	{
	}

	void BaseWidget::DestroyChild(BaseWidget* widget)
	{
		auto it = std::find_if(m_children.begin(), m_children.end(), [widget] (const std::unique_ptr<BaseWidget>& widgetPtr) -> bool
		{
			return widgetPtr.get() == widget;
		});

		NazaraAssert(it != m_children.end(), "Child widget not found in parent");

		m_children.erase(it);
	}

	void BaseWidget::DestroyChildren()
	{
		m_children.clear();
	}

	void BaseWidget::RegisterToCanvas()
	{
		NazaraAssert(!IsRegisteredToCanvas(), "Widget is already registered to canvas");

		m_canvasIndex = m_canvas->RegisterWidget(this);
	}

	void BaseWidget::UnregisterFromCanvas()
	{
		if (IsRegisteredToCanvas())
		{
			m_canvas->UnregisterWidget(m_canvasIndex);
			m_canvasIndex = InvalidCanvasIndex;
		}
	}

	void BaseWidget::UpdatePositionAndSize()
	{
		if (IsRegisteredToCanvas())
			m_canvas->NotifyWidgetBoxUpdate(m_canvasIndex);

		Nz::Vector2f widgetPos = Nz::Vector2f(GetPosition());
		Nz::Vector2f widgetSize = GetSize();

		Nz::Rectf widgetRect(widgetPos.x, widgetPos.y, widgetSize.x, widgetSize.y);
		Nz::Rectf widgetRenderingRect(widgetPos.x + m_renderingRect.x, widgetPos.y + m_renderingRect.y, m_renderingRect.width, m_renderingRect.height);

		Nz::Rectf widgetBounds;
		widgetRect.Intersect(widgetRenderingRect, &widgetBounds);

		Nz::Recti fullBounds(widgetBounds);
		for (WidgetEntity& widgetEntity : m_entities)
		{
			const Ndk::EntityHandle& entity = widgetEntity.handle;
			if (entity->HasComponent<GraphicsComponent>())
				entity->GetComponent<GraphicsComponent>().SetScissorRect(fullBounds);
		}
	}
}
<commit_msg>SDK/BaseWidget: Fix entity activation of disabled widgets<commit_after>// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp

#include <NDK/BaseWidget.hpp>
#include <NDK/Canvas.hpp>
#include <NDK/Components/GraphicsComponent.hpp>
#include <NDK/Components/NodeComponent.hpp>
#include <NDK/World.hpp>
#include <algorithm>

namespace Ndk
{
	/*!
	* \ingroup NDK
	* \class Ndk::BaseWidget
	* \brief Abstract class serving as a base class for all widgets
	*/

	/*!
	* \brief Constructs a BaseWidget object using another widget as its parent
	*
	* \param parent Parent widget, must be valid and attached to a canvas
	*
	* Constructs a BaseWidget object using another widget as a base.
	* This will also register the widget to the canvas owning the top-most widget.
	*/
	BaseWidget::BaseWidget(BaseWidget* parent) :
	BaseWidget()
	{
		NazaraAssert(parent, "Invalid parent");
		NazaraAssert(parent->GetCanvas(), "Parent has no canvas");

		m_canvas = parent->GetCanvas();
		m_widgetParent = parent;
		m_world = m_canvas->GetWorld();

		RegisterToCanvas();
	}

	/*!
	* \brief Frees the widget, unregistering it from its canvas
	*/
	BaseWidget::~BaseWidget()
	{
		UnregisterFromCanvas();
	}

	/*!
	* \brief Clears keyboard focus if and only if this widget owns it.
	*/
	void BaseWidget::ClearFocus()
	{
		if (IsRegisteredToCanvas())
			m_canvas->ClearKeyboardOwner(m_canvasIndex);
	}

	/*!
	* \brief Destroy the widget, deleting it in the process.
	*
	* Calling this function immediately destroys the widget, freeing its memory.
	*/
	void BaseWidget::Destroy()
	{
		NazaraAssert(this != m_canvas, "Canvas cannot be destroyed by calling Destroy()");

		m_widgetParent->DestroyChild(this); //< This does delete us
	}

	/*!
	* \brief Enable or disables the widget background.
	*/
	void BaseWidget::EnableBackground(bool enable)
	{
		if (m_backgroundEntity.IsValid() == enable)
			return;

		if (enable)
		{
			m_backgroundSprite = Nz::Sprite::New();
			m_backgroundSprite->SetColor(m_backgroundColor);
			m_backgroundSprite->SetMaterial(Nz::Material::New((m_backgroundColor.IsOpaque()) ? "Basic2D" : "Translucent2D")); //< TODO: Use a shared material instead of creating one everytime

			m_backgroundEntity = CreateEntity();
			m_backgroundEntity->AddComponent<GraphicsComponent>().Attach(m_backgroundSprite, -1);
			m_backgroundEntity->AddComponent<NodeComponent>().SetParent(this);

			BaseWidget::Layout(); // Only layout background
		}
		else
		{
			DestroyEntity(m_backgroundEntity);
			m_backgroundEntity.Reset();
			m_backgroundSprite.Reset();
		}
	}

	/*!
	* \brief Checks if this widget has keyboard focus
	* \return true if widget has keyboard focus, false otherwise
	*/
	bool BaseWidget::HasFocus() const
	{
		if (!IsRegisteredToCanvas())
			return false;

		return m_canvas->IsKeyboardOwner(m_canvasIndex);
	}

	void BaseWidget::Resize(const Nz::Vector2f& size)
	{
		// Adjust new size
		Nz::Vector2f newSize = size;
		newSize.Maximize(m_minimumSize);
		newSize.Minimize(m_maximumSize);

		NotifyParentResized(newSize);
		m_size = newSize;

		Layout();
	}

	void BaseWidget::SetBackgroundColor(const Nz::Color& color)
	{
		m_backgroundColor = color;

		if (m_backgroundSprite)
		{
			m_backgroundSprite->SetColor(color);
			m_backgroundSprite->GetMaterial()->Configure((color.IsOpaque()) ? "Basic2D" : "Translucent2D"); //< Our sprite has its own material (see EnableBackground)
		}
	}

	void BaseWidget::SetCursor(Nz::SystemCursor systemCursor)
	{
		m_cursor = systemCursor;

		if (IsRegisteredToCanvas())
			m_canvas->NotifyWidgetCursorUpdate(m_canvasIndex);
	}

	void BaseWidget::SetFocus()
	{
		if (IsRegisteredToCanvas())
			m_canvas->SetKeyboardOwner(m_canvasIndex);
	}

	void BaseWidget::SetParent(BaseWidget* widget)
	{
		Canvas* oldCanvas = m_canvas;
		Canvas* newCanvas = widget->GetCanvas();

		// Changing a widget canvas is a problem because of the canvas entities
		NazaraAssert(oldCanvas == newCanvas, "Transferring a widget between canvas is not yet supported");

		Node::SetParent(widget);
		m_widgetParent = widget;

		Layout();
	}

	void BaseWidget::SetRenderingRect(const Nz::Rectf& renderingRect)
	{
		m_renderingRect = renderingRect;

		UpdatePositionAndSize();
	}

	void BaseWidget::Show(bool show)
	{
		if (m_visible != show)
		{
			m_visible = show;

			if (m_visible)
				RegisterToCanvas();
			else
				UnregisterFromCanvas();

			for (WidgetEntity& entity : m_entities)
			{
				if (entity.isEnabled)
				{
					entity.handle->Enable(show); //< This will override isEnabled
					entity.isEnabled = true;
				}
			}

			for (const auto& widgetPtr : m_children)
				widgetPtr->Show(show);
		}
	}

	const EntityHandle& BaseWidget::CreateEntity()
	{
		const EntityHandle& newEntity = m_world->CreateEntity();
		newEntity->Enable(m_visible);

		m_entities.emplace_back();
		WidgetEntity& newWidgetEntity = m_entities.back();
		newWidgetEntity.handle = newEntity;
		newWidgetEntity.onDisabledSlot.Connect(newEntity->OnEntityDisabled, [this](Entity* entity)
		{
			auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; });
			NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget");

			it->isEnabled = false;
		});

		newWidgetEntity.onEnabledSlot.Connect(newEntity->OnEntityEnabled, [this](Entity* entity)
		{
			auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; });
			NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget");

			if (!IsVisible())
				entity->Disable(); // Next line will override isEnabled status

			it->isEnabled = true;
		});

		return newEntity;
	}

	void BaseWidget::DestroyEntity(Entity* entity)
	{
		auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; });
		NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget");

		m_entities.erase(it);
	}

	void BaseWidget::Layout()
	{
		if (m_backgroundSprite)
			m_backgroundSprite->SetSize(m_size.x, m_size.y);

		UpdatePositionAndSize();
	}

	void BaseWidget::InvalidateNode()
	{
		Node::InvalidateNode();

		UpdatePositionAndSize();
	}

	bool BaseWidget::IsFocusable() const
	{
		return false;
	}

	void BaseWidget::OnFocusLost()
	{
	}

	void BaseWidget::OnFocusReceived()
	{
	}

	bool BaseWidget::OnKeyPressed(const Nz::WindowEvent::KeyEvent& key)
	{
		return false;
	}

	void BaseWidget::OnKeyReleased(const Nz::WindowEvent::KeyEvent& /*key*/)
	{
	}

	void BaseWidget::OnMouseEnter()
	{
	}

	void BaseWidget::OnMouseMoved(int /*x*/, int /*y*/, int /*deltaX*/, int /*deltaY*/)
	{
	}

	void BaseWidget::OnMouseButtonPress(int /*x*/, int /*y*/, Nz::Mouse::Button /*button*/)
	{
	}

	void BaseWidget::OnMouseButtonRelease(int /*x*/, int /*y*/, Nz::Mouse::Button /*button*/)
	{
	}

	void BaseWidget::OnMouseWheelMoved(int /*x*/, int /*y*/, float /*delta*/)
	{
	}

	void BaseWidget::OnMouseExit()
	{
	}

	void BaseWidget::OnParentResized(const Nz::Vector2f& /*newSize*/)
	{
	}

	void BaseWidget::OnTextEntered(char32_t /*character*/, bool /*repeated*/)
	{
	}

	void BaseWidget::DestroyChild(BaseWidget* widget)
	{
		auto it = std::find_if(m_children.begin(), m_children.end(), [widget] (const std::unique_ptr<BaseWidget>& widgetPtr) -> bool
		{
			return widgetPtr.get() == widget;
		});

		NazaraAssert(it != m_children.end(), "Child widget not found in parent");

		m_children.erase(it);
	}

	void BaseWidget::DestroyChildren()
	{
		m_children.clear();
	}

	void BaseWidget::RegisterToCanvas()
	{
		NazaraAssert(!IsRegisteredToCanvas(), "Widget is already registered to canvas");

		m_canvasIndex = m_canvas->RegisterWidget(this);
	}

	void BaseWidget::UnregisterFromCanvas()
	{
		if (IsRegisteredToCanvas())
		{
			m_canvas->UnregisterWidget(m_canvasIndex);
			m_canvasIndex = InvalidCanvasIndex;
		}
	}

	void BaseWidget::UpdatePositionAndSize()
	{
		if (IsRegisteredToCanvas())
			m_canvas->NotifyWidgetBoxUpdate(m_canvasIndex);

		Nz::Vector2f widgetPos = Nz::Vector2f(GetPosition());
		Nz::Vector2f widgetSize = GetSize();

		Nz::Rectf widgetRect(widgetPos.x, widgetPos.y, widgetSize.x, widgetSize.y);
		Nz::Rectf widgetRenderingRect(widgetPos.x + m_renderingRect.x, widgetPos.y + m_renderingRect.y, m_renderingRect.width, m_renderingRect.height);

		Nz::Rectf widgetBounds;
		widgetRect.Intersect(widgetRenderingRect, &widgetBounds);

		Nz::Recti fullBounds(widgetBounds);
		for (WidgetEntity& widgetEntity : m_entities)
		{
			const Ndk::EntityHandle& entity = widgetEntity.handle;
			if (entity->HasComponent<GraphicsComponent>())
				entity->GetComponent<GraphicsComponent>().SetScissorRect(fullBounds);
		}
	}
}
<|endoftext|>


<commit_before>#include "BackendWorker.hpp"

#include <iostream>

#include <opencv2/opencv.hpp>

#include "SgPoint.h"
#include "SgSystem.h"

namespace Go_Controller {

// converts a cv::Mat to a QImage
// inspired from http://www.qtforum.de/forum/viewtopic.php?t=9721
//
// Has two preconditions:
//   - depth of the cv::Mat has to be CV_8U = 8-bit unsigned integers ( 0..255 )
//   - number of channels has to be 3 (RGB, or BGR in opencv)
QImage mat_to_QImage(cv::Mat source)
{
    assert(source.depth() == CV_8U);
    assert(source.channels() == 3);

    // "cast" or convert to an IplImage to get easier access to needed infos,
    // no copying involved
    IplImage image = source;

    // create QImage from IplImage
    QImage ret((uchar*) image.imageData, image.width, image.height, QImage::Format_RGB888);

    // swap BGR (opencv format) to RGB
    ret = ret.rgbSwapped();

    return ret;
}

BackendWorker::BackendWorker()
    : _game(),
    _scanner(),
    _game_is_initialized(false),
    _cached_board_size(0),
    _scan_timer(this) // this makes sure that the timer has the same thread affinity as its parent (this)
{
    /* define default game rules
     *     handicap: 0
     *     komi:     6.5
     *     scoring:  japanese
     *     game end: after 2 consecutive passes
     */
    _new_game_rules = GoRules(0, GoKomi(6.5), true, true);

    connect(&_scan_timer, SIGNAL(timeout()), this, SLOT(scan()));
    _scan_timer.setInterval(1000);// call the connected slot every 1000 msec (1 fps)
    _scan_timer.start();  // put one event in this threads event queue
}

BackendWorker::~BackendWorker()
{}

void BackendWorker::scan() {
    cv::Mat image;
    GoSetup setup;

    // fetch new camera image
    auto scan_result = _scanner.scanCamera(setup, _cached_board_size, image);

    using Go_Scanner::ScanResult;
    using Go_Backend::UpdateResult;

    switch (scan_result) {
    case ScanResult::Success:
        {
            if (_game_is_initialized) {
                // update game state
                UpdateResult result = _game.update(setup);
                if (result == UpdateResult::Illegal) {
                    emit displayErrorMessage("Your board differs from virtual board!");
                }
                else if (result == UpdateResult::ToCapture) {
                    emit displayErrorMessage("There are stones left to capture.\nMake sure your board matches the virtual one.");
                }
                else {
                    emit displayErrorMessage(""); // no error
                }
            }
            else {
                // the gui doesn't support other sizes
                if (_cached_board_size == 9 || _cached_board_size == 13 || _cached_board_size == 19 ) {
                    _game.init(_cached_board_size, setup, _new_game_rules);
                    _game_is_initialized = true;
                    emit displayErrorMessage("");
                }
                else {
                    emit displayErrorMessage(QString("Not supported board size of %1x%1 detected!").arg(_cached_board_size));
                }
            }

            signalGuiGameDataChanged();

            // converting image (OpenCV data type) to QImage (Qt data type)
            const auto scanner_image = mat_to_QImage(image);
            // and send signal with new image to gui
            emit newImage(scanner_image);

            break;
        }
    case ScanResult::Failed:
        {
            // we still have a camera image to display, even when the scanning failed
            // converting image (OpenCV data type) to QImage (Qt data type)
            const auto scanner_image = mat_to_QImage(image);
            // send signal with new image to gui
            emit newImage(scanner_image);

            emit displayErrorMessage("Board could not be detected correctly!\nBoard selection still accurate?");
            break;
        }
    case ScanResult::NoCamera:
        // disables board detection menu items
        // the menu items will be reactivated when the next newImage signal gets handled
        emit noCameraImage();
        emit displayErrorMessage("No camera image could be retrieved!");
        break;
    default:
        assert(!"Unknown ScanResult?!");
    }
}

void BackendWorker::saveSgf(QString path, QString blackplayer_name, QString whiteplayer_name, QString game_name) {
    auto filepath = path.toStdString();

    if (!_game.saveGame(filepath, blackplayer_name.toStdString(), whiteplayer_name.toStdString(), game_name.toStdString()))
        std::cerr << "Error writing game data to file \"" << filepath << "\"!" << std::endl;
}

void BackendWorker::loadSgf(QString path) {
    auto filepath = path.toStdString();

    auto new_game = _game.loadGame(filepath);
    if (!new_game) {
        emit displayErrorMessagebox("Error loading the game", "Failed to open the selected sgf-file!");
        return;
    }

    auto size = 0;
    if (!new_game->GetIntProp(SG_PROP_SIZE, &size)) {
        emit displayErrorMessagebox("Error loading the game", "You tried to load a sgf file with missing board size!\nOnly sgf-files with a defined board size are supported here.");

        // cleanup
        new_game->DeleteTree();
        return;
    }

    if (size != 9 && size != 13 && size != 19) {
        emit displayErrorMessagebox("Error loading the game", "Only board sizes of 9x9, 13x13 and 19x19 are supported!");

        // cleanup
        new_game->DeleteTree();
        return;
    }

    // _game takes ownership of new_game
    _game.init(new_game);

    signalGuiGameDataChanged();
}

void BackendWorker::pass() {
    _game.pass();

    if (_game.hasEnded())
        signalGuiGameHasEnded();

    signalGuiGameDataChanged();
}

void BackendWorker::resetGame(GoRules rules) {
    _game_is_initialized = false;
    _new_game_rules      = rules;

    if (virtualModeActive()) {
        _game.init(19, GoSetup(), _new_game_rules);

        signalGuiGameDataChanged();
    }
}

void BackendWorker::finish() {
    _game.finishGame();

    signalGuiGameHasEnded();
}

void BackendWorker::resign() {
    _game.resign();

    signalGuiGameHasEnded();
}

void BackendWorker::signalGuiGameHasEnded() const {
    auto result = _game.getResult();

    // signal gui that game has ended with this result
    emit finishedGameResult(QString(result.c_str()));
}

void BackendWorker::setVirtualGameMode(bool checked) {
    if (virtualModeActive()) {
        // go into augmented mode -> do the scanning!
        _scan_timer.start();
    }
    else {
        // go into virtual mode -> no scanning!
        _scan_timer.stop();
        // also hide any scanning related error messages
        emit displayErrorMessage("");

        // initialize a game
        if (!_game_is_initialized)
            _game.init(19, GoSetup(), _new_game_rules);

        signalGuiGameDataChanged();
    }
}

void BackendWorker::playMove(const int x, const int y){
    auto position = SgPointUtil::Pt(x, y);
    _game.playMove(position);

    signalGuiGameDataChanged();
}

void BackendWorker::selectBoardManually() {
    _scan_timer.stop();
    _scanner.selectBoardManually();
    _scan_timer.start();
}

void BackendWorker::selectBoardAutomatically() {
    _scan_timer.stop();
    _scanner.selectBoardAutomatically();
    _scan_timer.start();
}

void BackendWorker::setScannerDebugImage(bool debug) {
    if (debug)
        _scanner.setDebugImage();
    else
        _scanner.setNormalImage();
}

bool BackendWorker::virtualModeActive() const {
    return !_scan_timer.isActive();
}

void BackendWorker::signalGuiGameDataChanged() const {
    // send board data to gui
    // the GUI controls the lifetime of this thread,
    // so passing a pointer to the GoBoard is safe and won't be invalidated
    // as long as the GUI says so
    emit gameDataChanged(&_game);
}

void BackendWorker::navigateHistory(SgNode::Direction dir) {
    if (_game.canNavigateHistory(dir))
        _game.navigateHistory(dir);
    signalGuiGameDataChanged();
}

void BackendWorker::changeScanningRate(int milliseconds) {
    _scan_timer.setInterval(milliseconds);
}

} // namespace Go_Controller
<commit_msg>[refs #4] Fixed bug where the controller didn't consider the game to be initialized after loading the sgf. Caused the sgf to being discarded when switching to virtual mode or when starting the scanning.<commit_after>#include "BackendWorker.hpp"

#include <iostream>

#include <opencv2/opencv.hpp>

#include "SgPoint.h"
#include "SgSystem.h"

namespace Go_Controller {

// converts a cv::Mat to a QImage
// inspired from http://www.qtforum.de/forum/viewtopic.php?t=9721
//
// Has two preconditions:
//   - depth of the cv::Mat has to be CV_8U = 8-bit unsigned integers ( 0..255 )
//   - number of channels has to be 3 (RGB, or BGR in opencv)
QImage mat_to_QImage(cv::Mat source)
{
    assert(source.depth() == CV_8U);
    assert(source.channels() == 3);

    // "cast" or convert to an IplImage to get easier access to needed infos,
    // no copying involved
    IplImage image = source;

    // create QImage from IplImage
    QImage ret((uchar*) image.imageData, image.width, image.height, QImage::Format_RGB888);

    // swap BGR (opencv format) to RGB
    ret = ret.rgbSwapped();

    return ret;
}

BackendWorker::BackendWorker()
    : _game(),
    _scanner(),
    _game_is_initialized(false),
    _cached_board_size(0),
    _scan_timer(this) // this makes sure that the timer has the same thread affinity as its parent (this)
{
    /* define default game rules
     *     handicap: 0
     *     komi:     6.5
     *     scoring:  japanese
     *     game end: after 2 consecutive passes
     */
    _new_game_rules = GoRules(0, GoKomi(6.5), true, true);

    connect(&_scan_timer, SIGNAL(timeout()), this, SLOT(scan()));
    _scan_timer.setInterval(1000);// call the connected slot every 1000 msec (1 fps)
    _scan_timer.start();  // put one event in this threads event queue
}

BackendWorker::~BackendWorker()
{}

void BackendWorker::scan() {
    cv::Mat image;
    GoSetup setup;

    // fetch new camera image
    auto scan_result = _scanner.scanCamera(setup, _cached_board_size, image);

    using Go_Scanner::ScanResult;
    using Go_Backend::UpdateResult;

    switch (scan_result) {
    case ScanResult::Success:
        {
            if (_game_is_initialized) {
                // update game state
                UpdateResult result = _game.update(setup);
                if (result == UpdateResult::Illegal) {
                    emit displayErrorMessage("Your board differs from virtual board!");
                }
                else if (result == UpdateResult::ToCapture) {
                    emit displayErrorMessage("There are stones left to capture.\nMake sure your board matches the virtual one.");
                }
                else {
                    emit displayErrorMessage(""); // no error
                }
            }
            else {
                // the gui doesn't support other sizes
                if (_cached_board_size == 9 || _cached_board_size == 13 || _cached_board_size == 19 ) {
                    _game.init(_cached_board_size, setup, _new_game_rules);
                    _game_is_initialized = true;
                    emit displayErrorMessage("");
                }
                else {
                    emit displayErrorMessage(QString("Not supported board size of %1x%1 detected!").arg(_cached_board_size));
                }
            }

            signalGuiGameDataChanged();

            // converting image (OpenCV data type) to QImage (Qt data type)
            const auto scanner_image = mat_to_QImage(image);
            // and send signal with new image to gui
            emit newImage(scanner_image);

            break;
        }
    case ScanResult::Failed:
        {
            // we still have a camera image to display, even when the scanning failed
            // converting image (OpenCV data type) to QImage (Qt data type)
            const auto scanner_image = mat_to_QImage(image);
            // send signal with new image to gui
            emit newImage(scanner_image);

            emit displayErrorMessage("Board could not be detected correctly!\nBoard selection still accurate?");
            break;
        }
    case ScanResult::NoCamera:
        // disables board detection menu items
        // the menu items will be reactivated when the next newImage signal gets handled
        emit noCameraImage();
        emit displayErrorMessage("No camera image could be retrieved!");
        break;
    default:
        assert(!"Unknown ScanResult?!");
    }
}

void BackendWorker::saveSgf(QString path, QString blackplayer_name, QString whiteplayer_name, QString game_name) {
    auto filepath = path.toStdString();

    if (!_game.saveGame(filepath, blackplayer_name.toStdString(), whiteplayer_name.toStdString(), game_name.toStdString()))
        std::cerr << "Error writing game data to file \"" << filepath << "\"!" << std::endl;
}

void BackendWorker::loadSgf(QString path) {
    auto filepath = path.toStdString();

    auto new_game = _game.loadGame(filepath);
    if (!new_game) {
        emit displayErrorMessagebox("Error loading the game", "Failed to open the selected sgf-file!");
        return;
    }

    auto size = 0;
    if (!new_game->GetIntProp(SG_PROP_SIZE, &size)) {
        emit displayErrorMessagebox("Error loading the game", "You tried to load a sgf file with missing board size!\nOnly sgf-files with a defined board size are supported here.");

        // cleanup
        new_game->DeleteTree();
        return;
    }

    if (size != 9 && size != 13 && size != 19) {
        emit displayErrorMessagebox("Error loading the game", "Only board sizes of 9x9, 13x13 and 19x19 are supported!");

        // cleanup
        new_game->DeleteTree();
        return;
    }

    // _game takes ownership of new_game
    _game.init(new_game);
    _game_is_initialized = true;

    signalGuiGameDataChanged();
}

void BackendWorker::pass() {
    _game.pass();

    if (_game.hasEnded())
        signalGuiGameHasEnded();

    signalGuiGameDataChanged();
}

void BackendWorker::resetGame(GoRules rules) {
    _game_is_initialized = false;
    _new_game_rules      = rules;

    if (virtualModeActive()) {
        _game.init(19, GoSetup(), _new_game_rules);

        signalGuiGameDataChanged();
    }
}

void BackendWorker::finish() {
    _game.finishGame();

    signalGuiGameHasEnded();
}

void BackendWorker::resign() {
    _game.resign();

    signalGuiGameHasEnded();
}

void BackendWorker::signalGuiGameHasEnded() const {
    auto result = _game.getResult();

    // signal gui that game has ended with this result
    emit finishedGameResult(QString(result.c_str()));
}

void BackendWorker::setVirtualGameMode(bool checked) {
    if (virtualModeActive()) {
        // go into augmented mode -> do the scanning!
        _scan_timer.start();
    }
    else {
        // go into virtual mode -> no scanning!
        _scan_timer.stop();
        // also hide any scanning related error messages
        emit displayErrorMessage("");

        // initialize a game
        if (!_game_is_initialized)
            _game.init(19, GoSetup(), _new_game_rules);

        signalGuiGameDataChanged();
    }
}

void BackendWorker::playMove(const int x, const int y){
    auto position = SgPointUtil::Pt(x, y);
    _game.playMove(position);

    signalGuiGameDataChanged();
}

void BackendWorker::selectBoardManually() {
    _scan_timer.stop();
    _scanner.selectBoardManually();
    _scan_timer.start();
}

void BackendWorker::selectBoardAutomatically() {
    _scan_timer.stop();
    _scanner.selectBoardAutomatically();
    _scan_timer.start();
}

void BackendWorker::setScannerDebugImage(bool debug) {
    if (debug)
        _scanner.setDebugImage();
    else
        _scanner.setNormalImage();
}

bool BackendWorker::virtualModeActive() const {
    return !_scan_timer.isActive();
}

void BackendWorker::signalGuiGameDataChanged() const {
    // send board data to gui
    // the GUI controls the lifetime of this thread,
    // so passing a pointer to the GoBoard is safe and won't be invalidated
    // as long as the GUI says so
    emit gameDataChanged(&_game);
}

void BackendWorker::navigateHistory(SgNode::Direction dir) {
    if (_game.canNavigateHistory(dir))
        _game.navigateHistory(dir);
    signalGuiGameDataChanged();
}

void BackendWorker::changeScanningRate(int milliseconds) {
    _scan_timer.setInterval(milliseconds);
}

} // namespace Go_Controller
<|endoftext|>


<commit_before>class segtree{

struct e {
	int sz, ans;
};

public:
    int n;  // array size
    vector<e> t;

	void combine(e &l, e &r, e &res) {
		if (l.sz == 0) {
            res = r;
            return;
		}
		if (r.sz == 0) {
            res = l;
            return;
		}
        res.sz = l.sz + r.sz;
        res.ans = l.ans + r.ans;
	}

	void combine_left(e &l, e &r) {
        e res;
        combine(l, r, res);
        l = res;
	}

	void combine_right(e &l, e &r) {
        e res;
        combine(l, r, res);
        r = res;
	}

    void build(vector<int> &v) {  // build the tree
        for (int i = n; i < 2 * n; i++) {
            t[i].sz = 1;
            t[i].ans = v[i - n];
        }
        for (int i = n - 1; i > 0; --i) combine(t[i<<1], t[i<<1|1], t[i]);
    }

    void modify(int p, int value) {  // set value at position p
        for (t[p += n].ans = value; p >>= 1; ) combine(t[p<<1], t[p<<1|1], t[p]);
    }

    int query(int l, int r) {  // sum on interval [l, r)
        e ansl, ansr;
        ansl.sz = ansr.sz = 0;
        for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
            if (l&1) combine_left(ansl, t[l++]);
            if (r&1) combine_right(t[--r], ansr);
        }
        combine_left(ansl, ansr);
        return ansl.ans;
    }

    segtree(int n):n(n),t(){
        t.resize(n << 1);
    }
};<commit_msg>Tab fixes<commit_after>class segtree{

struct e {
	int sz, ans;
};

public:
    int n;  // array size
    vector<e> t;

	void combine(e &l, e &r, e &res) {
	    if (l.sz == 0) {
                res = r;
                return;
	    }
	    if (r.sz == 0) {
                res = l;
                return;
	    }
            res.sz = l.sz + r.sz;
            res.ans = l.ans + r.ans;
	}

	void combine_left(e &l, e &r) {
            e res;
            combine(l, r, res);
            l = res;
	}

	void combine_right(e &l, e &r) {
            e res;
            combine(l, r, res);
            r = res;
	}

    void build(vector<int> &v) {  // build the tree
        for (int i = n; i < 2 * n; i++) {
            t[i].sz = 1;
            t[i].ans = v[i - n];
        }
        for (int i = n - 1; i > 0; --i) combine(t[i<<1], t[i<<1|1], t[i]);
    }

    void modify(int p, int value) {  // set value at position p
        for (t[p += n].ans = value; p >>= 1; ) combine(t[p<<1], t[p<<1|1], t[p]);
    }

    int query(int l, int r) {  // sum on interval [l, r)
        e ansl, ansr;
        ansl.sz = ansr.sz = 0;
        for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
            if (l&1) combine_left(ansl, t[l++]);
            if (r&1) combine_right(t[--r], ansr);
        }
        combine_left(ansl, ansr);
        return ansl.ans;
    }

    segtree(int n):n(n),t(){
        t.resize(n << 1);
    }
};
<|endoftext|>


<commit_before>#include "PyMeshPlugin.hh"

// todo: some switch for boost static build or not
#include <boost/python.hpp>

#include <OpenFlipper/BasePlugin/PluginFunctions.hh>
#include <OpenFlipper/common/GlobalOptions.hh>

// adds openmesh python module for inter language communication
//#include <OpenMesh/src/Python/Bindings.cc>
#include "OMPyModule.hh"

#include "MeshFactory.hh"

#include <QFileDialog>

static const char* g_job_id = "PyMesh Interpreter";

PyMeshPlugin::PyMeshPlugin():
    main_module_(),
    toolbox_(),
    createdObjects_()
{

}

PyMeshPlugin::~PyMeshPlugin()
{
    Py_Finalize();
}

void PyMeshPlugin::initializePlugin()
{

}

void PyMeshPlugin::slotSelectFile()
{
    QString fileName = QFileDialog::getOpenFileName(NULL,
        tr("Open Python Script"), toolbox_->filename->text(), tr("Python Script (*.py)"));

    if (!QFile::exists(fileName))
        return;

    toolbox_->filename->setText(fileName);
}

void PyMeshPlugin::pluginsInitialized()
{
    toolbox_ = new PyMeshToolbox();
    Q_EMIT addToolbox("PyMesh", toolbox_, new QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "pymesh_python.png"));

    toolbox_->pbRunFile->setIcon(QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "arrow-right.png"));
    toolbox_->pbRunFile->setText("");
    toolbox_->pbFileSelect->setIcon(QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "document-open.png"));
    toolbox_->pbFileSelect->setText("");

    toolbox_->filename->setText(OpenFlipperSettings().value("Plugin-PyMesh/LastOpenedFile",
        OpenFlipper::Options::applicationDirStr() + OpenFlipper::Options::dirSeparator()).toString());

    connect(toolbox_->pbRunFile, SIGNAL(clicked()), this, SLOT(slotRunScript()));
    connect(toolbox_->pbFileSelect, SIGNAL(clicked()), this, SLOT(slotSelectFile()));
}

void PyMeshPlugin::slotRunScript()
{
    const QString filename = toolbox_->filename->text();
    OpenFlipperSettings().setValue("Plugin-PyMesh/LastOpenedFile", filename);
    runPyScriptFileAsync(filename, toolbox_->cbClearPrevious->isChecked());
}

////////////////////////////////////////////////////////////////
// Python Interpreter Setup&Run
void PyMeshPlugin::runPyScriptFile(const QString& _filename, bool _clearPrevious)
{
    QFile f(_filename);
    if (!f.exists())
    {
        Q_EMIT log(LOGERR, QString("Could not find file %1").arg(_filename));
    }
    f.open(QFile::ReadOnly);
    runPyScript(QTextStream(&f).readAll(), _clearPrevious);
}

void PyMeshPlugin::runPyScriptFileAsync(const QString& _filename, bool _clearPrevious)
{
    QFile f(_filename);
    if (!f.exists())
    {
        Q_EMIT log(LOGERR, QString("Could not find file %1").arg(_filename));
    }
    f.open(QFile::ReadOnly);
    runPyScriptAsync(QTextStream(&f).readAll(), _clearPrevious);
}

void PyMeshPlugin::runPyScriptAsync(const QString& _script, bool _clearPrevious)
{
    OpenFlipperThread* th = new OpenFlipperThread(g_job_id);
    connect(th, SIGNAL(finished()), this, SLOT(runPyScriptFinished()));

    connect(th, &OpenFlipperThread::function, this, [_script, _clearPrevious, this]()
    {
        this->runPyScript_internal(_script, _clearPrevious);
        Q_EMIT this->finishJob(QString(g_job_id));
    }
    , Qt::DirectConnection);
    // Run Script

    emit startJob(QString(g_job_id), "Runs Python Script", 0, 0, true);
    th->start();
    th->startProcessing();
}

void PyMeshPlugin::runPyScript(const QString& _script, bool _clearPrevious)
{
    runPyScript_internal(_script, _clearPrevious);
    runPyScriptFinished();
}

void PyMeshPlugin::runPyScript_internal(const QString& _script, bool _clearPrevious)
{
    // init
    initPython();

    // clear env
    if (_clearPrevious)
        for (size_t i = 0; i < createdObjects_.size(); ++i)
            Q_EMIT deleteObject(createdObjects_[i]);
    createdObjects_.clear();

    QString jobName = name() + " PyRun Worker";
    OpenFlipperThread* th = new OpenFlipperThread(jobName);
    connect(th, SIGNAL(finished()), this, SLOT(runPyScriptFinished()));

    PyRun_SimpleString(_script.toLatin1());
}

void PyMeshPlugin::runPyScriptFinished()
{
    // Update
    for (size_t i = 0; i < createdObjects_.size(); ++i)
    {
        Q_EMIT updatedObject(createdObjects_[i], UPDATE_ALL);
        Q_EMIT createBackup(createdObjects_[i], "Original Object");
    }

    PluginFunctions::viewAll();
}

void PyMeshPlugin::initPython()
{
    if (Py_IsInitialized())
        return;

    PyImport_AppendInittab("openmesh", openmesh_pyinit_function);

    Py_Initialize();
    PyEval_InitThreads();

    main_module_ = boost::python::object(boost::python::handle<>(PyImport_AddModule("__main__")));

    // redirect python output
    initPyLogger(main_module_.ptr(), this);

    // add openmesh module
    boost::python::object main_namespace = main_module_.attr("__dict__");

    boost::python::object om_module((boost::python::handle<>(PyImport_ImportModule("openmesh"))));
    main_namespace["openmesh"] = om_module;

    // hook into mesh constructors
    registerFactoryMethods(this, om_module);

}

// Abort functions
int quitPython(void*)
{
    PyErr_SetInterrupt();
    return -1;
}

void PyMeshPlugin::canceledJob(QString _job)
{
    if (!_job.contains(g_job_id))
        return;

    PyGILState_STATE state = PyGILState_Ensure();
    Py_AddPendingCall(&quitPython, NULL);
    PyGILState_Release(state);

    Q_EMIT log(LOGINFO, "Python Execution Canceled.");
}

///////////////////////////////////////////////////////////////////
// Python callback functions
void PyMeshPlugin::pyOutput(const char* w)
{
    Q_EMIT log(LOGOUT, QString(w));
}

void PyMeshPlugin::pyError(const char* w)
{
    Q_EMIT log(LOGERR, QString(w));
}

OpenMesh::Python::TriMesh* PyMeshPlugin::createTriMesh()
{
    int objectId = -1;

    Q_EMIT addEmptyObject(DATA_TRIANGLE_MESH, objectId);

    TriMeshObject* object;
    PluginFunctions::getObject(objectId, object);

    createdObjects_.push_back(objectId);

    return reinterpret_cast<OpenMesh::Python::TriMesh*>(object->mesh());
}

OpenMesh::Python::PolyMesh* PyMeshPlugin::createPolyMesh()
{
    int objectId = -1;

    Q_EMIT addEmptyObject(DATA_POLY_MESH, objectId);

    PolyMeshObject* object;
    PluginFunctions::getObject(objectId, object);

    createdObjects_.push_back(objectId);

    return reinterpret_cast<OpenMesh::Python::PolyMesh*>(object->mesh());
}

<commit_msg>moved python init to main thread<commit_after>#include "PyMeshPlugin.hh"

// todo: some switch for boost static build or not
#include <boost/python.hpp>

#include <OpenFlipper/BasePlugin/PluginFunctions.hh>
#include <OpenFlipper/common/GlobalOptions.hh>

// adds openmesh python module for inter language communication
//#include <OpenMesh/src/Python/Bindings.cc>
#include "OMPyModule.hh"

#include "MeshFactory.hh"

#include <QFileDialog>

static const char* g_job_id = "PyMesh Interpreter";

PyMeshPlugin::PyMeshPlugin():
    main_module_(),
    toolbox_(),
    createdObjects_()
{

}

PyMeshPlugin::~PyMeshPlugin()
{
    Py_Finalize();
}

void PyMeshPlugin::initializePlugin()
{

}

void PyMeshPlugin::slotSelectFile()
{
    QString fileName = QFileDialog::getOpenFileName(NULL,
        tr("Open Python Script"), toolbox_->filename->text(), tr("Python Script (*.py)"));

    if (!QFile::exists(fileName))
        return;

    toolbox_->filename->setText(fileName);
}

void PyMeshPlugin::pluginsInitialized()
{
    toolbox_ = new PyMeshToolbox();
    Q_EMIT addToolbox("PyMesh", toolbox_, new QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "pymesh_python.png"));

    toolbox_->pbRunFile->setIcon(QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "arrow-right.png"));
    toolbox_->pbRunFile->setText("");
    toolbox_->pbFileSelect->setIcon(QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "document-open.png"));
    toolbox_->pbFileSelect->setText("");

    toolbox_->filename->setText(OpenFlipperSettings().value("Plugin-PyMesh/LastOpenedFile",
        OpenFlipper::Options::applicationDirStr() + OpenFlipper::Options::dirSeparator()).toString());

    connect(toolbox_->pbRunFile, SIGNAL(clicked()), this, SLOT(slotRunScript()));
    connect(toolbox_->pbFileSelect, SIGNAL(clicked()), this, SLOT(slotSelectFile()));
}

void PyMeshPlugin::slotRunScript()
{
    const QString filename = toolbox_->filename->text();
    OpenFlipperSettings().setValue("Plugin-PyMesh/LastOpenedFile", filename);
    runPyScriptFileAsync(filename, toolbox_->cbClearPrevious->isChecked());
}

////////////////////////////////////////////////////////////////
// Python Interpreter Setup&Run
void PyMeshPlugin::runPyScriptFile(const QString& _filename, bool _clearPrevious)
{
    QFile f(_filename);
    if (!f.exists())
    {
        Q_EMIT log(LOGERR, QString("Could not find file %1").arg(_filename));
    }
    f.open(QFile::ReadOnly);
    runPyScript(QTextStream(&f).readAll(), _clearPrevious);
}

void PyMeshPlugin::runPyScriptFileAsync(const QString& _filename, bool _clearPrevious)
{
    QFile f(_filename);
    if (!f.exists())
    {
        Q_EMIT log(LOGERR, QString("Could not find file %1").arg(_filename));
    }
    f.open(QFile::ReadOnly);
    runPyScriptAsync(QTextStream(&f).readAll(), _clearPrevious);
}

void PyMeshPlugin::runPyScriptAsync(const QString& _script, bool _clearPrevious)
{
    initPython();
    OpenFlipperThread* th = new OpenFlipperThread(g_job_id);
    connect(th, SIGNAL(finished()), this, SLOT(runPyScriptFinished()));

    connect(th, &OpenFlipperThread::function, this, [_script, _clearPrevious, this]()
    {
        this->runPyScript_internal(_script, _clearPrevious);
        Q_EMIT this->finishJob(QString(g_job_id));
    }
    , Qt::DirectConnection);
    // Run Script

    emit startJob(QString(g_job_id), "Runs Python Script", 0, 0, true);
    th->start();
    th->startProcessing();
}

void PyMeshPlugin::runPyScript(const QString& _script, bool _clearPrevious)
{
    runPyScript_internal(_script, _clearPrevious);
    runPyScriptFinished();
}

void PyMeshPlugin::runPyScript_internal(const QString& _script, bool _clearPrevious)
{
    // init
    initPython();

    // clear env
    if (_clearPrevious)
        for (size_t i = 0; i < createdObjects_.size(); ++i)
            Q_EMIT deleteObject(createdObjects_[i]);
    createdObjects_.clear();

    QString jobName = name() + " PyRun Worker";
    OpenFlipperThread* th = new OpenFlipperThread(jobName);
    connect(th, SIGNAL(finished()), this, SLOT(runPyScriptFinished()));

    PyRun_SimpleString(_script.toLatin1());
}

void PyMeshPlugin::runPyScriptFinished()
{
    // Update
    for (size_t i = 0; i < createdObjects_.size(); ++i)
    {
        Q_EMIT updatedObject(createdObjects_[i], UPDATE_ALL);
        Q_EMIT createBackup(createdObjects_[i], "Original Object");
    }

    PluginFunctions::viewAll();
}

void PyMeshPlugin::initPython()
{
    if (Py_IsInitialized())
        return;

    PyImport_AppendInittab("openmesh", openmesh_pyinit_function);

    Py_Initialize();
    PyEval_InitThreads();

    main_module_ = boost::python::object(boost::python::handle<>(PyImport_AddModule("__main__")));

    // redirect python output
    initPyLogger(main_module_.ptr(), this);

    // add openmesh module
    boost::python::object main_namespace = main_module_.attr("__dict__");

    boost::python::object om_module((boost::python::handle<>(PyImport_ImportModule("openmesh"))));
    main_namespace["openmesh"] = om_module;

    // hook into mesh constructors
    registerFactoryMethods(this, om_module);

}

// Abort functions
int quitPython(void*)
{
    PyErr_SetInterrupt();
    return -1;
}

void PyMeshPlugin::canceledJob(QString _job)
{
    if (!_job.contains(g_job_id))
        return;

    PyGILState_STATE state = PyGILState_Ensure();
    Py_AddPendingCall(&quitPython, NULL);
    PyGILState_Release(state);

    Q_EMIT log(LOGINFO, "Python Execution Canceled.");
}

///////////////////////////////////////////////////////////////////
// Python callback functions
void PyMeshPlugin::pyOutput(const char* w)
{
    Q_EMIT log(LOGOUT, QString(w));
}

void PyMeshPlugin::pyError(const char* w)
{
    Q_EMIT log(LOGERR, QString(w));
}

OpenMesh::Python::TriMesh* PyMeshPlugin::createTriMesh()
{
    int objectId = -1;

    Q_EMIT addEmptyObject(DATA_TRIANGLE_MESH, objectId);

    TriMeshObject* object;
    PluginFunctions::getObject(objectId, object);

    createdObjects_.push_back(objectId);

    return reinterpret_cast<OpenMesh::Python::TriMesh*>(object->mesh());
}

OpenMesh::Python::PolyMesh* PyMeshPlugin::createPolyMesh()
{
    int objectId = -1;

    Q_EMIT addEmptyObject(DATA_POLY_MESH, objectId);

    PolyMeshObject* object;
    PluginFunctions::getObject(objectId, object);

    createdObjects_.push_back(objectId);

    return reinterpret_cast<OpenMesh::Python::PolyMesh*>(object->mesh());
}

<|endoftext|>


<commit_before>/*
 * Quad2.h
 *
 *  Created on: May 15, 2013
 *      Author: Ryler Hockenbury
 *
 * Quad2
 */

// Do not remove the include below
#include "Quad2.h"

uint8_t vehicleStatus = 0x0;
bool onGround = TRUE;

ITG3200 gyro;
ADXL345 accel;
HMC5883L comp;
AR6210 receiver;
PID controller[2]; // pitch and roll controllers

float gyroData[3] = {0.0, 0.0, 0.0};  // x, y and z axis
float accelData[3] = {0.0, 0.0, 0.0};
float compData[3] = {0.0, 0.0, 0.0};

float currentFlightAngle[3] = {0.0, 0.0, 0.0}; // roll, pitch, yaw
float targetFlightAngle[3] = {0.0, 0.0}; // roll and pitch

float stickCommands[6] = {1500, 1500, 1500, 1000, 1000, 1000};  //raw throttle, roll, pitch, yaw

bool auxStatus[2] = {ON, OFF}; // controller mode

uint16_t currentSystemTime = 0; // millis() is a 16 bit timer
uint16_t lastSystemTime = 0;
uint16_t deltaSystemTime = 0;

uint16_t last100HzTime = 0;
uint16_t last50HzTime = 0;
uint16_t last10HzTime = 0;

void handleReceiverInterrupt() {
  receiver.readChannels();
}

void setup() {
  Wire.begin();         // initialize I2C bus
  Serial.begin(57600);  // initialize serial link
  receiver.init();      // initialize receiver

  // set up interrupts to read receiver channels
  pinMode(PPM_PIN, INPUT);
  attachInterrupt(0, handleReceiverInterrupt, RISING);

  // process stick inputs to bring sensors and ESCs online
  Serial.println("Entering process init commands");
  receiver.processInitCommands(&gyro, &accel, &comp);

  // run system test
  // turn on green LED

  //Serial.println("Initializing Sensors");
  //gyro.init();
  //accel.init();
  //comp.init();

  delay(200);
  Serial.println("Starting main loop");
}

// TODO config file

void loop() {
  currentSystemTime = millis();
  deltaSystemTime = currentSystemTime - lastSystemTime;

  //Serial.println(currentSystemTime);
  //Serial.println(last100HzTime);
  //Serial.println(deltaSystemTime);   // loop time

  if(currentSystemTime < last100HzTime)
	  last100HzTime = 0;

  if(currentSystemTime < last50HzTime)
	  last50HzTime = 0;

  if(currentSystemTime < last10HzTime)
 	  last10HzTime = 0;

  // TODO there is a problem with timer overflow here, and maintaining frequency

  // TODO Can i make the assumption that these happend periodically as i would except?

  /* 100 Hz Tasks
   * Poll IMU sensors, calculate orientation, update controller and command motors.
   *
   */
  if(currentSystemTime >= (last100HzTime + 10))
  {
	/*
    gyro.getRate(gyroData);
    accel.getValue(accelData);
    comp.getHeading(compData);
    getOrientation(currentFlightAngle, gyroData, accelData, compData);
	*/

    //levelAdjust[ROLL_AXIS] = targetFlightAngle[ROLL_AXIS] - currentFlightAngle[ROLL_AXIS];
    //levelAdjust[PITCH_AXIS] = targetFlightAngle[PITCH_AXIS] - currentFlightAngle[PITCH_AXIS];

    //motor.axisCommand[ROLL_AXIS] = controller[ROLL_AXIS].updatePid(1500.0 + gyroData[1], stickCommands[ROLL] + levelAdjust[ROLL_AXIS]);
    //motor.axisCommand[PITCH_AXIS] = controller[PITCH_AXIS].updatePid(1500.0 + gyroData[0], stickCommands[PITCH] + levelAdjust[PITCH_AXIS]);

    //motor.axisCommand[THROTTLE] = stickCommands[THROTTLE];
    //motor.axisCommand[YAW_AXIS] = stickCommands[YAW];

    //motor.command[MOTOR1] = throttle + roll + pitch + yaw commands;

    last100HzTime = currentSystemTime;
  }

  /* 50 Hz Tasks
   * Read and sanitize commands from radio, and monitor battery.
   *
   */
  if(currentSystemTime >= (last50HzTime + 20))
  {
    receiver.getStickCommands(stickCommands);
    //targetFlightAngle[ROLL_AXIS] = reciever.mapStickCommandToAngle(stickCommands[ROLL]);
    //targetFlightAngle[PITCH_AXIS] = reciever.mapStickCommandToAngle(stickCommands[PITCH]);

    //bool auxStatus[AUX1] = reciever.mapStickCommandToBool(stickCommands[AUX1]);
    //bool auxStatus[AUX2] = reciever.mapStickCommandToBool(stickCommands[AUX2]);

    //controller[ROLL_AXIS].setMode(aux1Status[AUX1]);
    //controller[PITCH_AXIS].setMode(aux1Status[AUX1]);

    //TODO - monitor battery health

    last50HzTime = currentSystemTime;
  }

  /* 10 Hz Tasks
   * Send serial stream to ground station.
   *
   */
  if(currentSystemTime >= (last10HzTime + 100))
  {
    serialOpen();
    //serialPrint(currentFlightAngle, 3); // must remove extra comma
    serialPrint(stickCommands, 6);
    //serialPrint(battVoltage);
    //serialPrint(battCurrent);
    serialClose();

    // TODO -  check for and process serial input
     //  pid gains
     //  stick scale/senstivitiy

    last10HzTime = currentSystemTime;
  }

  lastSystemTime = currentSystemTime;

}

/////////////////////////////////////////////
// CODE TO READ SERIES OF SENSOR REGSITERS //
/////////////////////////////////////////////

/*
byte buffer[6];

Serial.print("\n");
Serial.println("Reading Initialized Bytes");
I2Cdev::readByte(0x53, 0x0, buffer);
    Serial.print("\n");
	Serial.print(buffer[0], HEX);
	Serial.print("\n");
I2Cdev::readByte(0x53, 0x1E, buffer);
    Serial.print("\n");
	Serial.print(buffer[0]);
	Serial.print("\n");
I2Cdev::readByte(0x53, 0x1F, buffer);
    Serial.print("\n");
	Serial.print(buffer[0]);
	Serial.print("\n");
I2Cdev::readByte(0x53, 0x20, buffer);
    Serial.print("\n");
	Serial.print(buffer[0]);
	Serial.print("\n");

I2Cdev::readByte(0x53, 0x2C, buffer);
    Serial.print("\n");
	Serial.print(buffer[0], BIN);
	Serial.print("\n");
I2Cdev::readByte(0x53, 0x2d, buffer);
    Serial.print("\n");
	Serial.print(buffer[0], BIN);
	Serial.print("\n");
I2Cdev::readByte(0x53, 0x31, buffer);
    Serial.print("\n");
	Serial.print(buffer[0], BIN);
	Serial.print("\n");
*/
//delay(20);

//Serial.print(buffer[0]);
//Serial.print("\n");

//delay(5000);
//gyro.setSampleRate(0x9);

//Serial.println("Writing Byte");
//byte status = I2Cdev::writeByte(0x68, 0x15, 0x9);
//Serial.print(status);

//delay(5000);
//I2Cdev::readBytes(0x68, 0x1D, 6, buffer);
//Serial.print("\n");
//Serial.println("Reading Byte");
//I2Cdev::readByte(0x68, 0x15, buffer);

//Serial.print(buffer[0]);
//Serial.print("\n");
<commit_msg>Changed timer variables to 32 bits <commit_after>/*
 * Quad2.h
 *
 *  Created on: May 15, 2013
 *      Author: Ryler Hockenbury
 *
 * Quad2
 */

// Do not remove the include below
#include "Quad2.h"

uint8_t vehicleStatus = 0x0;
bool onGround = TRUE;

ITG3200 gyro;
ADXL345 accel;
HMC5883L comp;
AR6210 receiver;
PID controller[2]; // pitch and roll controllers

float gyroData[3] = {0.0, 0.0, 0.0};  // x, y and z axis
float accelData[3] = {0.0, 0.0, 0.0};
float compData[3] = {0.0, 0.0, 0.0};

float currentFlightAngle[3] = {0.0, 0.0, 0.0}; // roll, pitch, yaw
float targetFlightAngle[3] = {0.0, 0.0}; // roll and pitch

float stickCommands[6] = {1500, 1500, 1500, 1000, 1000, 1000};  //raw throttle, roll, pitch, yaw

bool auxStatus[2] = {ON, OFF}; // controller mode

uint32_t currentSystemTime = 0;
uint32_t lastSystemTime = 0;
uint32_t deltaSystemTime = 0;

uint32_t last100HzTime = 0;
uint32_t last50HzTime = 0;
uint32_t last10HzTime = 0;

void handleReceiverInterrupt() {
  receiver.readChannels();
}

void setup() {
  Wire.begin();         // initialize I2C bus
  Serial.begin(57600);  // initialize serial link
  receiver.init();      // initialize receiver

  // set up interrupts to read receiver channels
  pinMode(PPM_PIN, INPUT);
  attachInterrupt(0, handleReceiverInterrupt, RISING);

  // process stick inputs to bring sensors and ESCs online
  Serial.println("Entering process init commands");
  receiver.processInitCommands(&gyro, &accel, &comp);

  // run system test
  // turn on green LED

  //Serial.println("Initializing Sensors");
  //gyro.init();
  //accel.init();
  //comp.init();

  delay(200);
  Serial.println("Starting main loop");
}

// TODO config file

void loop() {
  currentSystemTime = millis();
  deltaSystemTime = currentSystemTime - lastSystemTime;

  Serial.println("current:" + currentSystemTime);
  Serial.println("100Hz:" + last100HzTime);
  Serial.println("50Hz:" + last50HzTime);
  Serial.println("10Hz:" + last10HzTime);
  Serial.println("last:" + deltaSystemTime);   // loop time

  //if(currentSystemTime < last100HzTime)
	//  last100HzTime = 0;

  //if(currentSystemTime < last50HzTime)
	//  last50HzTime = 0;

  //if(currentSystemTime < last10HzTime)
 	//  last10HzTime = 0;

  // TODO there is a problem with timer overflow here, and maintaining frequency
     /// this is fixed, i think, just need testing

  // TODO Can i make the assumption that these happend periodically as i would except?

  /* 100 Hz Tasks
   * Poll IMU sensors, calculate orientation, update controller and command motors.
   *
   */
  if(currentSystemTime >= (last100HzTime + 10))
  {
	/*
    gyro.getRate(gyroData);
    accel.getValue(accelData);
    comp.getHeading(compData);
    getOrientation(currentFlightAngle, gyroData, accelData, compData);
	*/

    //levelAdjust[ROLL_AXIS] = targetFlightAngle[ROLL_AXIS] - currentFlightAngle[ROLL_AXIS];
    //levelAdjust[PITCH_AXIS] = targetFlightAngle[PITCH_AXIS] - currentFlightAngle[PITCH_AXIS];

    //motor.axisCommand[ROLL_AXIS] = controller[ROLL_AXIS].updatePid(1500.0 + gyroData[1], stickCommands[ROLL] + levelAdjust[ROLL_AXIS]);
    //motor.axisCommand[PITCH_AXIS] = controller[PITCH_AXIS].updatePid(1500.0 + gyroData[0], stickCommands[PITCH] + levelAdjust[PITCH_AXIS]);

    //motor.axisCommand[THROTTLE] = stickCommands[THROTTLE];
    //motor.axisCommand[YAW_AXIS] = stickCommands[YAW];

    //motor.command[MOTOR1] = throttle + roll + pitch + yaw commands;

    last100HzTime = currentSystemTime;
  }

  /* 50 Hz Tasks
   * Read and sanitize commands from radio, and monitor battery.
   *
   */
  if(currentSystemTime >= (last50HzTime + 20))
  {
    //receiver.getStickCommands(stickCommands);
    //targetFlightAngle[ROLL_AXIS] = reciever.mapStickCommandToAngle(stickCommands[ROLL]);
    //targetFlightAngle[PITCH_AXIS] = reciever.mapStickCommandToAngle(stickCommands[PITCH]);

    //bool auxStatus[AUX1] = reciever.mapStickCommandToBool(stickCommands[AUX1]);
    //bool auxStatus[AUX2] = reciever.mapStickCommandToBool(stickCommands[AUX2]);

    //controller[ROLL_AXIS].setMode(aux1Status[AUX1]);
    //controller[PITCH_AXIS].setMode(aux1Status[AUX1]);

    //TODO - monitor battery health

    last50HzTime = currentSystemTime;
  }

  /* 10 Hz Tasks
   * Send serial stream to ground station.
   *
   */
  if(currentSystemTime >= (last10HzTime + 100))
  {
    serialOpen();
    //serialPrint(currentFlightAngle, 3); // must remove extra comma
    //serialPrint(stickCommands, 6);
    //serialPrint(battVoltage);
    //serialPrint(battCurrent);
    serialClose();

    // TODO -  check for and process serial input
     //  pid gains
     //  stick scale/senstivitiy

    last10HzTime = currentSystemTime;
  }

  lastSystemTime = currentSystemTime;

}

/////////////////////////////////////////////
// CODE TO READ SERIES OF SENSOR REGSITERS //
/////////////////////////////////////////////

/*
byte buffer[6];

Serial.print("\n");
Serial.println("Reading Initialized Bytes");
I2Cdev::readByte(0x53, 0x0, buffer);
    Serial.print("\n");
	Serial.print(buffer[0], HEX);
	Serial.print("\n");
I2Cdev::readByte(0x53, 0x1E, buffer);
    Serial.print("\n");
	Serial.print(buffer[0]);
	Serial.print("\n");
I2Cdev::readByte(0x53, 0x1F, buffer);
    Serial.print("\n");
	Serial.print(buffer[0]);
	Serial.print("\n");
I2Cdev::readByte(0x53, 0x20, buffer);
    Serial.print("\n");
	Serial.print(buffer[0]);
	Serial.print("\n");

I2Cdev::readByte(0x53, 0x2C, buffer);
    Serial.print("\n");
	Serial.print(buffer[0], BIN);
	Serial.print("\n");
I2Cdev::readByte(0x53, 0x2d, buffer);
    Serial.print("\n");
	Serial.print(buffer[0], BIN);
	Serial.print("\n");
I2Cdev::readByte(0x53, 0x31, buffer);
    Serial.print("\n");
	Serial.print(buffer[0], BIN);
	Serial.print("\n");
*/
//delay(20);

//Serial.print(buffer[0]);
//Serial.print("\n");

//delay(5000);
//gyro.setSampleRate(0x9);

//Serial.println("Writing Byte");
//byte status = I2Cdev::writeByte(0x68, 0x15, 0x9);
//Serial.print(status);

//delay(5000);
//I2Cdev::readBytes(0x68, 0x1D, 6, buffer);
//Serial.print("\n");
//Serial.println("Reading Byte");
//I2Cdev::readByte(0x68, 0x15, buffer);

//Serial.print(buffer[0]);
//Serial.print("\n");
<|endoftext|>


<commit_before>/*
  This is an example file shipped by 'Aleph - A Library for Exploring
  Persistent Homology'.

  This example demonstrates how to load a network---a graph---from
  a variety of input files.

  The graph will subsequently be expanded to a simplicial complex and
  the persistent homology of the graph will be calculated.

  Demonstrated classes:

    - aleph::PersistenceDiagram
    - aleph::geometry::RipsExpander
    - aleph::topology::io::EdgeListReader
    - aleph::topology::io::GMLReader
    - aleph::topology::io::PajekReader
    - aleph::topology::Simplex
    - aleph::topology::SimplicialComplex
    - aleph::topology::filtrations::Data

  Demonstrated functions:

    - aleph::calculatePersistenceDiagrams
    - aleph::utilities::basename
    - aleph::utilities::extension
    - aleph::utilities::format
    - aleph::utilities::stem
    - Betti numbers of persistence diagrams
    - Simplicial complex dimensionality queries
    - Simplicial complex sorting (for filtrations)

  Original author: Bastian Rieck
*/

#include <aleph/geometry/RipsExpander.hh>

#include <aleph/topology/io/EdgeLists.hh>
#include <aleph/topology/io/GML.hh>
#include <aleph/topology/io/Pajek.hh>

#include <aleph/persistentHomology/Calculation.hh>

#include <aleph/topology/Simplex.hh>
#include <aleph/topology/SimplicialComplex.hh>

#include <aleph/topology/filtrations/Data.hh>

#include <aleph/utilities/Filesystem.hh>
#include <aleph/utilities/Format.hh>

#include <cmath>

#include <algorithm>
#include <iostream>
#include <fstream>
#include <limits>
#include <sstream>
#include <string>
#include <vector>

#include <getopt.h>

void usage()
{
  std::cerr << "Usage: network_analysis FILE [DIMENSION]\n"
            << "\n"
            << "Loads a weighted network (graph) from FILE, expands it up to\n"
            << "the specified DIMENSION, and calculates persistence diagrams\n"
            << "of the weight function of the input.\n"
            << "\n"
            << "Diagrams will be written to STDOUT in a gnuplot-like style.\n"
            << "\n"
            << "Optional arguments:\n"
            << "\n"
            << " --infinity FACTOR: Sets the value to use for unpaired points\n"
            << "                   in the persistence diagram. By default, a\n"
            << "                   large number or +inf will be used. If the\n"
            << "                   specified number is non-zero, it shall be\n"
            << "                   used as a factor in the weight assignment\n"
            << "                   of these points.\n"
            << "\n"
            << " --invert-weights: If specified, inverts input weights. This\n"
            << "                   is useful if the original weights measure\n"
            << "                   the strength of a relationship, and not a\n"
            << "                   dissimilarity between nodes.\n"
            << "\n"
            << " --normalize     : Normalizes all weights to [0,1]. Use this\n"
            << "                   to compare multiple networks.\n"
            << "\n"
            << " --output PATH   : Uses the specified path to store diagrams\n"
            << "                   instead of writing them to STDOUT.\n"
            << "\n"
            << "\n";
}

int main( int argc, char** argv )
{
  // We have to specify the required data type and vertex type for the
  // simplex class here. These settings are generic and should cover
  // most situations. If your weights are floats or even integers, you
  // can of course change it here. Likewise, if your simplicial complex
  // is very small, you could use `short` instead of `unsigned` to
  // represent all possible values for vertices.
  using DataType          = double;
  using VertexType        = unsigned;

  static option commandLineOptions[] =
  {
    { "infinity"      , required_argument, nullptr, 'f' },
    { "invert-weights", no_argument      , nullptr, 'i' },
    { "normalize"     , no_argument      , nullptr, 'n' },
    { "output"        , required_argument, nullptr, 'o' },
    { nullptr         , 0                , nullptr,  0  }
  };

  bool invertWeights       = false;
  bool normalize           = false;
  DataType infinity        = std::numeric_limits<DataType>::has_infinity ? std::numeric_limits<DataType>::infinity() : std::numeric_limits<DataType>::max();
  std::string basePath     = std::string();

  {
    int option = 0;
    while( ( option = getopt_long( argc, argv, "f:ino:", commandLineOptions, nullptr ) ) != -1 )
    {
      switch( option )
      {
      case 'f':
        infinity = static_cast<DataType>( std::stod( optarg ) );
        break;

      case 'i':
        invertWeights = true;
        break;

      case 'n':
        normalize = true;
        break;

      case 'o':
        basePath = optarg;
        break;

      default:
        break;
      }
    }
  }

  if( (argc - optind ) < 1 )
  {
    usage();
    return -1;
  }

  // These are just given for convenience. It makes declaring an
  // instance of a simplicial complex much easier.
  using Simplex           = aleph::topology::Simplex<DataType, VertexType>;
  using SimplicialComplex = aleph::topology::SimplicialComplex<Simplex>;

  // By convention, simplicial complexes are always called K (or L if
  // you need two of them). This is probably bad style. ;)
  SimplicialComplex K;

  // Reading -----------------------------------------------------------

  std::string filename = argv[optind++];
  std::cerr << "* Reading '" << filename << "'...";

  // For supporting different graph formats, Aleph offers different
  // loader classes. Every loader class uses the same interface for
  // reading simplicial complexes, via `operator()()`.
  //
  // Some loader classes support special features of a given format
  // such as label reading. Here, we do not make use of any of them
  // because we are only interested in demonstrating the expansion.
  //
  // Every reader attempts to read edge weights from the file. They
  // are assigned to the 1-simplices in the simplicial complex.
  {
    auto extension = aleph::utilities::extension( filename );
    if( extension == ".gml" )
    {
      aleph::topology::io::GMLReader reader;
      reader( filename, K );
    }
    else if( extension == ".net" )
    {
      aleph::topology::io::PajekReader reader;
      reader( filename, K );
    }
    else
    {
      aleph::topology::io::EdgeListReader reader;
      reader( filename, K );
    }
  }

  std::cerr << "finished\n"
            << "* Extracted simplicial complex with " << K.size() << " simplices\n";

  // Pre-processing ----------------------------------------------------
  //
  // Determine the minimum and the maximum weight. If desired by the
  // user, normalize those weights and/or invert them.

  DataType maxWeight = std::numeric_limits<DataType>::lowest();
  DataType minWeight = std::numeric_limits<DataType>::max();
  for( auto&& simplex : K )
  {
    maxWeight = std::max( maxWeight, simplex.data() );
    minWeight = std::min( minWeight, simplex.data() );
  }

  if( normalize && maxWeight != minWeight )
  {
    std::cerr << "* Normalizing weights to [0,1]...";

    auto range = maxWeight - minWeight;

    for (auto it = K.begin(); it != K.end(); ++it )
    {
      if( it->dimension() == 0 )
        continue;

      auto s = *it;

      s.setData( ( s.data() - minWeight ) / range );
      K.replace( it, s );
    }

    maxWeight = DataType(1);
    minWeight = DataType(0);

    std::cerr << "finished\n";
  }

  if( invertWeights )
  {
    std::cerr << "* Inverting filtration weights...";

    for( auto it = K.begin(); it != K.end(); ++it )
    {
      if( it->dimension() == 0 )
        continue;

      auto s = *it;
      s.setData( maxWeight - s.data() );

      K.replace( it, s );
    }

    std::cerr << "finished\n";
  }

  // Rips expansion ----------------------------------------------------
  //
  // The Rips expander is a generic class for expanding a graph into a
  // higher-dimensional simplicial complex. This follows the procedure
  // described in:
  //
  //   Fast construction of the Vietoris--Rips complex
  //   Afra Zomorodian
  //   Computers & Graphics, Volume 34, Issue 3, pp. 263-–271
  //
  // Formally speaking, the algorithm creates a k-simplex for subsets
  // of simplices in which k+1 simplices have pairwise intersections.
  //
  // Hence, if all three edges of a triangle are present, a 2-simplex
  // will be added to the simplicial complex.

  std::cerr << "* Expanding simplicial complex...";

  aleph::geometry::RipsExpander<SimplicialComplex> ripsExpander;
  if( argc - optind > 0 )
    K = ripsExpander( K, static_cast<unsigned>( std::stoul( argv[optind++] ) ) );

  // This leaves the simplicial complex untouched. The simplices with
  // highest dimension are the edges, i.e. the 1-simplices.
  else
    K = ripsExpander( K, 1 );

  // This tells the expander to use the maximum weight of the faces of
  // a simplex in order to assign the weight of the simplex. Thus, the
  // simplicial complex models a sublevel set filtration after sorting
  // it with the appropriate functor.
  K = ripsExpander.assignMaximumWeight( K );

  std::cerr << "...finished\n"
            << "* Expanded complex has dimension " << K.dimension() << "\n"
            << "* Expanded complex has " << K.size() << " simplices\n";

  std::cerr << "* Establishing filtration order...";

  // A filtration is an ordering of K in which faces precede co-faces in
  // order to ensure that the ordering represents a growing complex. The
  // functor used below sorts simplices based on their data. It hence is
  // just another way of expressing the weights specified in the graph.
  //
  // The data-based filtration ensures that upon coinciding weights, the
  // lower-dimensional simplex will precede the higher-dimensional one.
  K.sort(
    aleph::topology::filtrations::Data<Simplex>()
  );

  std::cerr << "...finished\n";

  // Persistent homology calculation -----------------------------------
  //
  // This uses a convenience function. It expects a simplicial complex
  // in filtration order, calculates it persistent homology using the
  // default algorithms, and returns a vector of persistence diagrams.

  std::cerr << "* Calculating persistent homology...";

  auto diagrams
    = aleph::calculatePersistenceDiagrams( K );

  std::cerr << "...finished\n";

  for( auto&& D : diagrams )
  {
    // This removes all features with a persistence of zero. They only
    // clutter up the diagram.
    D.removeDiagonal();

    if( std::isfinite( infinity ) && infinity != std::numeric_limits<DataType>::max() )
    {
      std::cerr << "* Transforming unpaired points in persistence diagram with a factor of " << infinity << "...\n";

      using PersistenceDiagram = aleph::PersistenceDiagram<DataType>;

      std::transform( D.begin(), D.end(), D.begin(),
          [&maxWeight, &infinity] ( const PersistenceDiagram::Point& p )
          {
            if( !std::isfinite( p.y() ) )
              return PersistenceDiagram::Point( p.x(), infinity * maxWeight );
            else
              return PersistenceDiagram::Point( p );
          } );
    }

    std::ostringstream stream;

    // This 'header' contains some informative entries about the
    // persistence diagram.
    //
    // The Betti number counts how many simplices are without a
    // partner in the diagram.
    //
    // Notice that the diagram has pre-defined output operator;
    // if you do not like the output, you may of course iterate
    // over the points in the diagram yourself.
    stream << "# Persistence diagram <" << filename << ">\n"
           << "#\n"
           << "# Dimension   : " << D.dimension() << "\n"
           << "# Entries     : " << D.size() << "\n"
           << "# Betti number: " << D.betti() << "\n"
           << D;

    // Use the separator for all but the last persistence diagram. Else,
    // the output format is inconsistent and results in empty files.
    if( D != diagrams.back() )
      stream << "\n\n";

    if( basePath.empty() )
      std::cout << stream.str();
    else
    {
      using namespace aleph::utilities;

      auto outputFilename = basePath + "/" + stem( basename( filename ) )
                                     + "_d"
                                     + format( D.dimension(), K.dimension() )
                                     + ".txt";

      std::cerr << "* Storing output in '" << outputFilename << "'...\n";

      std::ofstream out( outputFilename );
      out << stream.str();
    }
  }
}
<commit_msg>Fixed file formatting for network analysis example<commit_after>/*
  This is an example file shipped by 'Aleph - A Library for Exploring
  Persistent Homology'.

  This example demonstrates how to load a network---a graph---from
  a variety of input files.

  The graph will subsequently be expanded to a simplicial complex and
  the persistent homology of the graph will be calculated.

  Demonstrated classes:

    - aleph::PersistenceDiagram
    - aleph::geometry::RipsExpander
    - aleph::topology::io::EdgeListReader
    - aleph::topology::io::GMLReader
    - aleph::topology::io::PajekReader
    - aleph::topology::Simplex
    - aleph::topology::SimplicialComplex
    - aleph::topology::filtrations::Data

  Demonstrated functions:

    - aleph::calculatePersistenceDiagrams
    - aleph::utilities::basename
    - aleph::utilities::extension
    - aleph::utilities::format
    - aleph::utilities::stem
    - Betti numbers of persistence diagrams
    - Simplicial complex dimensionality queries
    - Simplicial complex sorting (for filtrations)

  Original author: Bastian Rieck
*/

#include <aleph/geometry/RipsExpander.hh>

#include <aleph/topology/io/EdgeLists.hh>
#include <aleph/topology/io/GML.hh>
#include <aleph/topology/io/Pajek.hh>

#include <aleph/persistentHomology/Calculation.hh>

#include <aleph/topology/Simplex.hh>
#include <aleph/topology/SimplicialComplex.hh>

#include <aleph/topology/filtrations/Data.hh>

#include <aleph/utilities/Filesystem.hh>
#include <aleph/utilities/Format.hh>

#include <cmath>

#include <algorithm>
#include <iostream>
#include <fstream>
#include <limits>
#include <sstream>
#include <string>
#include <vector>

#include <getopt.h>

void usage()
{
  std::cerr << "Usage: network_analysis FILE [DIMENSION]\n"
            << "\n"
            << "Loads a weighted network (graph) from FILE, expands it up to\n"
            << "the specified DIMENSION, and calculates persistence diagrams\n"
            << "of the weight function of the input.\n"
            << "\n"
            << "Diagrams will be written to STDOUT in a gnuplot-like style.\n"
            << "\n"
            << "Optional arguments:\n"
            << "\n"
            << " --infinity FACTOR: Sets the value to use for unpaired points\n"
            << "                   in the persistence diagram. By default, a\n"
            << "                   large number or +inf will be used. If the\n"
            << "                   specified number is non-zero, it shall be\n"
            << "                   used as a factor in the weight assignment\n"
            << "                   of these points.\n"
            << "\n"
            << " --invert-weights: If specified, inverts input weights. This\n"
            << "                   is useful if the original weights measure\n"
            << "                   the strength of a relationship, and not a\n"
            << "                   dissimilarity between nodes.\n"
            << "\n"
            << " --normalize     : Normalizes all weights to [0,1]. Use this\n"
            << "                   to compare multiple networks.\n"
            << "\n"
            << " --output PATH   : Uses the specified path to store diagrams\n"
            << "                   instead of writing them to STDOUT.\n"
            << "\n"
            << "\n";
}

int main( int argc, char** argv )
{
  // We have to specify the required data type and vertex type for the
  // simplex class here. These settings are generic and should cover
  // most situations. If your weights are floats or even integers, you
  // can of course change it here. Likewise, if your simplicial complex
  // is very small, you could use `short` instead of `unsigned` to
  // represent all possible values for vertices.
  using DataType          = double;
  using VertexType        = unsigned;

  static option commandLineOptions[] =
  {
    { "infinity"      , required_argument, nullptr, 'f' },
    { "invert-weights", no_argument      , nullptr, 'i' },
    { "normalize"     , no_argument      , nullptr, 'n' },
    { "output"        , required_argument, nullptr, 'o' },
    { nullptr         , 0                , nullptr,  0  }
  };

  bool invertWeights       = false;
  bool normalize           = false;
  DataType infinity        = std::numeric_limits<DataType>::has_infinity ? std::numeric_limits<DataType>::infinity() : std::numeric_limits<DataType>::max();
  std::string basePath     = std::string();

  {
    int option = 0;
    while( ( option = getopt_long( argc, argv, "f:ino:", commandLineOptions, nullptr ) ) != -1 )
    {
      switch( option )
      {
      case 'f':
        infinity = static_cast<DataType>( std::stod( optarg ) );
        break;

      case 'i':
        invertWeights = true;
        break;

      case 'n':
        normalize = true;
        break;

      case 'o':
        basePath = optarg;
        break;

      default:
        break;
      }
    }
  }

  if( (argc - optind ) < 1 )
  {
    usage();
    return -1;
  }

  // These are just given for convenience. It makes declaring an
  // instance of a simplicial complex much easier.
  using Simplex           = aleph::topology::Simplex<DataType, VertexType>;
  using SimplicialComplex = aleph::topology::SimplicialComplex<Simplex>;

  // By convention, simplicial complexes are always called K (or L if
  // you need two of them). This is probably bad style. ;)
  SimplicialComplex K;

  // Reading -----------------------------------------------------------

  std::string filename = argv[optind++];
  std::cerr << "* Reading '" << filename << "'...";

  // For supporting different graph formats, Aleph offers different
  // loader classes. Every loader class uses the same interface for
  // reading simplicial complexes, via `operator()()`.
  //
  // Some loader classes support special features of a given format
  // such as label reading. Here, we do not make use of any of them
  // because we are only interested in demonstrating the expansion.
  //
  // Every reader attempts to read edge weights from the file. They
  // are assigned to the 1-simplices in the simplicial complex.
  {
    auto extension = aleph::utilities::extension( filename );
    if( extension == ".gml" )
    {
      aleph::topology::io::GMLReader reader;
      reader( filename, K );
    }
    else if( extension == ".net" )
    {
      aleph::topology::io::PajekReader reader;
      reader( filename, K );
    }
    else
    {
      aleph::topology::io::EdgeListReader reader;
      reader( filename, K );
    }
  }

  std::cerr << "finished\n"
            << "* Extracted simplicial complex with " << K.size() << " simplices\n";

  // Pre-processing ----------------------------------------------------
  //
  // Determine the minimum and the maximum weight. If desired by the
  // user, normalize those weights and/or invert them.

  DataType maxWeight = std::numeric_limits<DataType>::lowest();
  DataType minWeight = std::numeric_limits<DataType>::max();
  for( auto&& simplex : K )
  {
    maxWeight = std::max( maxWeight, simplex.data() );
    minWeight = std::min( minWeight, simplex.data() );
  }

  if( normalize && maxWeight != minWeight )
  {
    std::cerr << "* Normalizing weights to [0,1]...";

    auto range = maxWeight - minWeight;

    for (auto it = K.begin(); it != K.end(); ++it )
    {
      if( it->dimension() == 0 )
        continue;

      auto s = *it;

      s.setData( ( s.data() - minWeight ) / range );
      K.replace( it, s );
    }

    maxWeight = DataType(1);
    minWeight = DataType(0);

    std::cerr << "finished\n";
  }

  if( invertWeights )
  {
    std::cerr << "* Inverting filtration weights...";

    for( auto it = K.begin(); it != K.end(); ++it )
    {
      if( it->dimension() == 0 )
        continue;

      auto s = *it;
      s.setData( maxWeight - s.data() );

      K.replace( it, s );
    }

    std::cerr << "finished\n";
  }

  // Rips expansion ----------------------------------------------------
  //
  // The Rips expander is a generic class for expanding a graph into a
  // higher-dimensional simplicial complex. This follows the procedure
  // described in:
  //
  //   Fast construction of the Vietoris--Rips complex
  //   Afra Zomorodian
  //   Computers & Graphics, Volume 34, Issue 3, pp. 263-–271
  //
  // Formally speaking, the algorithm creates a k-simplex for subsets
  // of simplices in which k+1 simplices have pairwise intersections.
  //
  // Hence, if all three edges of a triangle are present, a 2-simplex
  // will be added to the simplicial complex.

  std::cerr << "* Expanding simplicial complex...";

  // By default, tThis leaves the simplicial complex untouched. The
  // simplices with highest dimension are the 1-simplices, i.e. the
  // edges. If the user specified an optional parameter, we use it.
  unsigned k = 1;

  if( argc - optind > 0 )
    k = static_cast<unsigned>( std::stoul( argv[optind++] ) );

  aleph::geometry::RipsExpander<SimplicialComplex> ripsExpander;
  K = ripsExpander( K, k );

  // This tells the expander to use the maximum weight of the faces of
  // a simplex in order to assign the weight of the simplex. Thus, the
  // simplicial complex models a sublevel set filtration after sorting
  // it with the appropriate functor.
  K = ripsExpander.assignMaximumWeight( K );

  std::cerr << "...finished\n"
            << "* Expanded complex has dimension " << K.dimension() << "\n"
            << "* Expanded complex has " << K.size() << " simplices\n";

  std::cerr << "* Establishing filtration order...";

  // A filtration is an ordering of K in which faces precede co-faces in
  // order to ensure that the ordering represents a growing complex. The
  // functor used below sorts simplices based on their data. It hence is
  // just another way of expressing the weights specified in the graph.
  //
  // The data-based filtration ensures that upon coinciding weights, the
  // lower-dimensional simplex will precede the higher-dimensional one.
  K.sort(
    aleph::topology::filtrations::Data<Simplex>()
  );

  std::cerr << "...finished\n";

  // Persistent homology calculation -----------------------------------
  //
  // This uses a convenience function. It expects a simplicial complex
  // in filtration order, calculates it persistent homology using the
  // default algorithms, and returns a vector of persistence diagrams.

  std::cerr << "* Calculating persistent homology...";

  auto diagrams
    = aleph::calculatePersistenceDiagrams( K );

  std::cerr << "...finished\n";

  for( auto&& D : diagrams )
  {
    // This removes all features with a persistence of zero. They only
    // clutter up the diagram.
    D.removeDiagonal();

    if( std::isfinite( infinity ) && infinity != std::numeric_limits<DataType>::max() )
    {
      std::cerr << "* Transforming unpaired points in persistence diagram with a factor of " << infinity << "...\n";

      using PersistenceDiagram = aleph::PersistenceDiagram<DataType>;

      std::transform( D.begin(), D.end(), D.begin(),
          [&maxWeight, &infinity] ( const PersistenceDiagram::Point& p )
          {
            if( !std::isfinite( p.y() ) )
              return PersistenceDiagram::Point( p.x(), infinity * maxWeight );
            else
              return PersistenceDiagram::Point( p );
          } );
    }

    std::ostringstream stream;

    // This 'header' contains some informative entries about the
    // persistence diagram.
    //
    // The Betti number counts how many simplices are without a
    // partner in the diagram.
    //
    // Notice that the diagram has pre-defined output operator;
    // if you do not like the output, you may of course iterate
    // over the points in the diagram yourself.
    stream << "# Persistence diagram <" << filename << ">\n"
           << "#\n"
           << "# Dimension   : " << D.dimension() << "\n"
           << "# Entries     : " << D.size() << "\n"
           << "# Betti number: " << D.betti() << "\n"
           << D;

    // Use the separator for all but the last persistence diagram. Else,
    // the output format is inconsistent and results in empty files.
    if( D != diagrams.back() )
      stream << "\n\n";

    if( basePath.empty() )
      std::cout << stream.str();
    else
    {
      using namespace aleph::utilities;

      // The formatting ensures that if the user specified an expansion
      // up to, say, 10, the resulting files will use two-digit numbers
      // instead of one-digit numbers, regardless of how many complexes
      // there are.
      auto outputFilename = basePath + "/" + stem( basename( filename ) )
                                     + "_d"
                                     + format( D.dimension(), std::max( K.dimension(), static_cast<std::size_t>(k) ) )
                                     + ".txt";

      std::cerr << "* Storing output in '" << outputFilename << "'...\n";

      std::ofstream out( outputFilename );
      out << stream.str();
    }
  }
}
<|endoftext|>


<commit_before>/*
    Copyright (C) 2012-2013 Collabora Ltd. <info@collabora.com>
      @author George Kiagiadakis <george.kiagiadakis@collabora.com>
    Copyright (C) 2013 basysKom GmbH <info@basyskom.com>
      @author Benjamin Federau <benjamin.federau@basyskom.com>

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "player.h"
#include <QtGui/QGuiApplication>
#include <QtQuick/QQuickView>
#include <QtQml/QQmlContext>
#include <QtQml/QQmlEngine>
#include <QGst/Init>
#include <QGst/Quick/VideoSurface>

int main(int argc, char **argv)
{
#if defined(QTVIDEOSINK_PATH)
    //this allows the example to run from the QtGStreamer build tree without installing QtGStreamer
    qputenv("GST_PLUGIN_PATH", QTVIDEOSINK_PATH);
#endif

    QGuiApplication app(argc, argv);
    QGst::init(&argc, &argv);

    QQuickView view;

    QGst::Quick::VideoSurface *surface = new QGst::Quick::VideoSurface;
    view.rootContext()->setContextProperty(QLatin1String("videoSurface1"), surface);

    Player *player = new Player(&view);
    player->setVideoSink(surface->videoSink());
    player->setUri(QLatin1Literal("http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_surround-fix.avi"));
    view.rootContext()->setContextProperty(QLatin1String("player"), player);

#if defined(UNINSTALLED_IMPORTS_DIR)
    //this allows the example to run from the QtGStreamer build tree without installing QtGStreamer
    view.engine()->addImportPath(QLatin1String(UNINSTALLED_IMPORTS_DIR));
#endif

    view.setSource(QUrl(QLatin1String("qrc:///qmlplayer2.qml")));
    view.show();

    return app.exec();
}
<commit_msg>examples/qmlplayer2: allow setting a different video uri from the command line<commit_after>/*
    Copyright (C) 2012-2013 Collabora Ltd. <info@collabora.com>
      @author George Kiagiadakis <george.kiagiadakis@collabora.com>
    Copyright (C) 2013 basysKom GmbH <info@basyskom.com>
      @author Benjamin Federau <benjamin.federau@basyskom.com>

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "player.h"
#include <QtGui/QGuiApplication>
#include <QtQuick/QQuickView>
#include <QtQml/QQmlContext>
#include <QtQml/QQmlEngine>
#include <QGst/Init>
#include <QGst/Quick/VideoSurface>

int main(int argc, char **argv)
{
#if defined(QTVIDEOSINK_PATH)
    //this allows the example to run from the QtGStreamer build tree without installing QtGStreamer
    qputenv("GST_PLUGIN_PATH", QTVIDEOSINK_PATH);
#endif

    QGuiApplication app(argc, argv);
    QGst::init(&argc, &argv);

    QQuickView view;

    QGst::Quick::VideoSurface *surface = new QGst::Quick::VideoSurface;
    view.rootContext()->setContextProperty(QLatin1String("videoSurface1"), surface);

    Player *player = new Player(&view);
    player->setVideoSink(surface->videoSink());
    if (argc > 1)
        player->setUri(QString::fromLocal8Bit(argv[1]));
    else
        player->setUri(QLatin1Literal("http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_surround-fix.avi"));
    view.rootContext()->setContextProperty(QLatin1String("player"), player);

#if defined(UNINSTALLED_IMPORTS_DIR)
    //this allows the example to run from the QtGStreamer build tree without installing QtGStreamer
    view.engine()->addImportPath(QLatin1String(UNINSTALLED_IMPORTS_DIR));
#endif

    view.setSource(QUrl(QLatin1String("qrc:///qmlplayer2.qml")));
    view.show();

    return app.exec();
}
<|endoftext|>


<commit_before>#include <iostream>
#include <utility>
#include <algorithm>
using namespace std;
typedef pair<int, int> pii;
void chmin(int &a,int &b) { if(a > b) a = b; }
void chmax(int &a,int &b) { if(a < b) a = b; }
#define A first
#define B second
const int MAXN = 15010;
const int MAX = 30010;

int n, k;
int A[MAXN];
int pref[MAXN];
int pref2[MAXN], idx[MAXN];
pii dat[MAXN];

inline void init() {
    for(int i = 0;i <= n;i++) dat[i].A = n, dat[i].B = 0;
}
inline void update(pii &a, pii b) { chmin(a.A, b.A); chmax(a.B, b.B); }
inline void insertVal(int i, pii v) {
    for(;i <= n;i += i & - i) update(dat[i], v);
}
inline pii getVal(int i) {
    pii ret(MAXN, 0);
    for(;i > 0;i -= i & -i) update(ret, dat[i]);
    return ret;
}

bool OK(int M) {
    init();
    pii res;
    for(int i = 1;i < n - 1;i++) {
        int k = n - (lower_bound(pref2, pref2 + n, pref[i] - M) - pref2);
        res = getVal(k);
        if(pref[i] <= M) insertVal(idx[i], pii(1, 1));
        if(res.A < n && res.B > 0) {
            res.A++; res.B++;
            insertVal(idx[i], res);
        }
    }
    return res.A <= k && k <= res.B;
}

int solve(int L, int R) {
    while(R - L > 1) {
        int M = (L + R) / 2;
        if(OK(M)) R = M;
        else L = M;
    }
    return R;
}

int main() {
    ios::sync_with_stdio(false);
    int tcase; cin >> tcase;
    while(tcase--) {
        cin >> n >> k;

        int L = -1, R = 1;
        pref[0] = pref2[0] = 0;
        for(int i = 1;i <= n;i++) {
            int a; cin >> a;
            pref2[i] = (pref[i] = pref[i - 1] + a);
            if(a > 0) R += a; else L += a;
        }
        pref2[n + 1] = pref[n + 1] = L;
        n+=2;
        sort(pref2, pref2 + n);
        for(int i = 1;i < n - 1;i++) {
            idx[i] = n - (lower_bound(pref2, pref2 + n, pref[i]) - pref2);
        }

        cout << solve(L, R) << endl;
    }

    return 0;
}
<commit_msg>add comment<commit_after>// O(n log n log c)
#include <iostream>
#include <utility>
#include <algorithm>
using namespace std;
typedef pair<int, int> pii;
void chmin(int &a,int &b) { if(a > b) a = b; }
void chmax(int &a,int &b) { if(a < b) a = b; }
#define A first
#define B second
const int MAXN = 15010;
const int MAX = 30010;

int n, k;
int A[MAXN];
int pref[MAXN];
int pref2[MAXN], idx[MAXN];
pii dat[MAXN];

inline void init() {
    for(int i = 0;i <= n;i++) dat[i].A = n, dat[i].B = 0;
}
inline void update(pii &a, pii b) { chmin(a.A, b.A); chmax(a.B, b.B); }
inline void insertVal(int i, pii v) {
    for(;i <= n;i += i & - i) update(dat[i], v);
}
inline pii getVal(int i) {
    pii ret(MAXN, 0);
    for(;i > 0;i -= i & -i) update(ret, dat[i]);
    return ret;
}

bool OK(int M) {
    init();
    pii res;
    for(int i = 1;i < n - 1;i++) {
        int k = n - (lower_bound(pref2, pref2 + n, pref[i] - M) - pref2);
        res = getVal(k);
        if(pref[i] <= M) insertVal(idx[i], pii(1, 1));
        if(res.A < n && res.B > 0) {
            res.A++; res.B++;
            insertVal(idx[i], res);
        }
    }
    return res.A <= k && k <= res.B;
}

int solve(int L, int R) {
    while(R - L > 1) {
        int M = (L + R) / 2;
        if(OK(M)) R = M;
        else L = M;
    }
    return R;
}

int main() {
    ios::sync_with_stdio(false);
    int tcase; cin >> tcase;
    while(tcase--) {
        cin >> n >> k;

        int L = -1, R = 1;
        pref[0] = pref2[0] = 0;
        for(int i = 1;i <= n;i++) {
            int a; cin >> a;
            pref2[i] = (pref[i] = pref[i - 1] + a);
            if(a > 0) R += a; else L += a;
        }
        pref2[n + 1] = pref[n + 1] = L;
        n+=2;
        sort(pref2, pref2 + n);
        for(int i = 1;i < n - 1;i++) {
            idx[i] = n - (lower_bound(pref2, pref2 + n, pref[i]) - pref2);
        }

        cout << solve(L, R) << endl;
    }

    return 0;
}
<|endoftext|>