<commit_before>#include "stdafx.h"

#include "GraphHandler.h"
#include "Options.h"

#include <cinder/app/AppNative.h>
#include <cinder/params/Params.h>
#include <cinder/qtime/MovieWriter.h>

using namespace ci;
using namespace ci::app;

class GraphStudioApp : public AppNative {
public:
    void prepareSettings(Settings *settings) override;
    void setup() override;
    void mouseDown(MouseEvent event) override;
    void keyDown(KeyEvent event) override;
    void update() override;
    void draw() override;
    void resize() override;
    void shutdown() override;

    const ColorScheme &getCurrentColorScheme();
private:
    ci::params::InterfaceGlRef	params;
    qtime::MovieWriterRef	mMovieWriter;
    std::map<std::string, ColorScheme> colorSchemes;
    std::vector<std::string> colorSchemeNames;
    GraphHandler gh;
    int prevColorSchemeIndex;
    void addNewColorScheme();
    void storeColorScheme();

    void loadSettings();
    void saveSettings();
};

void GraphStudioApp::prepareSettings(Settings *settings)
{
    settings->enableConsoleWindow();
    settings->setWindowSize(800, 600);
    settings->setFrameRate(60.0f);
}

const ColorScheme &GraphStudioApp::getCurrentColorScheme()
{
    return colorSchemes[colorSchemeNames[Options::instance().currentColorSchemeIdx]];
}

void GraphStudioApp::addNewColorScheme()
{
    auto &cs = Options::instance().currentColorScheme;
    cs = ColorScheme();
    bool freeNameFound = false;
    int freeNameIdx = 0;
    std::string newName;
    do
    {
        newName = "ColorScheme #" + std::to_string(++freeNameIdx);
        if (find(begin(colorSchemeNames), end(colorSchemeNames), newName) == colorSchemeNames.end())
            freeNameFound = true;
    } while (!freeNameFound);
    cs.name = newName;
    colorSchemeNames.push_back(cs.name);
    colorSchemes[cs.name] = cs;
    Options::instance().currentColorSchemeIdx = colorSchemes.size() - 1;
    params->addParam("ColorScheme", colorSchemeNames, &Options::instance().currentColorSchemeIdx);
}

void GraphStudioApp::storeColorScheme()
{
    auto &cs = Options::instance().currentColorScheme;
    colorSchemes[cs.name] = cs;
}

void GraphStudioApp::saveSettings()
{
    ci::XmlTree configXml("graphStudioSettings", "");
    configXml.push_back(ci::XmlTree("nodeSize", std::to_string(Options::instance().nodeSize)));
    configXml.push_back(ci::XmlTree("edgeWidth", std::to_string(Options::instance().edgeWidth)));
    configXml.push_back(ci::XmlTree("highlightedEdgeWidth", std::to_string(Options::instance().highlighedEdgeWidth)));
    configXml.push_back(ci::XmlTree("arrowLength", std::to_string(Options::instance().arrowLength)));
    configXml.push_back(ci::XmlTree("arrowAngle", std::to_string(Options::instance().arrowAngle)));
    configXml.push_back(ci::XmlTree("showEdgeWeights", std::to_string(Options::instance().showEdgeWeights)));
    configXml.push_back(ci::XmlTree("showNodeWeights", std::to_string(Options::instance().showNodeWeights)));
    configXml.push_back(ci::XmlTree("force", std::to_string(Options::instance().force)));
    configXml.push_back(ci::XmlTree("speed", std::to_string(Options::instance().speed)));
    configXml.push_back(ci::XmlTree("edgeWeightScale", std::to_string(Options::instance().edgeWeightScale)));
    ci::XmlTree csList("colorSchemes", "");
    for (const auto &cs : colorSchemes)
    {
        csList.push_back(cs.second.toXml());
    }
    configXml.push_back(csList);

    configXml.write(ci::writeFile("config.xml"));
}

void GraphStudioApp::loadSettings()
{
    ci::XmlTree configXml(ci::loadFile("config.xml"));
    ci::XmlTree settings = configXml.getChild("graphStudioSettings");
    Options::instance().nodeSize = settings.getChild("nodeSize").getValue<float>();
    Options::instance().edgeWidth = settings.getChild("edgeWidth").getValue<float>();
    Options::instance().highlighedEdgeWidth = settings.getChild("highlightedEdgeWidth").getValue<float>();
    Options::instance().arrowLength = settings.getChild("arrowLength").getValue<float>();
    Options::instance().arrowAngle = settings.getChild("arrowAngle").getValue<float>();
    Options::instance().showEdgeWeights = settings.getChild("showEdgeWeights").getValue<bool>();
    Options::instance().showNodeWeights = settings.getChild("showNodeWeights").getValue<bool>();

    Options::instance().force = settings.getChild("force").getValue<float>();
    Options::instance().speed = settings.getChild("speed").getValue<int>();
    Options::instance().edgeWeightScale = settings.getChild("edgeWeightScale").getValue<int>();

    ci::XmlTree csList = settings.getChild("colorSchemes");
    for (auto csIt = csList.begin(); csIt != csList.end(); ++csIt)
    {
        ColorScheme cs = ColorScheme::fromXml(*csIt);
        colorSchemes[cs.name] = cs;
        colorSchemeNames.push_back(cs.name);
    }
}

void GraphStudioApp::setup()
{
    loadSettings();

    params = params::InterfaceGl::create("Graph Studio", Vec2i(200, 310));
    params->addParam("Node Size", &Options::instance().nodeSize, "min=1.0 max=50.0 step=1.0");
    params->addParam("Edge Width", &Options::instance().edgeWidth, "min=0.1 max=10.0 step=0.1");
    params->addParam("Highlighted Edge Width", &Options::instance().highlighedEdgeWidth, "min=0.0 max=10.0 step=0.1");
    params->addParam("Arrow Length", &Options::instance().arrowLength, "min=1.0 max=50.0 step=1.0");
    params->addParam("Arrow Angle", &Options::instance().arrowAngle, "min=0.0 max=90.0 step=1.0");
    params->addParam("Show Edge Weights", &Options::instance().showEdgeWeights);
    params->addParam("Show Node Weights", &Options::instance().showNodeWeights);
    params->addSeparator();
    params->addParam("Algorithm", AlgorithmNames, &Options::instance().algorithm);
    params->addParam("Starting Node",  &Options::instance().startNode, "min=1 step=1");
    params->addSeparator();
    params->addParam("Force", &Options::instance().force, "min=1.0 max=300.0 step=1.0");
    params->addParam("Speed", &Options::instance().speed, "min=1.0 max=300.0 step=1.0");
    params->addParam("Edge Weight Scale", &Options::instance().edgeWeightScale, "min=1.0 max=1000.0 step=1.0");
    params->addSeparator();
    params->addText("Colors");
    params->addParam("ColorScheme", colorSchemeNames, &Options::instance().currentColorSchemeIdx);
    params->addParam("Background", &Options::instance().currentColorScheme.backgroundColor);

    params->addParam("Node ", &Options::instance().currentColorScheme.nodeColor);
    params->addParam("Highlighted Node 1", &Options::instance().currentColorScheme.highlightedNodeColor1);
    params->addParam("Highlighted Node 2", &Options::instance().currentColorScheme.highlightedNodeColor2);
    params->addParam("Highlighted Node 3", &Options::instance().currentColorScheme.highlightedNodeColor3);

    params->addParam("Edge", &Options::instance().currentColorScheme.edgeColor);
    params->addParam("Dark Edge", &Options::instance().currentColorScheme.darkEdgeColor);
    params->addParam("Highlighted Edge 1", &Options::instance().currentColorScheme.highlightedEdgeColor1);
    params->addParam("Highlighted Edge 2", &Options::instance().currentColorScheme.highlightedEdgeColor2);
    params->addParam("Highlighted Edge 3", &Options::instance().currentColorScheme.highlightedEdgeColor3);

    params->addParam("Node Text", &Options::instance().currentColorScheme.nodeTextColor);
    params->addParam("highlightednodeText", &Options::instance().currentColorScheme.highlightednodeTextColor);
    params->addParam("Edge Text", &Options::instance().currentColorScheme.edgeTextColor);
    params->addParam("Highlighted Edge Text", &Options::instance().currentColorScheme.highlightedEdgeTextColor);

    params->addButton("New", std::bind(&GraphStudioApp::addNewColorScheme, this));
    params->addSeparator();
    params->addText("Random Edge Weights");
    params->addParam("Min", &Options::instance().minRandomEdgeWeight, "min=1, max=1000, step=1");
    params->addParam("Max", &Options::instance().maxRandomEdgeWeight, "min=1, max=1000, step=1");
    params->addButton("Generate Weights", std::bind(&GraphHandler::setRandomEdgeWeights, &gh));
    params->addSeparator();
    params->addText("Generate Grid");
    params->addParam("Columns", &GraphParamsGrid::instance().columns, "min=1 step=1");
    params->addParam("Rows", &GraphParamsGrid::instance().rows, "min=1 step=1");
    params->addParam("Directed", &GraphParamsGrid::instance().directed);
    params->addParam("Horizontal Edges", &GraphParamsGrid::instance().horizontal);
    params->addParam("Vertical Edges", &GraphParamsGrid::instance().vertical);
    params->addParam("Diagonal /", &GraphParamsGrid::instance().upDiagonal);
    params->addParam("Diagonal \\", &GraphParamsGrid::instance().downDiagonal);
    params->addButton("Generate grid", std::bind(&GraphHandler::generateSpecialGraph, &gh, GraphHandler::GraphType::grid));
    params->addText("Generate Triangle Mesh");
    params->addParam("Triangles", &GraphParamsTriangleMesh::instance().triangles, "min=1 step=1");
    params->addParam("Randomness", &GraphParamsTriangleMesh::instance().randomness, "min=0.0 step=0.1");
    params->addButton("Generate tri", std::bind(&GraphHandler::generateSpecialGraph, &gh, GraphHandler::GraphType::triangleMesh));

    gh.setup(getWindow());
    /*
    fs::path path = getSaveFilePath();
    if (path.empty())
        return; // user cancelled save
    qtime::MovieWriter::Format format;
    if (qtime::MovieWriter::getUserCompressionSettings(&format)) {
        mMovieWriter = qtime::MovieWriter::create(path, getWindowWidth(), getWindowHeight(), format);
    }
    */
}

void GraphStudioApp::shutdown()
{
    saveSettings();
}

void GraphStudioApp::keyDown(KeyEvent event)
{
    std::string nameBase = "graph_small2";
    if (event.getCode() == KeyEvent::KEY_SPACE)
    {
        // stop - play - pause - play - pause - play - until the last state is reached
        if (!Options::instance().animationPlaying)
        {
            Options::instance().animationPlaying = true;
            Options::instance().animationPaused = false;
            gh.animationPrepare();
        }
        else
        {
            Options::instance().animationPaused = !Options::instance().animationPaused;
            if (Options::instance().animationPaused)
            {
                gh.animationPause();
            }
            else
            {
                gh.animationResume();
            }
        }

        return;
    }
    else  if (event.getCode() == KeyEvent::KEY_RIGHT)
    {
        gh.animationNext();
        return;
    }
    else  if (event.getCode() == KeyEvent::KEY_LEFT)
    {
        gh.animationPrevious();
        return;
    }
    if (event.getChar() == 'm')
    {
        Options::instance().randomMovement = !Options::instance().randomMovement;
        if (Options::instance().randomMovement)
            Options::instance().animationPlaying = false;
    }
    if (event.getChar() == 's')
    {
        std::cout << "Saving graph..." << std::endl;
        gh.saveGraph(nameBase + ".txt");
        gh.saveGraphPositions(nameBase + ".pos");
        std::cout << "Done" << std::endl;
    }
    if (event.getChar() == 'l')
    {
        std::cout << "Loading graph..." << std::endl;

        gh.loadGraph(nameBase + ".txt");
        gh.loadGraphPositions(nameBase + ".pos");
        Options::instance().startNode = 1;
        gh.fitToWindow();
        std::cout << "Done" << std::endl;
    }
    if (event.getChar() == 'u')
    {
        gh.toggleAutomaticEdgeWeightUpdate();
        std::cout << "automaticEdgeWeightUpdate = " << gh.getAutomaticEdgeWeightUpdate() << std::endl;
    }
    if (event.getChar() == 'f')
    {
        gh.fitToWindow();
    }
    if (event.getChar() == 'r')
    {
        gh.reorderNodesSquare();
    }

    if (event.getChar() == 'q')
    {
        saveSettings();
        quit();
    }

}

void GraphStudioApp::mouseDown( MouseEvent event )
{
    //std::cout << "GraphStudioApp::mouseDown" << std::endl;
}

void GraphStudioApp::update()
{
    gh.update();
    if (prevColorSchemeIndex != Options::instance().currentColorSchemeIdx)
    {
        prevColorSchemeIndex = Options::instance().currentColorSchemeIdx;
        Options::instance().currentColorScheme = colorSchemes[colorSchemeNames[Options::instance().currentColorSchemeIdx]];
    }
    else
    {
        storeColorScheme();
    }
}

void GraphStudioApp::draw()
{
    // clear out the window with black
    gl::clear(Color(0, 0, 0));
    gh.draw();
    params->draw();
    /*
    if (mMovieWriter)
        mMovieWriter->addFrame(copyWindowSurface());
    */
}

void GraphStudioApp::resize()
{
    gh.resize(getWindowBounds());
}

CINDER_APP_NATIVE( GraphStudioApp, RendererGl )
<commit_msg>Added save and load graph dialog<commit_after>#include "stdafx.h"

#include "GraphHandler.h"
#include "Options.h"

#include <cinder/app/AppNative.h>
#include <cinder/params/Params.h>
#include <cinder/qtime/MovieWriter.h>

using namespace ci;
using namespace ci::app;

class GraphStudioApp : public AppNative {
public:
    void prepareSettings(Settings *settings) override;
    void setup() override;
    void mouseDown(MouseEvent event) override;
    void keyDown(KeyEvent event) override;
    void update() override;
    void draw() override;
    void resize() override;
    void shutdown() override;

    const ColorScheme &getCurrentColorScheme();
private:
    ci::params::InterfaceGlRef	params;
    qtime::MovieWriterRef	mMovieWriter;
    std::map<std::string, ColorScheme> colorSchemes;
    std::vector<std::string> colorSchemeNames;
    GraphHandler gh;
    int prevColorSchemeIndex;
    void addNewColorScheme();
    void storeColorScheme();

    void loadSettings();
    void saveSettings();
};

void GraphStudioApp::prepareSettings(Settings *settings)
{
    settings->enableConsoleWindow();
    settings->setWindowSize(800, 600);
    settings->setFrameRate(60.0f);
}

const ColorScheme &GraphStudioApp::getCurrentColorScheme()
{
    return colorSchemes[colorSchemeNames[Options::instance().currentColorSchemeIdx]];
}

void GraphStudioApp::addNewColorScheme()
{
    auto &cs = Options::instance().currentColorScheme;
    cs = ColorScheme();
    bool freeNameFound = false;
    int freeNameIdx = 0;
    std::string newName;
    do
    {
        newName = "ColorScheme #" + std::to_string(++freeNameIdx);
        if (find(begin(colorSchemeNames), end(colorSchemeNames), newName) == colorSchemeNames.end())
            freeNameFound = true;
    } while (!freeNameFound);
    cs.name = newName;
    colorSchemeNames.push_back(cs.name);
    colorSchemes[cs.name] = cs;
    Options::instance().currentColorSchemeIdx = colorSchemes.size() - 1;
    params->addParam("ColorScheme", colorSchemeNames, &Options::instance().currentColorSchemeIdx);
}

void GraphStudioApp::storeColorScheme()
{
    auto &cs = Options::instance().currentColorScheme;
    colorSchemes[cs.name] = cs;
}

void GraphStudioApp::saveSettings()
{
    ci::XmlTree configXml("graphStudioSettings", "");
    configXml.push_back(ci::XmlTree("nodeSize", std::to_string(Options::instance().nodeSize)));
    configXml.push_back(ci::XmlTree("edgeWidth", std::to_string(Options::instance().edgeWidth)));
    configXml.push_back(ci::XmlTree("highlightedEdgeWidth", std::to_string(Options::instance().highlighedEdgeWidth)));
    configXml.push_back(ci::XmlTree("arrowLength", std::to_string(Options::instance().arrowLength)));
    configXml.push_back(ci::XmlTree("arrowAngle", std::to_string(Options::instance().arrowAngle)));
    configXml.push_back(ci::XmlTree("showEdgeWeights", std::to_string(Options::instance().showEdgeWeights)));
    configXml.push_back(ci::XmlTree("showNodeWeights", std::to_string(Options::instance().showNodeWeights)));
    configXml.push_back(ci::XmlTree("force", std::to_string(Options::instance().force)));
    configXml.push_back(ci::XmlTree("speed", std::to_string(Options::instance().speed)));
    configXml.push_back(ci::XmlTree("edgeWeightScale", std::to_string(Options::instance().edgeWeightScale)));
    ci::XmlTree csList("colorSchemes", "");
    for (const auto &cs : colorSchemes)
    {
        csList.push_back(cs.second.toXml());
    }
    configXml.push_back(csList);

    configXml.write(ci::writeFile("config.xml"));
}

void GraphStudioApp::loadSettings()
{
    ci::XmlTree configXml(ci::loadFile("config.xml"));
    ci::XmlTree settings = configXml.getChild("graphStudioSettings");
    Options::instance().nodeSize = settings.getChild("nodeSize").getValue<float>();
    Options::instance().edgeWidth = settings.getChild("edgeWidth").getValue<float>();
    Options::instance().highlighedEdgeWidth = settings.getChild("highlightedEdgeWidth").getValue<float>();
    Options::instance().arrowLength = settings.getChild("arrowLength").getValue<float>();
    Options::instance().arrowAngle = settings.getChild("arrowAngle").getValue<float>();
    Options::instance().showEdgeWeights = settings.getChild("showEdgeWeights").getValue<bool>();
    Options::instance().showNodeWeights = settings.getChild("showNodeWeights").getValue<bool>();

    Options::instance().force = settings.getChild("force").getValue<float>();
    Options::instance().speed = settings.getChild("speed").getValue<int>();
    Options::instance().edgeWeightScale = settings.getChild("edgeWeightScale").getValue<int>();

    ci::XmlTree csList = settings.getChild("colorSchemes");
    for (auto csIt = csList.begin(); csIt != csList.end(); ++csIt)
    {
        ColorScheme cs = ColorScheme::fromXml(*csIt);
        colorSchemes[cs.name] = cs;
        colorSchemeNames.push_back(cs.name);
    }
}

void GraphStudioApp::setup()
{
    loadSettings();

    params = params::InterfaceGl::create("Graph Studio", Vec2i(200, 310));
    params->addParam("Node Size", &Options::instance().nodeSize, "min=1.0 max=50.0 step=1.0");
    params->addParam("Edge Width", &Options::instance().edgeWidth, "min=0.1 max=10.0 step=0.1");
    params->addParam("Highlighted Edge Width", &Options::instance().highlighedEdgeWidth, "min=0.0 max=10.0 step=0.1");
    params->addParam("Arrow Length", &Options::instance().arrowLength, "min=1.0 max=50.0 step=1.0");
    params->addParam("Arrow Angle", &Options::instance().arrowAngle, "min=0.0 max=90.0 step=1.0");
    params->addParam("Show Edge Weights", &Options::instance().showEdgeWeights);
    params->addParam("Show Node Weights", &Options::instance().showNodeWeights);
    params->addSeparator();
    params->addParam("Algorithm", AlgorithmNames, &Options::instance().algorithm);
    params->addParam("Starting Node",  &Options::instance().startNode, "min=1 step=1");
    params->addSeparator();
    params->addParam("Force", &Options::instance().force, "min=1.0 max=300.0 step=1.0");
    params->addParam("Speed", &Options::instance().speed, "min=1.0 max=300.0 step=1.0");
    params->addParam("Edge Weight Scale", &Options::instance().edgeWeightScale, "min=1.0 max=1000.0 step=1.0");
    params->addSeparator();
    params->addText("Colors");
    params->addParam("ColorScheme", colorSchemeNames, &Options::instance().currentColorSchemeIdx);
    params->addParam("Background", &Options::instance().currentColorScheme.backgroundColor);

    params->addParam("Node ", &Options::instance().currentColorScheme.nodeColor);
    params->addParam("Highlighted Node 1", &Options::instance().currentColorScheme.highlightedNodeColor1);
    params->addParam("Highlighted Node 2", &Options::instance().currentColorScheme.highlightedNodeColor2);
    params->addParam("Highlighted Node 3", &Options::instance().currentColorScheme.highlightedNodeColor3);

    params->addParam("Edge", &Options::instance().currentColorScheme.edgeColor);
    params->addParam("Dark Edge", &Options::instance().currentColorScheme.darkEdgeColor);
    params->addParam("Highlighted Edge 1", &Options::instance().currentColorScheme.highlightedEdgeColor1);
    params->addParam("Highlighted Edge 2", &Options::instance().currentColorScheme.highlightedEdgeColor2);
    params->addParam("Highlighted Edge 3", &Options::instance().currentColorScheme.highlightedEdgeColor3);

    params->addParam("Node Text", &Options::instance().currentColorScheme.nodeTextColor);
    params->addParam("highlightednodeText", &Options::instance().currentColorScheme.highlightednodeTextColor);
    params->addParam("Edge Text", &Options::instance().currentColorScheme.edgeTextColor);
    params->addParam("Highlighted Edge Text", &Options::instance().currentColorScheme.highlightedEdgeTextColor);

    params->addButton("New", std::bind(&GraphStudioApp::addNewColorScheme, this));
    params->addSeparator();
    params->addText("Random Edge Weights");
    params->addParam("Min", &Options::instance().minRandomEdgeWeight, "min=1, max=1000, step=1");
    params->addParam("Max", &Options::instance().maxRandomEdgeWeight, "min=1, max=1000, step=1");
    params->addButton("Generate Weights", std::bind(&GraphHandler::setRandomEdgeWeights, &gh));
    params->addSeparator();
    params->addText("Generate Grid");
    params->addParam("Columns", &GraphParamsGrid::instance().columns, "min=1 step=1");
    params->addParam("Rows", &GraphParamsGrid::instance().rows, "min=1 step=1");
    params->addParam("Directed", &GraphParamsGrid::instance().directed);
    params->addParam("Horizontal Edges", &GraphParamsGrid::instance().horizontal);
    params->addParam("Vertical Edges", &GraphParamsGrid::instance().vertical);
    params->addParam("Diagonal /", &GraphParamsGrid::instance().upDiagonal);
    params->addParam("Diagonal \\", &GraphParamsGrid::instance().downDiagonal);
    params->addButton("Generate grid", std::bind(&GraphHandler::generateSpecialGraph, &gh, GraphHandler::GraphType::grid));
    params->addText("Generate Triangle Mesh");
    params->addParam("Triangles", &GraphParamsTriangleMesh::instance().triangles, "min=1 step=1");
    params->addParam("Randomness", &GraphParamsTriangleMesh::instance().randomness, "min=0.0 step=0.1");
    params->addButton("Generate tri", std::bind(&GraphHandler::generateSpecialGraph, &gh, GraphHandler::GraphType::triangleMesh));

    gh.setup(getWindow());
    /*
    fs::path path = getSaveFilePath();
    if (path.empty())
        return; // user cancelled save
    qtime::MovieWriter::Format format;
    if (qtime::MovieWriter::getUserCompressionSettings(&format)) {
        mMovieWriter = qtime::MovieWriter::create(path, getWindowWidth(), getWindowHeight(), format);
    }
    */
}

void GraphStudioApp::shutdown()
{
    saveSettings();
}

void GraphStudioApp::keyDown(KeyEvent event)
{
    std::string nameBase = "graph_small2";
    static const std::vector<std::string> extensions{ "graph", "txt" };
    if (event.getCode() == KeyEvent::KEY_SPACE)
    {
        // stop - play - pause - play - pause - play - until the last state is reached
        if (!Options::instance().animationPlaying)
        {
            Options::instance().animationPlaying = true;
            Options::instance().animationPaused = false;
            gh.animationPrepare();
        }
        else
        {
            Options::instance().animationPaused = !Options::instance().animationPaused;
            if (Options::instance().animationPaused)
            {
                gh.animationPause();
            }
            else
            {
                gh.animationResume();
            }
        }

        return;
    }
    else  if (event.getCode() == KeyEvent::KEY_RIGHT)
    {
        gh.animationNext();
        return;
    }
    else  if (event.getCode() == KeyEvent::KEY_LEFT)
    {
        gh.animationPrevious();
        return;
    }
    if (event.getChar() == 'm')
    {
        Options::instance().randomMovement = !Options::instance().randomMovement;
        if (Options::instance().randomMovement)
            Options::instance().animationPlaying = false;
    }
    if (event.getChar() == 's')
    {
        boost::filesystem::path path;
        if (event.isAltDown())
        {
            path = getSaveFilePath("", extensions);
            if (path.empty())
                return;

            if (path.extension().empty())
            {
                path += ".graph";
            }
            gh.saveGraph(path.string());
            gh.saveGraphPositions(path.replace_extension("pos").string());
        }
        else
        {
            gh.saveGraph(nameBase + ".txt");
            gh.saveGraphPositions(nameBase + ".pos");
        }
    }
    if (event.getChar() == 'l')
    {
        boost::filesystem::path path;
        if (event.isAltDown())
        {
            path = getOpenFilePath(nameBase + ".txt", extensions);
            if (path.empty())
                return;

            gh.loadGraph(path.string());
            gh.loadGraphPositions(path.replace_extension("pos").string());
        }
        else
        {
            gh.loadGraph(nameBase + ".txt");
            gh.loadGraphPositions(nameBase + ".pos");
        }

        Options::instance().startNode = 1;
        gh.fitToWindow();
    }
    if (event.getChar() == 'u')
    {
        gh.toggleAutomaticEdgeWeightUpdate();
        std::cout << "automaticEdgeWeightUpdate = " << gh.getAutomaticEdgeWeightUpdate() << std::endl;
    }
    if (event.getChar() == 'f')
    {
        gh.fitToWindow();
    }
    if (event.getChar() == 'r')
    {
        gh.reorderNodesSquare();
    }

    if (event.getChar() == 'q')
    {
        saveSettings();
        quit();
    }

}

void GraphStudioApp::mouseDown( MouseEvent event )
{
    //std::cout << "GraphStudioApp::mouseDown" << std::endl;
}

void GraphStudioApp::update()
{
    gh.update();
    if (prevColorSchemeIndex != Options::instance().currentColorSchemeIdx)
    {
        prevColorSchemeIndex = Options::instance().currentColorSchemeIdx;
        Options::instance().currentColorScheme = colorSchemes[colorSchemeNames[Options::instance().currentColorSchemeIdx]];
    }
    else
    {
        storeColorScheme();
    }
}

void GraphStudioApp::draw()
{
    // clear out the window with black
    gl::clear(Color(0, 0, 0));
    gh.draw();
    params->draw();
    /*
    if (mMovieWriter)
        mMovieWriter->addFrame(copyWindowSurface());
    */
}

void GraphStudioApp::resize()
{
    gh.resize(getWindowBounds());
}

CINDER_APP_NATIVE( GraphStudioApp, RendererGl )
<|endoftext|>


<commit_before>#include "scriptfile.hpp"
#include "npafile.hpp"

#include <cstring>
#include <fstream>

ScriptFile::ScriptFile(std::string Name, char* NsbData, uint32_t NsbSize, char* MapData, uint32_t MapSize) :
Name(Name)
{
    Open(NsbData, NsbSize, MapData, MapSize);
}

ScriptFile::ScriptFile(std::string Name) :
Name(Name)
{
    std::ifstream NsbFile(Name, std::ios::binary);
    NsbFile.seekg(0, std::ios::end);
    uint32_t NsbSize = NsbFile.tellg();
    char* NsbData = new char[NsbSize];
    NsbFile.seekg(0, std::ios::beg);
    NsbFile.read(NsbData, NsbSize);

    std::string MapName = std::string(Name, 0, Name.size() - 3) + "map";
    std::ifstream MapFile(MapName, std::ios::binary);
    MapFile.seekg(0, std::ios::end);
    uint32_t MapSize = MapFile.tellg();
    char* MapData = new char[MapSize];
    MapFile.seekg(0, std::ios::beg);
    MapFile.read(MapData, MapSize);

    Open(NsbData, NsbSize, MapData, MapSize);

    delete[] NsbData;
    delete[] MapData;
}

void ScriptFile::Open(char* NsbData, uint32_t NsbSize, char* MapData, uint32_t MapSize)
{
    uint32_t Entry, Length;
    uint16_t NumParams;
    Line* CurrLine;
    char* Iter;

    // Read source code lines
    Iter = NsbData;
    while (Iter < NsbData + NsbSize)
    {
        Read(&Iter, &Entry, sizeof(uint32_t));
        Entry -= 1; // Start counting at zero
        Source.resize(Source.size() + 1);
        CurrLine = &Source[Entry];
        Read(&Iter, &CurrLine->Magic, sizeof(uint16_t));
        Read(&Iter, &NumParams, sizeof(uint16_t));
        CurrLine->Params.reserve(NumParams);

        // Read parameters
        for (uint16_t i = 0; i < NumParams; ++i)
        {
            Read(&Iter, &Length, sizeof(uint32_t));
            char* String = new char[Length];
            Read(&Iter, String, Length);
            CurrLine->Params.push_back(NpaFile::ToUtf8(std::string(String, Length)));
            delete[] String;
        }
    }

    uint32_t Offset;
    uint16_t Size;
    std::string Label;

    // Read symbols
    Iter = MapData;
    while (Iter < MapData + MapSize)
    {
        Read(&Iter, &Offset, sizeof(uint32_t));
        Read(&Iter, &Size, sizeof(uint16_t));
        Label.resize(Size);
        Read(&Iter, &Label[0], Size);
        std::memcpy(&Entry, NsbData + Offset, sizeof(uint32_t));
        Symbols[Label] = Entry - 1;
    }
}

void ScriptFile::Read(char** Src, void* Dest, uint32_t Size)
{
    std::memcpy(Dest, *Src, Size);
    *Src += Size;
}

uint32_t ScriptFile::GetSymbol(const std::string& Symbol)
{
    auto iter = Symbols.find(Symbol);
    if (iter != Symbols.end())
        return iter->second;
    return NSB_INVALIDE_LINE;
}
<commit_msg>Convert symbols to UTF-8<commit_after>#include "scriptfile.hpp"
#include "npafile.hpp"

#include <cstring>
#include <fstream>

ScriptFile::ScriptFile(std::string Name, char* NsbData, uint32_t NsbSize, char* MapData, uint32_t MapSize) :
Name(Name)
{
    Open(NsbData, NsbSize, MapData, MapSize);
}

ScriptFile::ScriptFile(std::string Name) :
Name(Name)
{
    std::ifstream NsbFile(Name, std::ios::binary);
    NsbFile.seekg(0, std::ios::end);
    uint32_t NsbSize = NsbFile.tellg();
    char* NsbData = new char[NsbSize];
    NsbFile.seekg(0, std::ios::beg);
    NsbFile.read(NsbData, NsbSize);

    std::string MapName = std::string(Name, 0, Name.size() - 3) + "map";
    std::ifstream MapFile(MapName, std::ios::binary);
    MapFile.seekg(0, std::ios::end);
    uint32_t MapSize = MapFile.tellg();
    char* MapData = new char[MapSize];
    MapFile.seekg(0, std::ios::beg);
    MapFile.read(MapData, MapSize);

    Open(NsbData, NsbSize, MapData, MapSize);

    delete[] NsbData;
    delete[] MapData;
}

void ScriptFile::Open(char* NsbData, uint32_t NsbSize, char* MapData, uint32_t MapSize)
{
    uint32_t Entry, Length;
    uint16_t NumParams;
    Line* CurrLine;
    char* Iter;

    // Read source code lines
    Iter = NsbData;
    while (Iter < NsbData + NsbSize)
    {
        Read(&Iter, &Entry, sizeof(uint32_t));
        Entry -= 1; // Start counting at zero
        Source.resize(Source.size() + 1);
        CurrLine = &Source[Entry];
        Read(&Iter, &CurrLine->Magic, sizeof(uint16_t));
        Read(&Iter, &NumParams, sizeof(uint16_t));
        CurrLine->Params.reserve(NumParams);

        // Read parameters
        for (uint16_t i = 0; i < NumParams; ++i)
        {
            Read(&Iter, &Length, sizeof(uint32_t));
            char* String = new char[Length];
            Read(&Iter, String, Length);
            CurrLine->Params.push_back(NpaFile::ToUtf8(std::string(String, Length)));
            delete[] String;
        }
    }

    uint32_t Offset;
    uint16_t Size;
    std::string Label;

    // Read symbols
    Iter = MapData;
    while (Iter < MapData + MapSize)
    {
        Read(&Iter, &Offset, sizeof(uint32_t));
        Read(&Iter, &Size, sizeof(uint16_t));
        Label.resize(Size);
        Read(&Iter, &Label[0], Size);
        std::memcpy(&Entry, NsbData + Offset, sizeof(uint32_t));
        Symbols[NpaFile::ToUtf8(Label)] = Entry - 1;
    }
}

void ScriptFile::Read(char** Src, void* Dest, uint32_t Size)
{
    std::memcpy(Dest, *Src, Size);
    *Src += Size;
}

uint32_t ScriptFile::GetSymbol(const std::string& Symbol)
{
    auto iter = Symbols.find(Symbol);
    if (iter != Symbols.end())
        return iter->second;
    return NSB_INVALIDE_LINE;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <string.h>
#include "defineRubik.h"
using namespace std;

int main(int argc, char* argv[]){
   Rubik rubik, aux;
   ofstream os;
   vector<char> solution;
   bool boolean = true;
   string file;
   int i = 1;

   if (argv[1][0] == '-' && argv[1][1] == 's' && argc == 2){ // Search mode
     for (unsigned int j=0; j<100000 && boolean; j++){
       aux = rubik = Rubik::randomRubik();

       rubik.solve(solution);
       if(!rubik.isSolved()){
         file += "./data/error" + i;
         os.open(file);

         // Original rubik is solved, we want original positions to debug
         os << aux;
         os.close();
         boolean = false;
         ++i;
       }
     }
     if (!boolean)
       cout << "There are bugs :D" << endl;
   }
   else{ // Fix bugs mode
     ifstream is(argv[1]);

     is >> rubik;
     cout << rubik;

     rubik.solveStepByStep(solution);
     cout << rubik;
   }

   cout << endl;
}
<commit_msg>No bugs<commit_after>#include <iostream>
#include <fstream>
#include <string.h>
#include "defineRubik.h"
using namespace std;

int main(int argc, char* argv[]){
   Rubik rubik, aux;
   ofstream os;
   vector<char> solution;
   bool boolean = true;
   string file;
   int i = 1;

   if (argv[1][0] == '-' && argv[1][1] == 's' && argc == 2){ // Search mode
     for (unsigned int j=0; j<100000 && boolean; j++){
       aux = rubik = Rubik::randomRubik();

       rubik.solve(solution);
       if(!rubik.isSolved()){
         file += "./data/error" + i;
         os.open(file);

         // Original rubik is solved, we want original positions to debug
         os << aux;
         os.close();

         file = "";
         boolean = false;
         ++i;
       }
     }
     if (!boolean)
       cout << "There are bugs :D" << endl;
     else
       cout << "There aren't any bugs" << endl;
   }
   else{ // Fix bugs mode
     ifstream is(argv[1]);

     is >> rubik;
     cout << rubik;

     rubik.solveStepByStep(solution);
     cout << rubik;
   }

   cout << endl;
}
<|endoftext|>


<commit_before>// This file is part of meshoptimizer library; see meshoptimizer.hpp for version/license details
#include "meshoptimizer.hpp"

#include <algorithm>
#include <cassert>
#include <cmath>
#include <vector>

// This work is based on:
// Michael Garland and Paul S. Heckbert. Surface simplification using quadric error metrics. 1997
namespace meshopt
{

static size_t hash(unsigned long long key)
{
	key = (~key) + (key << 18);
	key = key ^ (key >> 31);
	key = key * 21;
	key = key ^ (key >> 11);
	key = key + (key << 6);
	key = key ^ (key >> 22);
	return size_t(key);
}

template <typename T>
static void hashInit(std::vector<T>& table, size_t count)
{
	size_t buckets = 1;
	while (buckets < count)
		buckets *= 2;

	table.clear();
	table.resize(buckets);
}

template <typename T>
static T* hashLookup(std::vector<T>& table, const T& key)
{
	assert((table.size() & (table.size() - 1)) == 0);

	size_t hashmod = table.size() - 1;
	size_t bucket = hash(key) & hashmod;

	for (size_t probe = 0; probe <= hashmod; ++probe)
	{
		T& item = table[bucket];

		if (item == T() || item == key)
			return &item;

		// hash collision, quadratic probing
		bucket = (bucket + probe + 1) & hashmod;
	}

	assert(false && "Hash table is full");
	return 0;
}

struct Vector3
{
	float x, y, z;
};

struct Quadric
{
	float a00;
	float a10, a11;
	float a20, a21, a22;
	float b0, b1, b2, c;
};

struct Collapse
{
	size_t v0;
	size_t v1;
	float error;

	bool operator<(const Collapse& other) const
	{
		return error < other.error;
	}
};

static float normalize(Vector3& v)
{
	float length = sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);

	if (length > 0)
	{
		v.x /= length;
		v.y /= length;
		v.z /= length;
	}

	return length;
}

static void quadricAdd(Quadric& Q, const Quadric& R)
{
	Q.a00 += R.a00;
	Q.a10 += R.a10;
	Q.a11 += R.a11;
	Q.a20 += R.a20;
	Q.a21 += R.a21;
	Q.a22 += R.a22;
	Q.b0 += R.b0;
	Q.b1 += R.b1;
	Q.b2 += R.b2;
	Q.c += R.c;
}

static void quadricMul(Quadric& Q, float s)
{
	Q.a00 *= s;
	Q.a10 *= s;
	Q.a11 *= s;
	Q.a20 *= s;
	Q.a21 *= s;
	Q.a22 *= s;
	Q.b0 *= s;
	Q.b1 *= s;
	Q.b2 *= s;
	Q.c *= s;
}

static float quadricError(Quadric& Q, const Vector3& v)
{
	float xx = v.x * v.x;
	float xy = v.x * v.y;
	float xz = v.x * v.z;
	float yy = v.y * v.y;
	float yz = v.y * v.z;
	float zz = v.z * v.z;

	float vTQv = Q.a00 * xx + Q.a10 * xy * 2 + Q.a11 * yy + Q.a20 * xz * 2 + Q.a21 * yz * 2 + Q.a22 * zz + Q.b0 * v.x * 2 + Q.b1 * v.y * 2 + Q.b2 * v.z * 2 + Q.c;

	return fabsf(vTQv);
}

static void quadricFromPlane(Quadric& Q, float a, float b, float c, float d)
{
	Q.a00 = a * a;
	Q.a10 = b * a;
	Q.a11 = b * b;
	Q.a20 = c * a;
	Q.a21 = c * b;
	Q.a22 = c * c;
	Q.b0 = d * a;
	Q.b1 = d * b;
	Q.b2 = d * c;
	Q.c = d * d;
}

static void quadricFromTriangle(Quadric& Q, const Vector3& p0, const Vector3& p1, const Vector3& p2)
{
	Vector3 p10 = { p1.x - p0.x, p1.y - p0.y, p1.z - p0.z };
	Vector3 p20 = { p2.x - p0.x, p2.y - p0.y, p2.z - p0.z };

	Vector3 normal = { p10.y * p20.z - p10.z * p20.y, p10.z * p20.x - p10.x * p20.z, p10.x * p20.y - p10.y * p20.x };
	float area = normalize(normal);

	float distance = normal.x * p0.x + normal.y * p0.y + normal.z * p0.z;

	quadricFromPlane(Q, normal.x, normal.y, normal.z, -distance);

	// Three classical weighting methods include weight=1, weight=area and weight=area^2
	// We use weight=area for now
	quadricMul(Q, area);
}

static void quadricFromTriangleEdge(Quadric& Q, const Vector3& p0, const Vector3& p1, const Vector3& p2)
{
	Vector3 p10 = { p1.x - p0.x, p1.y - p0.y, p1.z - p0.z };
	float length = normalize(p10);

	Vector3 p20 = { p2.x - p0.x, p2.y - p0.y, p2.z - p0.z };
	float p20p = p20.x * p10.x + p20.y * p10.y + p20.z * p10.z;

	Vector3 normal = { p20.x - p10.x * p20p, p20.y - p10.y * p20p, p20.z - p10.z * p20p };
	normalize(normal);

	float distance = normal.x * p0.x + normal.y * p0.y + normal.z * p0.z;

	quadricFromPlane(Q, normal.x, normal.y, normal.z, -distance);

	quadricMul(Q, length * 1000);
}

static unsigned long long edgeId(unsigned int a, unsigned int b)
{
	return (static_cast<unsigned long long>(a) << 32) | b;
}

static size_t simplifyEdgeCollapse(unsigned int* result, const unsigned int* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size, size_t target_index_count)
{
	std::vector<Vector3> vertex_positions(vertex_count);

	for (size_t i = 0; i < vertex_count; ++i)
	{
		const float* v = reinterpret_cast<const float*>(static_cast<const char*>(vertices) + i * vertex_size);

		vertex_positions[i].x = v[0];
		vertex_positions[i].y = v[1];
		vertex_positions[i].z = v[2];
	}

	std::vector<Quadric> vertex_quadrics(vertex_count);

	// face quadrics
	for (size_t i = 0; i < index_count; i += 3)
	{
		Quadric Q;
		quadricFromTriangle(Q, vertex_positions[indices[i + 0]], vertex_positions[indices[i + 1]], vertex_positions[indices[i + 2]]);

		quadricAdd(vertex_quadrics[indices[i + 0]], Q);
		quadricAdd(vertex_quadrics[indices[i + 1]], Q);
		quadricAdd(vertex_quadrics[indices[i + 2]], Q);
	}

	// edge quadrics for boundary edges
	std::vector<unsigned long long> edges;
	hashInit(edges, index_count);

	for (size_t i = 0; i < index_count; i += 3)
	{
		static const int next[3] = { 1, 2, 0 };

		for (int e = 0; e < 3; ++e)
		{
			unsigned int i0 = indices[i + e];
			unsigned int i1 = indices[i + next[e]];

			unsigned long long edge = edgeId(i0, i1);

			*hashLookup(edges, edge) = edge;
		}
	}

	for (size_t i = 0; i < index_count; i += 3)
	{
		static const int next[3] = { 1, 2, 0 };

		for (int e = 0; e < 3; ++e)
		{
			unsigned int i0 = indices[i + e];
			unsigned int i1 = indices[i + next[e]];

			unsigned long long edge = edgeId(i1, i0);

			if (*hashLookup(edges, edge) != edge)
			{
				unsigned int i2 = indices[i + next[next[e]]];

				Quadric Q;
				quadricFromTriangleEdge(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2]);

				quadricAdd(vertex_quadrics[i0], Q);
				quadricAdd(vertex_quadrics[i1], Q);
			}
		}
	}

	if (result != indices)
	{
		for (size_t i = 0; i < index_count; ++i)
		{
			result[i] = indices[i];
		}
	}

	size_t pass_count = 0;
	float worst_error = 0;

	while (index_count > target_index_count)
	{
		std::vector<Collapse> edge_collapses;
		edge_collapses.reserve(index_count);

		for (size_t i = 0; i < index_count; i += 3)
		{
			static const int next[3] = { 1, 2, 0 };

			for (int e = 0; e < 3; ++e)
			{
				unsigned int i0 = result[i + e];
				unsigned int i1 = result[i + next[e]];

				Collapse c01 = { i0, i1, quadricError(vertex_quadrics[i0], vertex_positions[i1]) };
				Collapse c10 = { i1, i0, quadricError(vertex_quadrics[i1], vertex_positions[i0]) };

				edge_collapses.push_back(c01.error <= c10.error ? c01 : c10);
			}
		}

		std::sort(edge_collapses.begin(), edge_collapses.end());

		std::vector<unsigned int> vertex_remap(vertex_count);

		for (size_t i = 0; i < vertex_remap.size(); ++i)
		{
			vertex_remap[i] = unsigned(i);
		}

		std::vector<char> vertex_locked(vertex_count);

		// each collapse removes 2 triangles
		size_t edge_collapse_goal = (index_count - target_index_count) / 6 + 1;

		size_t collapses = 0;
		float pass_error = 0;

		float error_goal = edge_collapse_goal < edge_collapses.size() ? edge_collapses[edge_collapse_goal].error : edge_collapses.back().error;
		float error_limit = error_goal * 1.5f;

		for (size_t i = 0; i < edge_collapses.size(); ++i)
		{
			const Collapse& c = edge_collapses[i];

			if (vertex_locked[c.v0] || vertex_locked[c.v1])
				continue;

			if (c.error > error_limit)
				break;

			assert(vertex_remap[c.v0] == c.v0);
			assert(vertex_remap[c.v1] == c.v1);

			quadricAdd(vertex_quadrics[c.v1], vertex_quadrics[c.v0]);

			vertex_remap[c.v0] = unsigned(c.v1);

			vertex_locked[c.v0] = 1;
			vertex_locked[c.v1] = 1;

			collapses++;
			pass_error = c.error;

			if (collapses >= edge_collapse_goal)
				break;
		}

		printf("pass %d: collapses: %d/%d, error: %e\n", int(pass_count), int(collapses), int(edge_collapses.size()), pass_error);

		pass_count++;
		worst_error = std::max(worst_error, pass_error);

		// no edges can be collapsed any more => bail out
		if (collapses == 0)
			break;

		size_t write = 0;

		for (size_t i = 0; i < index_count; i += 3)
		{
			unsigned int v0 = vertex_remap[result[i + 0]];
			unsigned int v1 = vertex_remap[result[i + 1]];
			unsigned int v2 = vertex_remap[result[i + 2]];

			assert(vertex_remap[v0] == v0);
			assert(vertex_remap[v1] == v1);
			assert(vertex_remap[v2] == v2);

			if (v0 != v1 && v0 != v2 && v1 != v2)
			{
				result[write + 0] = v0;
				result[write + 1] = v1;
				result[write + 2] = v2;
				write += 3;
			}
		}

		index_count = write;
	}

	printf("passes: %d, worst error: %e\n", int(pass_count), worst_error);

	return index_count;
}

size_t simplify(unsigned int* destination, const unsigned int* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size, size_t target_index_count)
{
	return simplifyEdgeCollapse(destination, indices, index_count, vertices, vertex_count, vertex_size, target_index_count);
}

} // namespace meshopt
<commit_msg>simplify: Remove debug prints<commit_after>// This file is part of meshoptimizer library; see meshoptimizer.hpp for version/license details
#include "meshoptimizer.hpp"

#include <algorithm>
#include <cassert>
#include <cmath>
#include <vector>

// This work is based on:
// Michael Garland and Paul S. Heckbert. Surface simplification using quadric error metrics. 1997
namespace meshopt
{

static size_t hash(unsigned long long key)
{
	key = (~key) + (key << 18);
	key = key ^ (key >> 31);
	key = key * 21;
	key = key ^ (key >> 11);
	key = key + (key << 6);
	key = key ^ (key >> 22);
	return size_t(key);
}

template <typename T>
static void hashInit(std::vector<T>& table, size_t count)
{
	size_t buckets = 1;
	while (buckets < count)
		buckets *= 2;

	table.clear();
	table.resize(buckets);
}

template <typename T>
static T* hashLookup(std::vector<T>& table, const T& key)
{
	assert((table.size() & (table.size() - 1)) == 0);

	size_t hashmod = table.size() - 1;
	size_t bucket = hash(key) & hashmod;

	for (size_t probe = 0; probe <= hashmod; ++probe)
	{
		T& item = table[bucket];

		if (item == T() || item == key)
			return &item;

		// hash collision, quadratic probing
		bucket = (bucket + probe + 1) & hashmod;
	}

	assert(false && "Hash table is full");
	return 0;
}

struct Vector3
{
	float x, y, z;
};

struct Quadric
{
	float a00;
	float a10, a11;
	float a20, a21, a22;
	float b0, b1, b2, c;
};

struct Collapse
{
	size_t v0;
	size_t v1;
	float error;

	bool operator<(const Collapse& other) const
	{
		return error < other.error;
	}
};

static float normalize(Vector3& v)
{
	float length = sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);

	if (length > 0)
	{
		v.x /= length;
		v.y /= length;
		v.z /= length;
	}

	return length;
}

static void quadricAdd(Quadric& Q, const Quadric& R)
{
	Q.a00 += R.a00;
	Q.a10 += R.a10;
	Q.a11 += R.a11;
	Q.a20 += R.a20;
	Q.a21 += R.a21;
	Q.a22 += R.a22;
	Q.b0 += R.b0;
	Q.b1 += R.b1;
	Q.b2 += R.b2;
	Q.c += R.c;
}

static void quadricMul(Quadric& Q, float s)
{
	Q.a00 *= s;
	Q.a10 *= s;
	Q.a11 *= s;
	Q.a20 *= s;
	Q.a21 *= s;
	Q.a22 *= s;
	Q.b0 *= s;
	Q.b1 *= s;
	Q.b2 *= s;
	Q.c *= s;
}

static float quadricError(Quadric& Q, const Vector3& v)
{
	float xx = v.x * v.x;
	float xy = v.x * v.y;
	float xz = v.x * v.z;
	float yy = v.y * v.y;
	float yz = v.y * v.z;
	float zz = v.z * v.z;

	float vTQv = Q.a00 * xx + Q.a10 * xy * 2 + Q.a11 * yy + Q.a20 * xz * 2 + Q.a21 * yz * 2 + Q.a22 * zz + Q.b0 * v.x * 2 + Q.b1 * v.y * 2 + Q.b2 * v.z * 2 + Q.c;

	return fabsf(vTQv);
}

static void quadricFromPlane(Quadric& Q, float a, float b, float c, float d)
{
	Q.a00 = a * a;
	Q.a10 = b * a;
	Q.a11 = b * b;
	Q.a20 = c * a;
	Q.a21 = c * b;
	Q.a22 = c * c;
	Q.b0 = d * a;
	Q.b1 = d * b;
	Q.b2 = d * c;
	Q.c = d * d;
}

static void quadricFromTriangle(Quadric& Q, const Vector3& p0, const Vector3& p1, const Vector3& p2)
{
	Vector3 p10 = { p1.x - p0.x, p1.y - p0.y, p1.z - p0.z };
	Vector3 p20 = { p2.x - p0.x, p2.y - p0.y, p2.z - p0.z };

	Vector3 normal = { p10.y * p20.z - p10.z * p20.y, p10.z * p20.x - p10.x * p20.z, p10.x * p20.y - p10.y * p20.x };
	float area = normalize(normal);

	float distance = normal.x * p0.x + normal.y * p0.y + normal.z * p0.z;

	quadricFromPlane(Q, normal.x, normal.y, normal.z, -distance);

	// Three classical weighting methods include weight=1, weight=area and weight=area^2
	// We use weight=area for now
	quadricMul(Q, area);
}

static void quadricFromTriangleEdge(Quadric& Q, const Vector3& p0, const Vector3& p1, const Vector3& p2)
{
	Vector3 p10 = { p1.x - p0.x, p1.y - p0.y, p1.z - p0.z };
	float length = normalize(p10);

	Vector3 p20 = { p2.x - p0.x, p2.y - p0.y, p2.z - p0.z };
	float p20p = p20.x * p10.x + p20.y * p10.y + p20.z * p10.z;

	Vector3 normal = { p20.x - p10.x * p20p, p20.y - p10.y * p20p, p20.z - p10.z * p20p };
	normalize(normal);

	float distance = normal.x * p0.x + normal.y * p0.y + normal.z * p0.z;

	quadricFromPlane(Q, normal.x, normal.y, normal.z, -distance);

	quadricMul(Q, length * 1000);
}

static unsigned long long edgeId(unsigned int a, unsigned int b)
{
	return (static_cast<unsigned long long>(a) << 32) | b;
}

static size_t simplifyEdgeCollapse(unsigned int* result, const unsigned int* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size, size_t target_index_count)
{
	std::vector<Vector3> vertex_positions(vertex_count);

	for (size_t i = 0; i < vertex_count; ++i)
	{
		const float* v = reinterpret_cast<const float*>(static_cast<const char*>(vertices) + i * vertex_size);

		vertex_positions[i].x = v[0];
		vertex_positions[i].y = v[1];
		vertex_positions[i].z = v[2];
	}

	std::vector<Quadric> vertex_quadrics(vertex_count);

	// face quadrics
	for (size_t i = 0; i < index_count; i += 3)
	{
		Quadric Q;
		quadricFromTriangle(Q, vertex_positions[indices[i + 0]], vertex_positions[indices[i + 1]], vertex_positions[indices[i + 2]]);

		quadricAdd(vertex_quadrics[indices[i + 0]], Q);
		quadricAdd(vertex_quadrics[indices[i + 1]], Q);
		quadricAdd(vertex_quadrics[indices[i + 2]], Q);
	}

	// edge quadrics for boundary edges
	std::vector<unsigned long long> edges;
	hashInit(edges, index_count);

	for (size_t i = 0; i < index_count; i += 3)
	{
		static const int next[3] = { 1, 2, 0 };

		for (int e = 0; e < 3; ++e)
		{
			unsigned int i0 = indices[i + e];
			unsigned int i1 = indices[i + next[e]];

			unsigned long long edge = edgeId(i0, i1);

			*hashLookup(edges, edge) = edge;
		}
	}

	for (size_t i = 0; i < index_count; i += 3)
	{
		static const int next[3] = { 1, 2, 0 };

		for (int e = 0; e < 3; ++e)
		{
			unsigned int i0 = indices[i + e];
			unsigned int i1 = indices[i + next[e]];

			unsigned long long edge = edgeId(i1, i0);

			if (*hashLookup(edges, edge) != edge)
			{
				unsigned int i2 = indices[i + next[next[e]]];

				Quadric Q;
				quadricFromTriangleEdge(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2]);

				quadricAdd(vertex_quadrics[i0], Q);
				quadricAdd(vertex_quadrics[i1], Q);
			}
		}
	}

	if (result != indices)
	{
		for (size_t i = 0; i < index_count; ++i)
		{
			result[i] = indices[i];
		}
	}

	size_t pass_count = 0;
	float worst_error = 0;

	while (index_count > target_index_count)
	{
		std::vector<Collapse> edge_collapses;
		edge_collapses.reserve(index_count);

		for (size_t i = 0; i < index_count; i += 3)
		{
			static const int next[3] = { 1, 2, 0 };

			for (int e = 0; e < 3; ++e)
			{
				unsigned int i0 = result[i + e];
				unsigned int i1 = result[i + next[e]];

				Collapse c01 = { i0, i1, quadricError(vertex_quadrics[i0], vertex_positions[i1]) };
				Collapse c10 = { i1, i0, quadricError(vertex_quadrics[i1], vertex_positions[i0]) };

				edge_collapses.push_back(c01.error <= c10.error ? c01 : c10);
			}
		}

		std::sort(edge_collapses.begin(), edge_collapses.end());

		std::vector<unsigned int> vertex_remap(vertex_count);

		for (size_t i = 0; i < vertex_remap.size(); ++i)
		{
			vertex_remap[i] = unsigned(i);
		}

		std::vector<char> vertex_locked(vertex_count);

		// each collapse removes 2 triangles
		size_t edge_collapse_goal = (index_count - target_index_count) / 6 + 1;

		size_t collapses = 0;
		float pass_error = 0;

		float error_goal = edge_collapse_goal < edge_collapses.size() ? edge_collapses[edge_collapse_goal].error : edge_collapses.back().error;
		float error_limit = error_goal * 1.5f;

		for (size_t i = 0; i < edge_collapses.size(); ++i)
		{
			const Collapse& c = edge_collapses[i];

			if (vertex_locked[c.v0] || vertex_locked[c.v1])
				continue;

			if (c.error > error_limit)
				break;

			assert(vertex_remap[c.v0] == c.v0);
			assert(vertex_remap[c.v1] == c.v1);

			quadricAdd(vertex_quadrics[c.v1], vertex_quadrics[c.v0]);

			vertex_remap[c.v0] = unsigned(c.v1);

			vertex_locked[c.v0] = 1;
			vertex_locked[c.v1] = 1;

			collapses++;
			pass_error = c.error;

			if (collapses >= edge_collapse_goal)
				break;
		}

		// printf("pass %d: collapses: %d/%d, error: %e\n", int(pass_count), int(collapses), int(edge_collapses.size()), pass_error);

		pass_count++;
		worst_error = std::max(worst_error, pass_error);

		// no edges can be collapsed any more => bail out
		if (collapses == 0)
			break;

		size_t write = 0;

		for (size_t i = 0; i < index_count; i += 3)
		{
			unsigned int v0 = vertex_remap[result[i + 0]];
			unsigned int v1 = vertex_remap[result[i + 1]];
			unsigned int v2 = vertex_remap[result[i + 2]];

			assert(vertex_remap[v0] == v0);
			assert(vertex_remap[v1] == v1);
			assert(vertex_remap[v2] == v2);

			if (v0 != v1 && v0 != v2 && v1 != v2)
			{
				result[write + 0] = v0;
				result[write + 1] = v1;
				result[write + 2] = v2;
				write += 3;
			}
		}

		index_count = write;
	}

	// printf("passes: %d, worst error: %e\n", int(pass_count), worst_error);

	return index_count;
}

size_t simplify(unsigned int* destination, const unsigned int* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size, size_t target_index_count)
{
	return simplifyEdgeCollapse(destination, indices, index_count, vertices, vertex_count, vertex_size, target_index_count);
}

} // namespace meshopt
<|endoftext|>


<commit_before>/*
Copyright 2016 Fixstars Corporation

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

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

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

#include <iostream>

#include <libsgm.h>

#include "internal.h"
#include "sgm.hpp"

namespace sgm {
	static bool is_cuda_input(EXECUTE_INOUT type) { return (int)type & 0x1; }
	static bool is_cuda_output(EXECUTE_INOUT type) { return (int)type & 0x2; }

	class SemiGlobalMatchingBase {
	public:
		using output_type = sgm::output_type;
		virtual void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness, bool subpixel) = 0;

		virtual ~SemiGlobalMatchingBase() {}
	};

	template <typename input_type, int DISP_SIZE>
	class SemiGlobalMatchingImpl : public SemiGlobalMatchingBase {
	public:
		void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness, bool subpixel) override
		{
			sgm_engine_.execute(dst_L, dst_R, (const input_type*)src_L, (const input_type*)src_R, w, h, P1, P2, uniqueness, subpixel);
		}
	private:
		SemiGlobalMatching<input_type, DISP_SIZE> sgm_engine_;
	};

	struct CudaStereoSGMResources {
		void* d_src_left;
		void* d_src_right;
		void* d_left_disp;
		void* d_right_disp;
		void* d_tmp_left_disp;
		void* d_tmp_right_disp;

		SemiGlobalMatchingBase* sgm_engine;

		CudaStereoSGMResources(int width_, int height_, int disparity_size_, int input_depth_bits_, int output_depth_bits_, EXECUTE_INOUT inout_type_) {

			if (input_depth_bits_ == 8 && disparity_size_ == 64)
				sgm_engine = new SemiGlobalMatchingImpl<uint8_t, 64>();
			else if (input_depth_bits_ == 8 && disparity_size_ == 128)
				sgm_engine = new SemiGlobalMatchingImpl<uint8_t, 128>();
			else if (input_depth_bits_ == 16 && disparity_size_ == 64)
				sgm_engine = new SemiGlobalMatchingImpl<uint16_t, 64>();
			else if (input_depth_bits_ == 16 && disparity_size_ == 128)
				sgm_engine = new SemiGlobalMatchingImpl<uint16_t, 128>();
			else
				throw std::logic_error("depth bits must be 8 or 16, and disparity size must be 64 or 128");

			if (is_cuda_input(inout_type_)) {
				this->d_src_left = NULL;
				this->d_src_right = NULL;
			}
			else {
				CudaSafeCall(cudaMalloc(&this->d_src_left, input_depth_bits_ / 8 * width_ * height_));
				CudaSafeCall(cudaMalloc(&this->d_src_right, input_depth_bits_ / 8 * width_ * height_));
			}

			CudaSafeCall(cudaMalloc(&this->d_left_disp, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMalloc(&this->d_right_disp, sizeof(uint16_t) * width_ * height_));

			CudaSafeCall(cudaMalloc(&this->d_tmp_left_disp, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMalloc(&this->d_tmp_right_disp, sizeof(uint16_t) * width_ * height_));

			CudaSafeCall(cudaMemset(this->d_left_disp, 0, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMemset(this->d_right_disp, 0, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMemset(this->d_tmp_left_disp, 0, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMemset(this->d_tmp_right_disp, 0, sizeof(uint16_t) * width_ * height_));
		}

		~CudaStereoSGMResources() {
			CudaSafeCall(cudaFree(this->d_src_left));
			CudaSafeCall(cudaFree(this->d_src_right));

			CudaSafeCall(cudaFree(this->d_left_disp));
			CudaSafeCall(cudaFree(this->d_right_disp));

			CudaSafeCall(cudaFree(this->d_tmp_left_disp));
			CudaSafeCall(cudaFree(this->d_tmp_right_disp));

			delete sgm_engine;
		}
	};

	StereoSGM::StereoSGM(int width, int height, int disparity_size, int input_depth_bits, int output_depth_bits,
		EXECUTE_INOUT inout_type, const Parameters& param) :
		cu_res_(NULL),
		width_(width),
		height_(height),
		disparity_size_(disparity_size),
		input_depth_bits_(input_depth_bits),
		output_depth_bits_(output_depth_bits),
		inout_type_(inout_type),
		param_(param)
	{
		// check values
		if (input_depth_bits_ != 8 && input_depth_bits_ != 16 && output_depth_bits_ != 8 && output_depth_bits_ != 16) {
			width_ = height_ = input_depth_bits_ = output_depth_bits_ = disparity_size_ = 0;
			throw std::logic_error("depth bits must be 8 or 16");
		}
		if (disparity_size_ != 64 && disparity_size_ != 128) {
			width_ = height_ = input_depth_bits_ = output_depth_bits_ = disparity_size_ = 0;
			throw std::logic_error("disparity size must be 64 or 128");
		}

		cu_res_ = new CudaStereoSGMResources(width_, height_, disparity_size_, input_depth_bits_, output_depth_bits_, inout_type_);
	}

	StereoSGM::~StereoSGM() {
		if (cu_res_) { delete cu_res_; }
	}

	void StereoSGM::execute(const void* left_pixels, const void* right_pixels, void* dst) {

		const void *d_input_left, *d_input_right;

		if (is_cuda_input(inout_type_)) {
			d_input_left = left_pixels;
			d_input_right = right_pixels;
		}
		else {
			CudaSafeCall(cudaMemcpy(cu_res_->d_src_left, left_pixels, input_depth_bits_ / 8 * width_ * height_, cudaMemcpyHostToDevice));
			CudaSafeCall(cudaMemcpy(cu_res_->d_src_right, right_pixels, input_depth_bits_ / 8 * width_ * height_, cudaMemcpyHostToDevice));
			d_input_left = cu_res_->d_src_left;
			d_input_right = cu_res_->d_src_right;
		}

		void* d_tmp_left_disp = cu_res_->d_tmp_left_disp;
		void* d_tmp_right_disp = cu_res_->d_tmp_right_disp;
		void* d_left_disp = cu_res_->d_left_disp;
		void* d_right_disp = cu_res_->d_right_disp;

		if (is_cuda_output(inout_type_) && output_depth_bits_ == 8)
			d_left_disp = dst; // when threre is no device-host copy or type conversion, use passed buffer

		cu_res_->sgm_engine->execute((uint16_t*)d_tmp_left_disp, (uint16_t*)d_tmp_right_disp,
			d_input_left, d_input_right, width_, height_, param_.P1, param_.P2, param_.uniqueness, param_.subpixel);

		sgm::details::median_filter((uint16_t*)d_tmp_left_disp, (uint16_t*)d_left_disp, width_, height_);
		sgm::details::median_filter((uint16_t*)d_tmp_right_disp, (uint16_t*)d_right_disp, width_, height_);
		sgm::details::check_consistency((uint16_t*)d_left_disp, (uint16_t*)d_right_disp, d_input_left, width_, height_, input_depth_bits_, param_.subpixel);

		if (!is_cuda_output(inout_type_) && output_depth_bits_ == 8) {
			sgm::details::cast_16bit_8bit_array((const uint16_t*)d_left_disp, (uint8_t*)d_tmp_left_disp, width_ * height_);
			CudaSafeCall(cudaMemcpy(dst, d_tmp_left_disp, sizeof(uint8_t) * width_ * height_, cudaMemcpyDeviceToHost));
		}
		else if (is_cuda_output(inout_type_) && output_depth_bits_ == 8) {
			sgm::details::cast_16bit_8bit_array((const uint16_t*)d_left_disp, (uint8_t*)dst, width_ * height_);
		}
		else if (!is_cuda_output(inout_type_) && output_depth_bits_ == 16) {
			CudaSafeCall(cudaMemcpy(dst, d_left_disp, sizeof(uint16_t) * width_ * height_, cudaMemcpyDeviceToHost));
		}
		else if (is_cuda_output(inout_type_) && output_depth_bits_ == 16) {
			// optimize! no-copy!
		}
		else {
			std::cerr << "not impl" << std::endl;
		}
	}
}
<commit_msg>Fix miss of 294e3dd9<commit_after>/*
Copyright 2016 Fixstars Corporation

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

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

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

#include <iostream>

#include <libsgm.h>

#include "internal.h"
#include "sgm.hpp"

namespace sgm {
	static bool is_cuda_input(EXECUTE_INOUT type) { return (int)type & 0x1; }
	static bool is_cuda_output(EXECUTE_INOUT type) { return (int)type & 0x2; }

	class SemiGlobalMatchingBase {
	public:
		using output_type = sgm::output_type;
		virtual void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness, bool subpixel) = 0;

		virtual ~SemiGlobalMatchingBase() {}
	};

	template <typename input_type, int DISP_SIZE>
	class SemiGlobalMatchingImpl : public SemiGlobalMatchingBase {
	public:
		void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness, bool subpixel) override
		{
			sgm_engine_.execute(dst_L, dst_R, (const input_type*)src_L, (const input_type*)src_R, w, h, P1, P2, uniqueness, subpixel);
		}
	private:
		SemiGlobalMatching<input_type, DISP_SIZE> sgm_engine_;
	};

	struct CudaStereoSGMResources {
		void* d_src_left;
		void* d_src_right;
		void* d_left_disp;
		void* d_right_disp;
		void* d_tmp_left_disp;
		void* d_tmp_right_disp;

		SemiGlobalMatchingBase* sgm_engine;

		CudaStereoSGMResources(int width_, int height_, int disparity_size_, int input_depth_bits_, int output_depth_bits_, EXECUTE_INOUT inout_type_) {

			if (input_depth_bits_ == 8 && disparity_size_ == 64)
				sgm_engine = new SemiGlobalMatchingImpl<uint8_t, 64>();
			else if (input_depth_bits_ == 8 && disparity_size_ == 128)
				sgm_engine = new SemiGlobalMatchingImpl<uint8_t, 128>();
			else if (input_depth_bits_ == 16 && disparity_size_ == 64)
				sgm_engine = new SemiGlobalMatchingImpl<uint16_t, 64>();
			else if (input_depth_bits_ == 16 && disparity_size_ == 128)
				sgm_engine = new SemiGlobalMatchingImpl<uint16_t, 128>();
			else
				throw std::logic_error("depth bits must be 8 or 16, and disparity size must be 64 or 128");

			if (is_cuda_input(inout_type_)) {
				this->d_src_left = NULL;
				this->d_src_right = NULL;
			}
			else {
				CudaSafeCall(cudaMalloc(&this->d_src_left, input_depth_bits_ / 8 * width_ * height_));
				CudaSafeCall(cudaMalloc(&this->d_src_right, input_depth_bits_ / 8 * width_ * height_));
			}

			CudaSafeCall(cudaMalloc(&this->d_left_disp, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMalloc(&this->d_right_disp, sizeof(uint16_t) * width_ * height_));

			CudaSafeCall(cudaMalloc(&this->d_tmp_left_disp, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMalloc(&this->d_tmp_right_disp, sizeof(uint16_t) * width_ * height_));

			CudaSafeCall(cudaMemset(this->d_left_disp, 0, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMemset(this->d_right_disp, 0, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMemset(this->d_tmp_left_disp, 0, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMemset(this->d_tmp_right_disp, 0, sizeof(uint16_t) * width_ * height_));
		}

		~CudaStereoSGMResources() {
			CudaSafeCall(cudaFree(this->d_src_left));
			CudaSafeCall(cudaFree(this->d_src_right));

			CudaSafeCall(cudaFree(this->d_left_disp));
			CudaSafeCall(cudaFree(this->d_right_disp));

			CudaSafeCall(cudaFree(this->d_tmp_left_disp));
			CudaSafeCall(cudaFree(this->d_tmp_right_disp));

			delete sgm_engine;
		}
	};

	StereoSGM::StereoSGM(int width, int height, int disparity_size, int input_depth_bits, int output_depth_bits,
		EXECUTE_INOUT inout_type, const Parameters& param) :
		cu_res_(NULL),
		width_(width),
		height_(height),
		disparity_size_(disparity_size),
		input_depth_bits_(input_depth_bits),
		output_depth_bits_(output_depth_bits),
		inout_type_(inout_type),
		param_(param)
	{
		// check values
		if (input_depth_bits_ != 8 && input_depth_bits_ != 16 && output_depth_bits_ != 8 && output_depth_bits_ != 16) {
			width_ = height_ = input_depth_bits_ = output_depth_bits_ = disparity_size_ = 0;
			throw std::logic_error("depth bits must be 8 or 16");
		}
		if (disparity_size_ != 64 && disparity_size_ != 128) {
			width_ = height_ = input_depth_bits_ = output_depth_bits_ = disparity_size_ = 0;
			throw std::logic_error("disparity size must be 64 or 128");
		}

		cu_res_ = new CudaStereoSGMResources(width_, height_, disparity_size_, input_depth_bits_, output_depth_bits_, inout_type_);
	}

	StereoSGM::~StereoSGM() {
		if (cu_res_) { delete cu_res_; }
	}

	void StereoSGM::execute(const void* left_pixels, const void* right_pixels, void* dst) {

		const void *d_input_left, *d_input_right;

		if (is_cuda_input(inout_type_)) {
			d_input_left = left_pixels;
			d_input_right = right_pixels;
		}
		else {
			CudaSafeCall(cudaMemcpy(cu_res_->d_src_left, left_pixels, input_depth_bits_ / 8 * width_ * height_, cudaMemcpyHostToDevice));
			CudaSafeCall(cudaMemcpy(cu_res_->d_src_right, right_pixels, input_depth_bits_ / 8 * width_ * height_, cudaMemcpyHostToDevice));
			d_input_left = cu_res_->d_src_left;
			d_input_right = cu_res_->d_src_right;
		}

		void* d_tmp_left_disp = cu_res_->d_tmp_left_disp;
		void* d_tmp_right_disp = cu_res_->d_tmp_right_disp;
		void* d_left_disp = cu_res_->d_left_disp;
		void* d_right_disp = cu_res_->d_right_disp;

		if (is_cuda_output(inout_type_) && output_depth_bits_ == 16)
			d_left_disp = dst; // when threre is no device-host copy or type conversion, use passed buffer

		cu_res_->sgm_engine->execute((uint16_t*)d_tmp_left_disp, (uint16_t*)d_tmp_right_disp,
			d_input_left, d_input_right, width_, height_, param_.P1, param_.P2, param_.uniqueness, param_.subpixel);

		sgm::details::median_filter((uint16_t*)d_tmp_left_disp, (uint16_t*)d_left_disp, width_, height_);
		sgm::details::median_filter((uint16_t*)d_tmp_right_disp, (uint16_t*)d_right_disp, width_, height_);
		sgm::details::check_consistency((uint16_t*)d_left_disp, (uint16_t*)d_right_disp, d_input_left, width_, height_, input_depth_bits_, param_.subpixel);

		if (!is_cuda_output(inout_type_) && output_depth_bits_ == 8) {
			sgm::details::cast_16bit_8bit_array((const uint16_t*)d_left_disp, (uint8_t*)d_tmp_left_disp, width_ * height_);
			CudaSafeCall(cudaMemcpy(dst, d_tmp_left_disp, sizeof(uint8_t) * width_ * height_, cudaMemcpyDeviceToHost));
		}
		else if (is_cuda_output(inout_type_) && output_depth_bits_ == 8) {
			sgm::details::cast_16bit_8bit_array((const uint16_t*)d_left_disp, (uint8_t*)dst, width_ * height_);
		}
		else if (!is_cuda_output(inout_type_) && output_depth_bits_ == 16) {
			CudaSafeCall(cudaMemcpy(dst, d_left_disp, sizeof(uint16_t) * width_ * height_, cudaMemcpyDeviceToHost));
		}
		else if (is_cuda_output(inout_type_) && output_depth_bits_ == 16) {
			// optimize! no-copy!
		}
		else {
			std::cerr << "not impl" << std::endl;
		}
	}
}
<|endoftext|>


<commit_before>#include "meshoptimizer.h"

#include <cassert>

#include <algorithm>
#include <vector>

static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int buffer_size, const unsigned int* valence)
{
	unsigned int index = 0;
	unsigned int iv = ~0u;

	for (unsigned int i = 0; i < buffer_size; ++i)
	{
		unsigned int v = std::min(valence[buffer[i][0]], std::min(valence[buffer[i][1]], valence[buffer[i][2]]));

		if (v < iv)
		{
			index = i;
			iv = v;
		}
	}

	return index;
}

static int findStripNext(const unsigned int buffer[][3], unsigned int buffer_size, unsigned int e0, unsigned int e1)
{
	for (unsigned int i = 0; i < buffer_size; ++i)
	{
		unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];

		if (e0 == a && e1 == b)
			return (i << 2) | 2;
		else if (e0 == b && e1 == c)
			return (i << 2) | 0;
		else if (e0 == c && e1 == a)
			return (i << 2) | 1;
	}

	return -1;
}

size_t meshopt_stripify(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count)
{
	const size_t buffer_capacity = 8;

	unsigned int buffer[buffer_capacity][3] = {};
	unsigned int buffer_size = 0;

	size_t index_offset = 0;

	unsigned int strip[2] = {};
	unsigned int stripx = 0;

	size_t strip_size = 0;

	std::vector<unsigned int> valence(vertex_count, 0);

	for (size_t i = 0; i < index_count; ++i)
	{
		unsigned int index = indices[i];
		assert(index < vertex_count);

		valence[index]++;
	}

	int next = -1;

	while (buffer_size > 0 || index_offset < index_count)
	{
		assert(next < 0 || (size_t(next >> 2) < buffer_size && (next & 3) < 3));

		// fill triangle buffer
		while (buffer_size < buffer_capacity && index_offset < index_count)
		{
			buffer[buffer_size][0] = indices[index_offset + 0];
			buffer[buffer_size][1] = indices[index_offset + 1];
			buffer[buffer_size][2] = indices[index_offset + 2];

			buffer_size++;
			index_offset += 3;
		}

		assert(buffer_size > 0);

		if (next >= 0)
		{
			unsigned int i = next >> 2;
			unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
			unsigned int v = buffer[i][next & 3];

			// unordered removal from the buffer
			buffer[i][0] = buffer[buffer_size - 1][0];
			buffer[i][1] = buffer[buffer_size - 1][1];
			buffer[i][2] = buffer[buffer_size - 1][2];
			buffer_size--;

			// update vertex valences for strip start heuristic
			valence[a]--;
			valence[b]--;
			valence[c]--;

			// find next triangle (note that edge order flips on every iteration)
			// in some cases we need to perform a swap to pick a different outgoing triangle edge
			// for [a b c], the default strip edge is [b c], but we might want to use [a c]
			int cont = findStripNext(buffer, buffer_size, stripx ? strip[1] : v, stripx ? v : strip[1]);
			int swap = cont < 0 ? findStripNext(buffer, buffer_size, stripx ? v : strip[0], stripx ? strip[0] : v) : -1;

			if (cont < 0 && swap >= 0)
			{
				// [a b c] => [a b a c]
				destination[strip_size++] = strip[0];
				destination[strip_size++] = v;

				// next strip has same winding
				// ? a b => b a v
				strip[1] = v;

				next = swap;
			}
			else
			{
				// emit the next vertex in the strip
				destination[strip_size++] = v;

				// next strip has flipped winding
				strip[0] = strip[1];
				strip[1] = v;
				stripx ^= 1;

				next = cont;
			}
		}
		else
		{
			// if we didn't find anything, we need to find the next new triangle
			// we use a heuristic to maximize the strip length
			unsigned int i = findStripFirst(buffer, buffer_size, &valence[0]);
			unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];

			// unordered removal from the buffer
			buffer[i][0] = buffer[buffer_size - 1][0];
			buffer[i][1] = buffer[buffer_size - 1][1];
			buffer[i][2] = buffer[buffer_size - 1][2];
			buffer_size--;

			// update vertex valences for strip start heuristic
			valence[a]--;
			valence[b]--;
			valence[c]--;

			// we need to pre-rotate the triangle so that we will find a match in the existing buffer on the next iteration
			int ea = findStripNext(buffer, buffer_size, c, b);
			int eb = ea < 0 ? findStripNext(buffer, buffer_size, a, c) : -1;
			int ec = eb < 0 ? findStripNext(buffer, buffer_size, b, a) : -1;

			if (ea >= 0)
			{
				// keep abc
				next = ea;
			}
			else if (eb >= 0)
			{
				// abc -> bca
				unsigned int t = a;
				a = b, b = c, c = t;

				next = eb;
			}
			else if (ec >= 0)
			{
				// abc -> cab
				unsigned int t = c;
				c = b, b = a, a = t;

				next = ec;
			}

			// emit the new strip; we use restart indices
			if (strip_size)
				destination[strip_size++] = ~0u;

			destination[strip_size++] = a;
			destination[strip_size++] = b;
			destination[strip_size++] = c;

			// new strip always starts with the same edge winding
			strip[0] = b;
			strip[1] = c;
			stripx = 1;
		}
	}

	return strip_size;
}

size_t meshopt_unstripify(unsigned int* destination, const unsigned int* indices, size_t index_count)
{
	size_t offset = 0;
	size_t start = 0;

	for (size_t i = 0; i < index_count; ++i)
	{
		if (indices[i] == ~0u)
		{
			start = i + 1;
		}
		else if (i - start >= 2)
		{
			unsigned int a = indices[i - 2], b = indices[i - 1], c = indices[i];

			if ((i - start) % 2 == 1)
				std::swap(a, b);

			if (a != b && a != c && b != c)
			{
				destination[offset + 0] = a;
				destination[offset + 1] = b;
				destination[offset + 2] = c;
				offset += 3;
			}
		}
	}

	return offset;
}
<commit_msg>stripify: Minor refactoring<commit_after>#include "meshoptimizer.h"

#include <cassert>

#include <vector>

namespace meshopt
{

static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int buffer_size, const unsigned int* valence)
{
	unsigned int index = 0;
	unsigned int iv = ~0u;

	for (unsigned int i = 0; i < buffer_size; ++i)
	{
		unsigned int va = valence[buffer[i][0]], vb = valence[buffer[i][1]], vc = valence[buffer[i][2]];
		unsigned int v = (va < vb && va < vc) ? va : (vb < vc) ? vb : vc;

		if (v < iv)
		{
			index = i;
			iv = v;
		}
	}

	return index;
}

static int findStripNext(const unsigned int buffer[][3], unsigned int buffer_size, unsigned int e0, unsigned int e1)
{
	for (unsigned int i = 0; i < buffer_size; ++i)
	{
		unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];

		if (e0 == a && e1 == b)
			return (i << 2) | 2;
		else if (e0 == b && e1 == c)
			return (i << 2) | 0;
		else if (e0 == c && e1 == a)
			return (i << 2) | 1;
	}

	return -1;
}

}

size_t meshopt_stripify(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count)
{
	assert(destination != indices);
	assert(index_count % 3 == 0);

	using namespace meshopt;

	const size_t buffer_capacity = 8;

	unsigned int buffer[buffer_capacity][3] = {};
	unsigned int buffer_size = 0;

	size_t index_offset = 0;

	unsigned int strip[2] = {};
	unsigned int parity = 0;

	size_t strip_size = 0;

	// compute vertex valence; this is used to prioritize starting triangle for strips
	std::vector<unsigned int> valence(vertex_count, 0);

	for (size_t i = 0; i < index_count; ++i)
	{
		unsigned int index = indices[i];
		assert(index < vertex_count);

		valence[index]++;
	}

	int next = -1;

	while (buffer_size > 0 || index_offset < index_count)
	{
		assert(next < 0 || (size_t(next >> 2) < buffer_size && (next & 3) < 3));

		// fill triangle buffer
		while (buffer_size < buffer_capacity && index_offset < index_count)
		{
			buffer[buffer_size][0] = indices[index_offset + 0];
			buffer[buffer_size][1] = indices[index_offset + 1];
			buffer[buffer_size][2] = indices[index_offset + 2];

			buffer_size++;
			index_offset += 3;
		}

		assert(buffer_size > 0);

		if (next >= 0)
		{
			unsigned int i = next >> 2;
			unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
			unsigned int v = buffer[i][next & 3];

			// unordered removal from the buffer
			buffer[i][0] = buffer[buffer_size - 1][0];
			buffer[i][1] = buffer[buffer_size - 1][1];
			buffer[i][2] = buffer[buffer_size - 1][2];
			buffer_size--;

			// update vertex valences for strip start heuristic
			valence[a]--;
			valence[b]--;
			valence[c]--;

			// find next triangle (note that edge order flips on every iteration)
			// in some cases we need to perform a swap to pick a different outgoing triangle edge
			// for [a b c], the default strip edge is [b c], but we might want to use [a c]
			int cont = findStripNext(buffer, buffer_size, parity ? strip[1] : v, parity ? v : strip[1]);
			int swap = cont < 0 ? findStripNext(buffer, buffer_size, parity ? v : strip[0], parity ? strip[0] : v) : -1;

			if (cont < 0 && swap >= 0)
			{
				// [a b c] => [a b a c]
				destination[strip_size++] = strip[0];
				destination[strip_size++] = v;

				// next strip has same winding
				// ? a b => b a v
				strip[1] = v;

				next = swap;
			}
			else
			{
				// emit the next vertex in the strip
				destination[strip_size++] = v;

				// next strip has flipped winding
				strip[0] = strip[1];
				strip[1] = v;
				parity ^= 1;

				next = cont;
			}
		}
		else
		{
			// if we didn't find anything, we need to find the next new triangle
			// we use a heuristic to maximize the strip length
			unsigned int i = findStripFirst(buffer, buffer_size, &valence[0]);
			unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];

			// unordered removal from the buffer
			buffer[i][0] = buffer[buffer_size - 1][0];
			buffer[i][1] = buffer[buffer_size - 1][1];
			buffer[i][2] = buffer[buffer_size - 1][2];
			buffer_size--;

			// update vertex valences for strip start heuristic
			valence[a]--;
			valence[b]--;
			valence[c]--;

			// we need to pre-rotate the triangle so that we will find a match in the existing buffer on the next iteration
			int ea = findStripNext(buffer, buffer_size, c, b);
			int eb = ea < 0 ? findStripNext(buffer, buffer_size, a, c) : -1;
			int ec = eb < 0 ? findStripNext(buffer, buffer_size, b, a) : -1;

			if (ea >= 0)
			{
				// keep abc
				next = ea;
			}
			else if (eb >= 0)
			{
				// abc -> bca
				unsigned int t = a;
				a = b, b = c, c = t;

				next = eb;
			}
			else if (ec >= 0)
			{
				// abc -> cab
				unsigned int t = c;
				c = b, b = a, a = t;

				next = ec;
			}

			// emit the new strip; we use restart indices
			if (strip_size)
				destination[strip_size++] = ~0u;

			destination[strip_size++] = a;
			destination[strip_size++] = b;
			destination[strip_size++] = c;

			// new strip always starts with the same edge winding
			strip[0] = b;
			strip[1] = c;
			parity = 1;
		}
	}

	return strip_size;
}

size_t meshopt_unstripify(unsigned int* destination, const unsigned int* indices, size_t index_count)
{
	assert(destination != indices);

	size_t offset = 0;
	size_t start = 0;

	for (size_t i = 0; i < index_count; ++i)
	{
		if (indices[i] == ~0u)
		{
			start = i + 1;
		}
		else if (i - start >= 2)
		{
			unsigned int a = indices[i - 2], b = indices[i - 1], c = indices[i];

			if ((i - start) & 1)
			{
				unsigned int t = a;
				a = b, b = t;
			}

			if (a != b && a != c && b != c)
			{
				destination[offset + 0] = a;
				destination[offset + 1] = b;
				destination[offset + 2] = c;
				offset += 3;
			}
		}
	}

	return offset;
}
<|endoftext|>


<commit_before>/** \file
 * Implements safer c++ wrappers for the sysctl() interface.
 */

#ifndef _POWERDXX_SYS_SYSCTL_HPP_
#define _POWERDXX_SYS_SYSCTL_HPP_

#include "error.hpp"       /* sys::sc_error */

#include <memory>          /* std::unique_ptr */

#include <cassert>         /* assert() */

#include <sys/types.h>     /* sysctl() */
#include <sys/sysctl.h>    /* sysctl() */

namespace sys {

/**
 * This namespace contains safer c++ wrappers for the sysctl() interface.
 *
 * The template class Sysctl represents a sysctl address and offers
 * handles to retrieve or set the stored value.
 *
 * The template class Sync represents a sysctl value that is read and
 * written synchronously.
 *
 * The template class Once represents a read once value.
 */
namespace ctl {

/**
 * The domain error type.
 */
struct error {};

/**
 * Management Information Base identifier type (see sysctl(3)).
 */
typedef int mib_t;

/**
 * Represents a sysctl MIB address.
 *
 * It offers set() and get() methods to access these sysctls.
 *
 * There are two ways of initialising a Sysctl instance, by symbolic
 * name or by directly using the MIB address. The latter one only
 * makes sense for sysctls with a fixed address, known at compile
 * time, e.g. `Sysctl<2>{CTL_HW, HW_NCPU}` for "hw.ncpu". Check
 * `/usr/include/sys/sysctl.h` for predefined MIBs.
 *
 * For all other sysctls, symbolic names must be used. E.g.
 * `Sysctl<4>{"dev.cpu.0.freq"}`. Creating a Sysctl from a symbolic
 * name may throw.
 *
 * A Sysctl instance created with the default constructor is unitialised,
 * initialisation can be deferred to a later moment by using copy assignment.
 * This can be used to create globals but construct them inline where
 * exceptions can be handled.
 *
 * @tparam MibDepth
 *	The MIB level, e.g. "hw.ncpu" is two levels deep
 */
template <size_t MibDepth>
class Sysctl {
	private:
	/**
	 * Stores the MIB address.
	 */
	mib_t mib[MibDepth];

	public:
	/**
	 * The default constructor.
	 *
	 * This is available to defer initialisation to a later moment.
	 * This might be useful when initialising global or static
	 * instances by a character string repesented name.
	 */
	constexpr Sysctl() : mib{} {}

	/**
	 * Initialise the MIB address from a character string.
	 *
	 * @param name
	 *	The symbolic name of the sysctl
	 * @throws sys::sc_error<error>
	 *	May throw an exception if the addressed sysct does
	 *	not exist or if the address is too long to store
	 */
	Sysctl(char const * const name) {
		size_t length = MibDepth;
		if (sysctlnametomib(name, this->mib, &length) == -1) {
			throw sc_error<error>{errno};
		}
		assert(length == MibDepth && "MIB depth mismatch");
	}

	/**
	 * Initialise the MIB address directly.
	 *
	 * Some important sysctl values have a fixed address that
	 * can be initialised at compile time with a noexcept guarantee.
	 *
	 * Spliting the MIB address into head and tail makes sure
	 * that `Sysctl(char *)` does not match the template and is
	 * instead implicitly cast to invoke `Sysctl(char const *)`.
	 *
	 * @tparam Tail
	 *	The types of the trailing MIB address values (must
	 *	be mib_t)
	 * @param head,tail
	 *	The mib
	 */
	template <typename... Tail>
	constexpr Sysctl(mib_t const head, Tail const... tail) noexcept :
	    mib{head, tail...} {
		static_assert(MibDepth == sizeof...(Tail) + 1,
		              "MIB depth mismatch");
	}

	/**
	 * Update the given buffer with a value retrieved from the
	 * sysctl.
	 *
	 * @param buf,bufsize
	 *	The target buffer and its size
	 * @throws sys::sc_error<error>
	 *	Throws if value retrieval fails or is incomplete,
	 *	e.g. because the value does not fit into the target
	 *	buffer
	 */
	void get(void * const buf, size_t const bufsize) const {
		auto len = bufsize;
		if (sysctl(this->mib, MibDepth, buf, &len, nullptr, 0)
		    == -1) {
			throw sc_error<error>{errno};
		}
	}

	/**
	 * Update the given value with a value retreived from the
	 * sysctl.
	 *
	 * @tparam T
	 *	The type store the sysctl value in
	 * @param value
	 *	A reference to the target value
	 * @throws sys::sc_error<error>
	 *	Throws if value retrieval fails or is incomplete,
	 *	e.g. because the value does not fit into the target
	 *	type
	 */
	template <typename T>
	void get(T & value) const {
		get(&value, sizeof(T));
	}

	/**
	 * Retrieve an array from the sysctl address.
	 *
	 * This is useful to retrieve variable length sysctls, like
	 * characer strings.
	 *
	 * @tparam T
	 *	The type stored in the array
	 * @return
	 *	And array of T with the right length to store the
	 *	whole sysctl value
	 * @throws sys::sc_error<error>
	 *	May throw if the size of the sysctl increases after
	 *	the length was queried
	 */
	template <typename T>
	std::unique_ptr<T[]> get() const {
		size_t len = 0;
		if (sysctl(this->mib, MibDepth, nullptr, &len, nullptr, 0)
		    == -1) {
			throw sc_error<error>{errno};
		}
		auto result = std::unique_ptr<T[]>(new T[len / sizeof(T)]);
		get(result.get(), len);
		return result;
	}

	/**
	 * Update the the sysctl value with the given buffer.
	 *
	 * @param buf,bufsize
	 *	The source buffer
	 * @throws sys::sc_error<error>
	 *	If the source buffer cannot be stored in the sysctl
	 */
	void set(void const * const buf, size_t const bufsize) {
		if (sysctl(this->mib, MibDepth, nullptr, nullptr,
		             buf, bufsize) == -1) {
			throw sc_error<error>{errno};
		}
	}

	/**
	 * Update the the sysctl value with the given value.
	 *
	 * @tparam T
	 *	The value type
	 * @param value
	 *	The value to set the sysctl to
	 */
	template <typename T>
	void set(T const & value) {
		set(&value, sizeof(T));
	}
};

/**
 * Create a Sysctl instances.
 *
 * This is only compatible with creating sysctls from predefined MIBs.
 *
 * @tparam Args
 *	List of argument types, should all be pid_t
 * @param args
 *	List of initialising arguments
 * @return
 *	A Sysctl instance with the depth matching the number of arguments
 */
template <typename... Args>
constexpr Sysctl<sizeof...(Args)> make_Sysctl(Args const... args) {
	return {args...};
}

/**
 * This is a wrapper around Sysctl that allows semantically transparent
 * use of a sysctl.
 *
 * ~~~ c++
 * Sync<int, Sysctl<3>> sndUnit{{"hw.snd.default_unit"}};
 * if (sndUnit != 3) {    // read from sysctl
 *	sndUnit = 3;      // assign to sysctl
 * }
 * ~~~
 *
 * Note that both assignment and read access (implemented through
 * type casting to T) may throw an exception.
 *
 * @tparam T
 *	The type to represent the sysctl as
 * @tparam SysctlT
 *	The Sysctl type
 */
template <typename T, class SysctlT>
class Sync {
	private:
	/**
	 * A sysctl to represent.
	 */
	SysctlT sysctl;

	public:
	/**
	 * The constructor copies the given Sysctl instance.
	 *
	 * @param sysctl
	 *	The Sysctl instance to represent
	 */
	constexpr Sync(SysctlT const & sysctl) noexcept : sysctl{sysctl} {}

	/**
	 * Transparently assiges values of type T to the represented
	 * Sysctl instance.
	 *
	 * @param value
	 *	The value to assign
	 * @return
	 *	A self reference
	 */
	Sync & operator =(T const & value) {
		this->sysctl.set(value);
		return *this;
	}

	/**
	 * Implicitly cast to the represented type.
	 *
	 * @return
	 *	Returns the value from the sysctl
	 */
	operator T () const {
		T value;
		this->sysctl.get(value);
		return value;
	}
};

/**
 * A convenience alias around Sync.
 *
 * ~~~ c++
 * // Sync<int, Sysctl<3>> sndUnit{{"hw.snd.default_unit"}};
 * SysctlSync<int, 3> sndUnit{{"hw.snd.default_unit"}};
 * if (sndUnit != 3) {    // read from sysctl
 *	sndUnit = 3;      // assign to sysctl
 * }
 * ~~~
 *
 * @tparam T
 *	The type to represent the sysctl as
 * @tparam MibDepth
 *	The maximum allowed MIB depth
 */
template <typename T, size_t MibDepth>
using SysctlSync = Sync<T, Sysctl<MibDepth>>;

/**
 * A read once representation of a Sysctl.
 *
 * This reads a sysctl once upon construction and always returns that
 * value. It does not support assignment.
 *
 * This class is intended for sysctls that are not expected to change,
 * such as hw.ncpu. A special property of this class is that the
 * constructor does not throw and takes a default value in case reading
 * the sysctl fails.
 *
 * ~~~ c++
 * // Read number of CPU cores, assume 1 on failure:
 * Once<coreid_t, Sysctl<2>> ncpu{1, {CTL_HW, HW_NCPU}};
 * // Equivalent:
 * int hw_ncpu;
 * try {
 * 	Sysctl<2>{CTL_HW, HW_NCPU}.get(hw_ncpu);
 * } catch (sys::sc_error<error>) {
 * 	hw_ncpu = 1;
 * }
 * ~~~
 *
 * @tparam T
 *	The type to represent the sysctl as
 * @tparam SysctlT
 *	The Sysctl type
 */
template <typename T, class SysctlT>
class Once {
	private:
	/**
	 * The sysctl value read upon construction.
	 */
	T value;

	public:
	/**
	 * The constructor tries to read and store the requested sysctl.
	 *
	 * If reading the requested sysctl fails for any reason,
	 * the given value is stored instead.
	 *
	 * @param value
	 *	The fallback value
	 * @param sysctl
	 *	The sysctl to represent
	 */
	Once(T const & value, SysctlT const & sysctl) noexcept {
		try {
			sysctl.get(this->value);
		} catch (sc_error<error>) {
			this->value = value;
		}
	}

	/**
	 * Return a const reference to the value.
	 *
	 * @return
	 *	A const reference to the value
	 */
	operator T const &() const {
		return this->value;
	}
};

/**
 * A convenience alias around Once.
 *
 * ~~~ c++
 * // Once<coreid_t, Sysctl<2>> ncpu{0, {CTL_HW, HW_NCPU}};
 * SysctlOnce<coreid_t, 2> ncpu{1, {CTL_HW, HW_NCPU}};
 * ~~~
 *
 * @tparam T
 *	The type to represent the sysctl as
 * @tparam MibDepth
 *	The maximum allowed MIB depth
 */
template <typename T, size_t MibDepth>
using SysctlOnce = Once<T, Sysctl<MibDepth>>;

/**
 * This creates a Once instance.
 *
 * This is intended for cases when a Once instance is created as a
 * temporary to retrieve a value, using it's fallback to a default
 * mechanism.
 *
 * @tparam T
 *	The value type
 * @tparam SysctlT
 *	The Sysctl type
 * @param value
 *	The default value to fall back to
 * @param sysctl
 *	The sysctl to try and read from
 */
template <typename T, class SysctlT>
constexpr Once<T, SysctlT> make_Once(T const & value, SysctlT const & sysctl)
noexcept { return {value, sysctl}; }

} /* namespace ctl */
} /* namespace sys */

#endif /* _POWERDXX_SYS_SYSCTL_HPP_ */
<commit_msg>Add default constructor for late initialisation<commit_after>/** \file
 * Implements safer c++ wrappers for the sysctl() interface.
 */

#ifndef _POWERDXX_SYS_SYSCTL_HPP_
#define _POWERDXX_SYS_SYSCTL_HPP_

#include "error.hpp"       /* sys::sc_error */

#include <memory>          /* std::unique_ptr */

#include <cassert>         /* assert() */

#include <sys/types.h>     /* sysctl() */
#include <sys/sysctl.h>    /* sysctl() */

namespace sys {

/**
 * This namespace contains safer c++ wrappers for the sysctl() interface.
 *
 * The template class Sysctl represents a sysctl address and offers
 * handles to retrieve or set the stored value.
 *
 * The template class Sync represents a sysctl value that is read and
 * written synchronously.
 *
 * The template class Once represents a read once value.
 */
namespace ctl {

/**
 * The domain error type.
 */
struct error {};

/**
 * Management Information Base identifier type (see sysctl(3)).
 */
typedef int mib_t;

/**
 * Represents a sysctl MIB address.
 *
 * It offers set() and get() methods to access these sysctls.
 *
 * There are two ways of initialising a Sysctl instance, by symbolic
 * name or by directly using the MIB address. The latter one only
 * makes sense for sysctls with a fixed address, known at compile
 * time, e.g. `Sysctl<2>{CTL_HW, HW_NCPU}` for "hw.ncpu". Check
 * `/usr/include/sys/sysctl.h` for predefined MIBs.
 *
 * For all other sysctls, symbolic names must be used. E.g.
 * `Sysctl<4>{"dev.cpu.0.freq"}`. Creating a Sysctl from a symbolic
 * name may throw.
 *
 * A Sysctl instance created with the default constructor is unitialised,
 * initialisation can be deferred to a later moment by using copy assignment.
 * This can be used to create globals but construct them inline where
 * exceptions can be handled.
 *
 * @tparam MibDepth
 *	The MIB level, e.g. "hw.ncpu" is two levels deep
 */
template <size_t MibDepth>
class Sysctl {
	private:
	/**
	 * Stores the MIB address.
	 */
	mib_t mib[MibDepth];

	public:
	/**
	 * The default constructor.
	 *
	 * This is available to defer initialisation to a later moment.
	 * This might be useful when initialising global or static
	 * instances by a character string repesented name.
	 */
	constexpr Sysctl() : mib{} {}

	/**
	 * Initialise the MIB address from a character string.
	 *
	 * @param name
	 *	The symbolic name of the sysctl
	 * @throws sys::sc_error<error>
	 *	May throw an exception if the addressed sysct does
	 *	not exist or if the address is too long to store
	 */
	Sysctl(char const * const name) {
		size_t length = MibDepth;
		if (sysctlnametomib(name, this->mib, &length) == -1) {
			throw sc_error<error>{errno};
		}
		assert(length == MibDepth && "MIB depth mismatch");
	}

	/**
	 * Initialise the MIB address directly.
	 *
	 * Some important sysctl values have a fixed address that
	 * can be initialised at compile time with a noexcept guarantee.
	 *
	 * Spliting the MIB address into head and tail makes sure
	 * that `Sysctl(char *)` does not match the template and is
	 * instead implicitly cast to invoke `Sysctl(char const *)`.
	 *
	 * @tparam Tail
	 *	The types of the trailing MIB address values (must
	 *	be mib_t)
	 * @param head,tail
	 *	The mib
	 */
	template <typename... Tail>
	constexpr Sysctl(mib_t const head, Tail const... tail) noexcept :
	    mib{head, tail...} {
		static_assert(MibDepth == sizeof...(Tail) + 1,
		              "MIB depth mismatch");
	}

	/**
	 * Update the given buffer with a value retrieved from the
	 * sysctl.
	 *
	 * @param buf,bufsize
	 *	The target buffer and its size
	 * @throws sys::sc_error<error>
	 *	Throws if value retrieval fails or is incomplete,
	 *	e.g. because the value does not fit into the target
	 *	buffer
	 */
	void get(void * const buf, size_t const bufsize) const {
		auto len = bufsize;
		if (sysctl(this->mib, MibDepth, buf, &len, nullptr, 0)
		    == -1) {
			throw sc_error<error>{errno};
		}
	}

	/**
	 * Update the given value with a value retreived from the
	 * sysctl.
	 *
	 * @tparam T
	 *	The type store the sysctl value in
	 * @param value
	 *	A reference to the target value
	 * @throws sys::sc_error<error>
	 *	Throws if value retrieval fails or is incomplete,
	 *	e.g. because the value does not fit into the target
	 *	type
	 */
	template <typename T>
	void get(T & value) const {
		get(&value, sizeof(T));
	}

	/**
	 * Retrieve an array from the sysctl address.
	 *
	 * This is useful to retrieve variable length sysctls, like
	 * characer strings.
	 *
	 * @tparam T
	 *	The type stored in the array
	 * @return
	 *	And array of T with the right length to store the
	 *	whole sysctl value
	 * @throws sys::sc_error<error>
	 *	May throw if the size of the sysctl increases after
	 *	the length was queried
	 */
	template <typename T>
	std::unique_ptr<T[]> get() const {
		size_t len = 0;
		if (sysctl(this->mib, MibDepth, nullptr, &len, nullptr, 0)
		    == -1) {
			throw sc_error<error>{errno};
		}
		auto result = std::unique_ptr<T[]>(new T[len / sizeof(T)]);
		get(result.get(), len);
		return result;
	}

	/**
	 * Update the the sysctl value with the given buffer.
	 *
	 * @param buf,bufsize
	 *	The source buffer
	 * @throws sys::sc_error<error>
	 *	If the source buffer cannot be stored in the sysctl
	 */
	void set(void const * const buf, size_t const bufsize) {
		if (sysctl(this->mib, MibDepth, nullptr, nullptr,
		             buf, bufsize) == -1) {
			throw sc_error<error>{errno};
		}
	}

	/**
	 * Update the the sysctl value with the given value.
	 *
	 * @tparam T
	 *	The value type
	 * @param value
	 *	The value to set the sysctl to
	 */
	template <typename T>
	void set(T const & value) {
		set(&value, sizeof(T));
	}
};

/**
 * Create a Sysctl instances.
 *
 * This is only compatible with creating sysctls from predefined MIBs.
 *
 * @tparam Args
 *	List of argument types, should all be pid_t
 * @param args
 *	List of initialising arguments
 * @return
 *	A Sysctl instance with the depth matching the number of arguments
 */
template <typename... Args>
constexpr Sysctl<sizeof...(Args)> make_Sysctl(Args const... args) {
	return {args...};
}

/**
 * This is a wrapper around Sysctl that allows semantically transparent
 * use of a sysctl.
 *
 * ~~~ c++
 * Sync<int, Sysctl<3>> sndUnit{{"hw.snd.default_unit"}};
 * if (sndUnit != 3) {    // read from sysctl
 *	sndUnit = 3;      // assign to sysctl
 * }
 * ~~~
 *
 * Note that both assignment and read access (implemented through
 * type casting to T) may throw an exception.
 *
 * @tparam T
 *	The type to represent the sysctl as
 * @tparam SysctlT
 *	The Sysctl type
 */
template <typename T, class SysctlT>
class Sync {
	private:
	/**
	 * A sysctl to represent.
	 */
	SysctlT sysctl;

	public:
	/**
	 * The default constructor.
	 *
	 * This is available to defer initialisation to a later moment.
	 * This might be useful when initialising global or static
	 * instances by a character string repesented name.
	 */
	constexpr Sync() {}

	/**
	 * The constructor copies the given Sysctl instance.
	 *
	 * @param sysctl
	 *	The Sysctl instance to represent
	 */
	constexpr Sync(SysctlT const & sysctl) noexcept : sysctl{sysctl} {}

	/**
	 * Transparently assiges values of type T to the represented
	 * Sysctl instance.
	 *
	 * @param value
	 *	The value to assign
	 * @return
	 *	A self reference
	 */
	Sync & operator =(T const & value) {
		this->sysctl.set(value);
		return *this;
	}

	/**
	 * Implicitly cast to the represented type.
	 *
	 * @return
	 *	Returns the value from the sysctl
	 */
	operator T () const {
		T value;
		this->sysctl.get(value);
		return value;
	}
};

/**
 * A convenience alias around Sync.
 *
 * ~~~ c++
 * // Sync<int, Sysctl<3>> sndUnit{{"hw.snd.default_unit"}};
 * SysctlSync<int, 3> sndUnit{{"hw.snd.default_unit"}};
 * if (sndUnit != 3) {    // read from sysctl
 *	sndUnit = 3;      // assign to sysctl
 * }
 * ~~~
 *
 * @tparam T
 *	The type to represent the sysctl as
 * @tparam MibDepth
 *	The maximum allowed MIB depth
 */
template <typename T, size_t MibDepth>
using SysctlSync = Sync<T, Sysctl<MibDepth>>;

/**
 * A read once representation of a Sysctl.
 *
 * This reads a sysctl once upon construction and always returns that
 * value. It does not support assignment.
 *
 * This class is intended for sysctls that are not expected to change,
 * such as hw.ncpu. A special property of this class is that the
 * constructor does not throw and takes a default value in case reading
 * the sysctl fails.
 *
 * ~~~ c++
 * // Read number of CPU cores, assume 1 on failure:
 * Once<coreid_t, Sysctl<2>> ncpu{1, {CTL_HW, HW_NCPU}};
 * // Equivalent:
 * int hw_ncpu;
 * try {
 * 	Sysctl<2>{CTL_HW, HW_NCPU}.get(hw_ncpu);
 * } catch (sys::sc_error<error>) {
 * 	hw_ncpu = 1;
 * }
 * ~~~
 *
 * @tparam T
 *	The type to represent the sysctl as
 * @tparam SysctlT
 *	The Sysctl type
 */
template <typename T, class SysctlT>
class Once {
	private:
	/**
	 * The sysctl value read upon construction.
	 */
	T value;

	public:
	/**
	 * The constructor tries to read and store the requested sysctl.
	 *
	 * If reading the requested sysctl fails for any reason,
	 * the given value is stored instead.
	 *
	 * @param value
	 *	The fallback value
	 * @param sysctl
	 *	The sysctl to represent
	 */
	Once(T const & value, SysctlT const & sysctl) noexcept {
		try {
			sysctl.get(this->value);
		} catch (sc_error<error>) {
			this->value = value;
		}
	}

	/**
	 * Return a const reference to the value.
	 *
	 * @return
	 *	A const reference to the value
	 */
	operator T const &() const {
		return this->value;
	}
};

/**
 * A convenience alias around Once.
 *
 * ~~~ c++
 * // Once<coreid_t, Sysctl<2>> ncpu{0, {CTL_HW, HW_NCPU}};
 * SysctlOnce<coreid_t, 2> ncpu{1, {CTL_HW, HW_NCPU}};
 * ~~~
 *
 * @tparam T
 *	The type to represent the sysctl as
 * @tparam MibDepth
 *	The maximum allowed MIB depth
 */
template <typename T, size_t MibDepth>
using SysctlOnce = Once<T, Sysctl<MibDepth>>;

/**
 * This creates a Once instance.
 *
 * This is intended for cases when a Once instance is created as a
 * temporary to retrieve a value, using it's fallback to a default
 * mechanism.
 *
 * @tparam T
 *	The value type
 * @tparam SysctlT
 *	The Sysctl type
 * @param value
 *	The default value to fall back to
 * @param sysctl
 *	The sysctl to try and read from
 */
template <typename T, class SysctlT>
constexpr Once<T, SysctlT> make_Once(T const & value, SysctlT const & sysctl)
noexcept { return {value, sysctl}; }

} /* namespace ctl */
} /* namespace sys */

#endif /* _POWERDXX_SYS_SYSCTL_HPP_ */
<|endoftext|>