<commit_before>#include "PySoundGenerator.hpp"

#if PY_MAJOR_VERSION >= 3
/**
 * @brief PySoundGenerator::PySoundGenerator
 * @param progName
 * @param pyInstructions
 *
 * The constructor of the PySoundGeneratorclass.
 * Sets up the python interpreter, the instructions with
 * which it will be fed, the class variables and the break shortcut.
 */
PySoundGenerator::PySoundGenerator(char* progName, char* pyInstructions){
    if(pyInstructions == QString()){
        Q_EMIT doneSignal(tr("File is empty. Nothing to execute."), -1);
        return;
    }

    Py_SetProgramName(QString(progName).toWCharArray());
    Py_Initialize();
    ownExcept = QString();
    exceptNum = -1;
    abortAction = new QAction(this);
    abortAction->setShortcut(QKeySequence("Ctrl-C"));
    connect(abortAction, SIGNAL(triggered()), this, SLOT(terminated()));

    PyObject* module = PyImport_AddModule("__main__");
    sys = PyImport_ImportModule("sys");
    PyObject *path = PyObject_GetAttrString(sys, "path");
    PyList_Append(path, PyString_FromString("../../../"));
    PyList_Append(path, PyString_FromString("."));
    dict = PyModule_GetDict(module);
    if(!dict){
        exceptionOccurred();
        Q_EMIT doneSignal(ownExcept, -1);
        return;
    }
    execute("import AudioPython");
    execute(pyInstructions);
    execute("samples = AudioPython.compute_samples(channels, None)");

    device = new AudioOutputProcessor();
    connect(device, SIGNAL(startWriting()), this, SLOT(setReady()));
    ready = true;
    device->start();
}

/**
 * @brief PySoundGenerator::~PySoundGenerator
 *
 * The destructor of the PySoundGenerator class.
 * Kills the python interpreter.
 */
PySoundGenerator::~PySoundGenerator(){
    Py_DECREF(sys);
    delete abortAction;
    Py_Finalize();
    device->exit();
    // Wait for the end of QThread before deletion, otherwise it raises an error
    while(device->isRunning())
        ;
    delete device;
}

/**
 * @brief PySoundGenerator::run
 *
 * The main loop. Calls the user code executing method
 * and Q_EMITs a signal when it's done.
 */
void PySoundGenerator::run(){
    write();
    while(!ready)
        QCoreApplication::processEvents();
}

/**
 * @brief PySoundGenerator::write
 *
 * Gets samples and does error handling. The actual
 * main loop, so to say.
 */
void PySoundGenerator::write(){
    while(ready){
        PyObject* check = execute("AudioPython.yield_raw(samples, None)");
        if(!check){
            exceptionOccurred();
            Q_EMIT doneSignal(ownExcept, exceptNum);
            break;
        }
        if(PyBytes_Check(check))
            stream(check);
    }
}

/**
 * @brief PySoundGenerator::execute
 * @return PyObject* / NULL if there was an exception
 *         in the python interpreter.
 *
 * executes the python code in the interpreter.
 */
PyObject* PySoundGenerator::execute(QString instruct){
    return PyRun_String(instruct.toLocal8Bit().data(),
                        Py_file_input, dict, dict);
}

/**
 * @brief PySoundGenerator::stream
 * @param process
 *
 * writes the generated bytes to the IODevice
 */
void PySoundGenerator::stream(PyObject* data){
    ready = device->write(PyBytes_AsString(data), PyBytes_Size(data));
}

/**
 * @brief PySoundGenerator::updateCode
 * @param filename
 * @param instructions
 * @return true if the name of the program could be changed,
 *         false otherwise
 *
 * Updates the code of the currently running Python interpreter.
 */
bool PySoundGenerator::updateCode(QString filename, QString instructions){
    Py_SetProgramName(filename.toWCharArray());
    execute(instructions.toLocal8Bit().data());
    return true;
}

/**
 * @brief PySoundGenerator::exceptionOccurred
 * @return PythonException
 *
 * Fetches the Python Exception and translates it to a QString
 * and an int representing exception and line number.
 */
void PySoundGenerator::exceptionOccurred(){
    PyObject *errtype, *errvalue, *traceback;
    PyObject *mod;
    PyObject *ret, *list, *string;
    PyErr_Fetch(&errtype, &errvalue, &traceback);
    PyErr_NormalizeException(&errtype, &errvalue, &traceback);
    QString exceptionText = QString(PyBytes_AsString(PyObject_Str(errtype)));
    exceptionText.append(": '");
    exceptionText.append(PyBytes_AsString(PyObject_Str(errvalue)));
    QRegExp line("line [0-9]+");
    if(line.indexIn(exceptionText) != -1){
        QString text = line.capturedTexts().at(0);
        exceptionText.replace(" (<string>, " + text + ")", "");
        exceptionText.append("' at " + text);
        text.replace("line ", "");
        exceptNum = text.toInt();
    } else{
        mod = PyImport_ImportModule("traceback");
        list = PyObject_CallMethod(mod, (char*)"format_exception",
                               (char*)"OOO", errtype, errvalue, traceback);
        if(list == 0){
            ownExcept = exceptionText;
            exceptNum = -1;
            return;
        }
        string = PyBytes_FromString("\n");
        ret = _PyBytes_Join(string, list);
        if(line.indexIn(PyBytes_AsString(ret)) != -1){
            QString text = line.capturedTexts().at(0);
            exceptionText.append("' at " + text);
            text.replace("line ", "");
            exceptNum = text.toInt();
        } else{
            exceptNum = -1;
        }
        Py_DECREF(list);
        Py_DECREF(string);
        Py_DECREF(ret);
    }
    PyErr_Clear();
    ownExcept = exceptionText;
}

/**
 * @brief PySoundGenerator::terminated
 *
 * SLOT that is called when the user interrupt(CTRL-C) SIGNAL
 * is Q_EMITted.
 */
void PySoundGenerator::terminated(){
    ownExcept = tr("User Terminated.");
}

/**
 * @brief PySoundGenerator::setReady
 * @param set
 *
 * Setter for the ready-variable that indicates
 * whether writing to the IODevice is allowed.
 */
void PySoundGenerator::setReady(){
    ready = true;
    write();
}
#else
/**
 * @brief PySoundGenerator::PySoundGenerator
 * @param progName
 * @param pyInstructions
 *
 * The constructor of the PySoundGeneratorclass.
 * Sets up the python interpreter, the instructions with
 * which it will be fed, the class variables and the break shortcut.
 */
PySoundGenerator::PySoundGenerator(char* progName, char* pyInstructions){
    if(pyInstructions == QString()){
        Q_EMIT doneSignal(tr("File is empty. Nothing to execute."), -1);
        return;
    }

    Py_SetProgramName(progName);
    Py_Initialize();
    ownExcept = QString();
    exceptNum = -1;
    abortAction = new QAction(this);
    abortAction->setShortcut(QKeySequence("Ctrl-C"));
    connect(abortAction, SIGNAL(triggered()), this, SLOT(terminated()));

    PyObject* module = PyImport_AddModule("__main__");
    sys = PyImport_ImportModule("sys");
//    PyObject *path = PyObject_GetAttrString(sys, "path");
//    PyList_Append(path, PyString_FromString("../../../"));
//    PyList_Append(path, PyString_FromString("."));
    dict = PyModule_GetDict(module);
    if(!dict){
        exceptionOccurred();
        Q_EMIT doneSignal(ownExcept, -1);
        return;
    }
    execute("import AudioPython");
    execute(pyInstructions);
    execute("samples = AudioPython.compute_samples(channels, None)");

    device = new AudioOutputProcessor();
    connect(device, SIGNAL(startWriting()), this, SLOT(setReady()));
    ready = true;
    device->start();
}

/**
 * @brief PySoundGenerator::~PySoundGenerator
 *
 * The destructor of the PySoundGenerator class.
 * Kills the python interpreter.
 */
PySoundGenerator::~PySoundGenerator(){
    Py_DECREF(sys);
    delete abortAction;
    Py_Finalize();
    device->exit();
    // Wait for the end of QThread before deletion, otherwise it raises an error
    while(device->isRunning())
        ;
    delete device;
}

/**
 * @brief PySoundGenerator::run
 *
 * The main loop. Calls the user code executing method
 * and Q_EMITs a signal when it's done.
 */
void PySoundGenerator::run(){
    write();
    while(!ready)
        QCoreApplication::processEvents();
}

/**
 * @brief PySoundGenerator::write
 *
 * Gets samples and does error handling. The actual
 * main loop, so to say.
 */
void PySoundGenerator::write(){
    while(ready){
        PyObject* check = execute("AudioPython.yield_raw(samples, None)");
        if(!check){
            exceptionOccurred();
            Q_EMIT doneSignal(ownExcept, exceptNum);
            break;
        }
        if(PyBytes_Check(check))
            stream(check);
    }
}

/**
 * @brief PySoundGenerator::execute
 * @return PyObject* / NULL if there was an exception
 *         in the python interpreter.
 *
 * executes the python code in the interpreter.
 */
PyObject* PySoundGenerator::execute(QString instruct){
    return PyRun_String(instruct.toLocal8Bit().data(),
                        Py_file_input, dict, dict);
}

/**
 * @brief PySoundGenerator::stream
 * @param process
 *
 * writes the generated bytes to the IODevice
 */
void PySoundGenerator::stream(PyObject* data){
    ready = device->write(PyBytes_AsString(data), PyBytes_Size(data));
}

/**
 * @brief PySoundGenerator::updateCode
 * @param filename
 * @param instructions
 * @return true if the name of the program could be changed,
 *         false otherwise
 *
 * Updates the code of the currently running Python interpreter.
 */
bool PySoundGenerator::updateCode(QString filename, QString instructions){
    Py_SetProgramName(filename.toLocal8Bit().data());
    execute(instructions.toLocal8Bit().data());
    return true;
}

/**
 * @brief PySoundGenerator::exceptionOccurred
 * @return PythonException
 *
 * Fetches the Python Exception and translates it to a QString
 * and an int representing exception and line number.
 */
void PySoundGenerator::exceptionOccurred(){
    PyObject *errtype, *errvalue, *traceback;
    PyObject *mod;
    PyObject *ret, *list, *string;
    PyErr_Fetch(&errtype, &errvalue, &traceback);
    PyErr_NormalizeException(&errtype, &errvalue, &traceback);
    QString exceptionText = QString(PyString_AsString(PyObject_Str(errtype)));
    exceptionText.append(": '");
    exceptionText.append(PyString_AsString(PyObject_Str(errvalue)));
    QRegExp line("line [0-9]+");
    if(line.indexIn(exceptionText) != -1){
        QString text = line.capturedTexts().at(0);
        exceptionText.replace(" (<string>, " + text + ")", "");
        exceptionText.append("' at " + text);
        text.replace("line ", "");
        exceptNum = text.toInt();
    } else{
        mod = PyImport_ImportModule("traceback");
        list = PyObject_CallMethod(mod, (char*)"format_exception",
                               (char*)"OOO", errtype, errvalue, traceback);
        if(list == 0){
            ownExcept = exceptionText;
            exceptNum = -1;
            return;
        }
        string = PyString_FromString("\n");
        ret = _PyString_Join(string, list);
        if(line.indexIn(PyString_AsString(ret)) != -1){
            QString text = line.capturedTexts().at(0);
            exceptionText.append("' at " + text);
            text.replace("line ", "");
            exceptNum = text.toInt();
        } else{
            exceptNum = -1;
        }
        Py_DECREF(list);
        Py_DECREF(string);
        Py_DECREF(ret);
    }
    PyErr_Clear();
    ownExcept = exceptionText;
}

/**
 * @brief PySoundGenerator::terminated
 *
 * SLOT that is called when the user interrupt(CTRL-C) SIGNAL
 * is Q_EMITted.
 */
void PySoundGenerator::terminated(){
    ownExcept = tr("User Terminated.");
}

/**
 * @brief PySoundGenerator::setReady
 * @param set
 *
 * Setter for the ready-variable that indicates
 * whether writing to the IODevice is allowed.
 */
void PySoundGenerator::setReady(){
    ready = true;
    write();
}
#endif
<commit_msg>Updated PySoundGenerator<commit_after>#include "PySoundGenerator.hpp"

#if PY_MAJOR_VERSION >= 3
/**
 * @brief PySoundGenerator::PySoundGenerator
 * @param progName
 * @param pyInstructions
 *
 * The constructor of the PySoundGeneratorclass.
 * Sets up the python interpreter, the instructions with
 * which it will be fed, the class variables and the break shortcut.
 */
PySoundGenerator::PySoundGenerator(char* progName, char* pyInstructions){
    if(pyInstructions == QString()){
        Q_EMIT doneSignal(tr("File is empty. Nothing to execute."), -1);
        return;
    }

    Py_SetProgramName(QString(progName).toWCharArray());
    Py_Initialize();
    ownExcept = QString();
    exceptNum = -1;
    abortAction = new QAction(this);
    abortAction->setShortcut(QKeySequence("Ctrl-C"));
    connect(abortAction, SIGNAL(triggered()), this, SLOT(terminated()));

    PyObject* module = PyImport_AddModule("__main__");
    sys = PyImport_ImportModule("sys");
    PyObject *path = PyObject_GetAttrString(sys, "path");
    PyList_Append(path, PyString_FromString("../../../"));
    PyList_Append(path, PyString_FromString("."));
    dict = PyModule_GetDict(module);
    if(!dict){
        exceptionOccurred();
        Q_EMIT doneSignal(ownExcept, -1);
        return;
    }
    execute("import AudioPython");
    execute(pyInstructions);
    execute("samples = AudioPython.compute_samples(channels, None)");

    device = new AudioOutputProcessor();
    connect(device, SIGNAL(startWriting()), this, SLOT(setReady()));
    ready = true;
    device->start();
}

/**
 * @brief PySoundGenerator::~PySoundGenerator
 *
 * The destructor of the PySoundGenerator class.
 * Kills the python interpreter.
 */
PySoundGenerator::~PySoundGenerator(){
    Py_DECREF(sys);
    delete abortAction;
    Py_Finalize();
    device->exit();
    // Wait for the end of QThread before deletion, otherwise it raises an error
    while(device->isRunning())
        ;
    delete device;
}

/**
 * @brief PySoundGenerator::run
 *
 * The main loop. Calls the user code executing method
 * and Q_EMITs a signal when it's done.
 */
void PySoundGenerator::run(){
    write();
    while(!ready)
        QCoreApplication::processEvents();
}

/**
 * @brief PySoundGenerator::write
 *
 * Gets samples and does error handling. The actual
 * main loop, so to say.
 */
void PySoundGenerator::write(){
    while(ready){
        PyObject* check = execute("AudioPython.yield_raw(samples, None)");
        if(!check){
            exceptionOccurred();
            Q_EMIT doneSignal(ownExcept, exceptNum);
            break;
        }
        if(PyBytes_Check(check))
            stream(check);
    }
}

/**
 * @brief PySoundGenerator::execute
 * @return PyObject* / NULL if there was an exception
 *         in the python interpreter.
 *
 * executes the python code in the interpreter.
 */
PyObject* PySoundGenerator::execute(QString instruct){
    return PyRun_String(instruct.toLocal8Bit().data(),
                        Py_file_input, dict, dict);
}

/**
 * @brief PySoundGenerator::stream
 * @param process
 *
 * writes the generated bytes to the IODevice
 */
void PySoundGenerator::stream(PyObject* data){
    ready = device->write(PyBytes_AsString(data), PyBytes_Size(data));
}

/**
 * @brief PySoundGenerator::updateCode
 * @param filename
 * @param instructions
 * @return true if the name of the program could be changed,
 *         false otherwise
 *
 * Updates the code of the currently running Python interpreter.
 */
bool PySoundGenerator::updateCode(QString filename, QString instructions){
    Py_SetProgramName(filename.toWCharArray());
    execute(instructions.toLocal8Bit().data());
    return true;
}

/**
 * @brief PySoundGenerator::exceptionOccurred
 * @return PythonException
 *
 * Fetches the Python Exception and translates it to a QString
 * and an int representing exception and line number.
 */
void PySoundGenerator::exceptionOccurred(){
    PyObject *errtype, *errvalue, *traceback;
    PyObject *mod;
    PyObject *ret, *list, *string;
    PyErr_Fetch(&errtype, &errvalue, &traceback);
    PyErr_NormalizeException(&errtype, &errvalue, &traceback);
    QString exceptionText = QString(PyBytes_AsString(PyObject_Str(errtype)));
    exceptionText.append(": '");
    exceptionText.append(PyBytes_AsString(PyObject_Str(errvalue)));
    QRegExp line("line [0-9]+");
    if(line.indexIn(exceptionText) != -1){
        QString text = line.capturedTexts().at(0);
        exceptionText.replace(" (<string>, " + text + ")", "");
        exceptionText.append("' at " + text);
        text.replace("line ", "");
        exceptNum = text.toInt();
    } else{
        mod = PyImport_ImportModule("traceback");
        list = PyObject_CallMethod(mod, (char*)"format_exception",
                               (char*)"OOO", errtype, errvalue, traceback);
        if(list == 0){
            ownExcept = exceptionText;
            exceptNum = -1;
            return;
        }
        string = PyBytes_FromString("\n");
        ret = _PyBytes_Join(string, list);
        if(line.indexIn(PyBytes_AsString(ret)) != -1){
            QString text = line.capturedTexts().at(0);
            exceptionText.append("' at " + text);
            text.replace("line ", "");
            exceptNum = text.toInt();
        } else{
            exceptNum = -1;
        }
        Py_DECREF(list);
        Py_DECREF(string);
        Py_DECREF(ret);
    }
    PyErr_Clear();
    ownExcept = exceptionText;
}

/**
 * @brief PySoundGenerator::terminated
 *
 * SLOT that is called when the user interrupt(CTRL-C) SIGNAL
 * is Q_EMITted.
 */
void PySoundGenerator::terminated(){
    ownExcept = tr("User Terminated.");
}

/**
 * @brief PySoundGenerator::setReady
 * @param set
 *
 * Setter for the ready-variable that indicates
 * whether writing to the IODevice is allowed.
 */
void PySoundGenerator::setReady(){
    ready = true;
    write();
}
#else
/**
 * @brief PySoundGenerator::PySoundGenerator
 * @param progName
 * @param pyInstructions
 *
 * The constructor of the PySoundGeneratorclass.
 * Sets up the python interpreter, the instructions with
 * which it will be fed, the class variables and the break shortcut.
 */
PySoundGenerator::PySoundGenerator(char* progName, char* pyInstructions){
    if(pyInstructions == QString()){
        Q_EMIT doneSignal(tr("File is empty. Nothing to execute."), -1);
        return;
    }

    Py_SetProgramName(progName);
    Py_Initialize();
    ownExcept = QString();
    exceptNum = -1;
    abortAction = new QAction(this);
    abortAction->setShortcut(QKeySequence("Ctrl-C"));
    connect(abortAction, SIGNAL(triggered()), this, SLOT(terminated()));

    PyObject* module = PyImport_AddModule("__main__");
    sys = PyImport_ImportModule("sys");
//    PyObject *path = PyObject_GetAttrString(sys, "path");
//    PyList_Append(path, PyString_FromString("../../../"));
//    PyList_Append(path, PyString_FromString("."));
    dict = PyModule_GetDict(module);
    if(!dict){
        exceptionOccurred();
        Q_EMIT doneSignal(ownExcept, -1);
        return;
    }
    execute("import AudioPython");
    execute("from AudioPython import *");
    execute(pyInstructions);
    execute("samples = AudioPython.compute_samples(channels, None)");
    execute("gen = AudioPython.yield_raw(samples, None)");

    device = new AudioOutputProcessor();
    connect(device, SIGNAL(startWriting()), this, SLOT(setReady()));
    ready = true;
    device->start();
}

/**
 * @brief PySoundGenerator::~PySoundGenerator
 *
 * The destructor of the PySoundGenerator class.
 * Kills the python interpreter.
 */
PySoundGenerator::~PySoundGenerator(){
    Py_DECREF(sys);
    delete abortAction;
    Py_Finalize();
    device->exit();
    // Wait for the end of QThread before deletion, otherwise it raises an error
    while(device->isRunning())
        ;
    delete device;
}

/**
 * @brief PySoundGenerator::run
 *
 * The main loop. Calls the user code executing method
 * and Q_EMITs a signal when it's done.
 */
void PySoundGenerator::run(){
    write();
    while(!ready)
        QCoreApplication::processEvents();
}

/**
 * @brief PySoundGenerator::write
 *
 * Gets samples and does error handling. The actual
 * main loop, so to say.
 */
void PySoundGenerator::write(){
    while(ready){
        PyObject* check = execute("next(gen)");
        if(!check){
            exceptionOccurred();
            Q_EMIT doneSignal(ownExcept, exceptNum);
            break;
        }
        if(PyBytes_Check(check))
            stream(check);
    }
}

/**
 * @brief PySoundGenerator::execute
 * @return PyObject* / NULL if there was an exception
 *         in the python interpreter.
 *
 * executes the python code in the interpreter.
 */
PyObject* PySoundGenerator::execute(QString instruct){
    return PyRun_String(instruct.toLocal8Bit().data(),
                        Py_file_input, dict, dict);
}

/**
 * @brief PySoundGenerator::stream
 * @param process
 *
 * writes the generated bytes to the IODevice
 */
void PySoundGenerator::stream(PyObject* data){
    ready = device->write(PyBytes_AsString(data), PyBytes_Size(data));
    qDebug() << ready;
}

/**
 * @brief PySoundGenerator::updateCode
 * @param filename
 * @param instructions
 * @return true if the name of the program could be changed,
 *         false otherwise
 *
 * Updates the code of the currently running Python interpreter.
 */
bool PySoundGenerator::updateCode(QString filename, QString instructions){
    Py_SetProgramName(filename.toLocal8Bit().data());
    execute(instructions.toLocal8Bit().data());
    execute("samples = AudioPython.compute_samples(channels, None)");
    execute("gen = AudioPython.yield_raw(samples, None)");
    return true;
}

/**
 * @brief PySoundGenerator::exceptionOccurred
 * @return PythonException
 *
 * Fetches the Python Exception and translates it to a QString
 * and an int representing exception and line number.
 */
void PySoundGenerator::exceptionOccurred(){
    PyObject *errtype, *errvalue, *traceback;
    PyObject *mod;
    PyObject *ret, *list, *string;
    PyErr_Fetch(&errtype, &errvalue, &traceback);
    PyErr_NormalizeException(&errtype, &errvalue, &traceback);
    QString exceptionText = QString(PyString_AsString(PyObject_Str(errtype)));
    exceptionText.append(": '");
    exceptionText.append(PyString_AsString(PyObject_Str(errvalue)));
    QRegExp line("line [0-9]+");
    if(line.indexIn(exceptionText) != -1){
        QString text = line.capturedTexts().at(0);
        exceptionText.replace(" (<string>, " + text + ")", "");
        exceptionText.append("' at " + text);
        text.replace("line ", "");
        exceptNum = text.toInt();
    } else{
        mod = PyImport_ImportModule("traceback");
        list = PyObject_CallMethod(mod, (char*)"format_exception",
                               (char*)"OOO", errtype, errvalue, traceback);
        if(list == 0){
            ownExcept = exceptionText;
            exceptNum = -1;
            return;
        }
        string = PyString_FromString("\n");
        ret = _PyString_Join(string, list);
        if(line.indexIn(PyString_AsString(ret)) != -1){
            QString text = line.capturedTexts().at(0);
            exceptionText.append("' at " + text);
            text.replace("line ", "");
            exceptNum = text.toInt();
        } else{
            exceptNum = -1;
        }
        Py_DECREF(list);
        Py_DECREF(string);
        Py_DECREF(ret);
    }
    PyErr_Clear();
    ownExcept = exceptionText;
}

/**
 * @brief PySoundGenerator::terminated
 *
 * SLOT that is called when the user interrupt(CTRL-C) SIGNAL
 * is Q_EMITted.
 */
void PySoundGenerator::terminated(){
    ownExcept = tr("User Terminated.");
}

/**
 * @brief PySoundGenerator::setReady
 * @param set
 *
 * Setter for the ready-variable that indicates
 * whether writing to the IODevice is allowed.
 */
void PySoundGenerator::setReady(){
    ready = true;
    write();
}
#endif
<|endoftext|>


<commit_before>#include "Node.hpp"

using namespace raw::root;

struct TypeNamePair {
    Node::Type   type;
    const char** aliases;
    size_t       nAliases;

    TypeNamePair(Node::Type type, ...): type(type), nAliases(0)
    {
        va_list args;
        const char* a;

        va_start(args, type);
        while ((a = va_arg(args, const char*)))
            ++nAliases;
        va_end(args);

        aliases = new const char*[nAliases];

        size_t i = 0;
        va_start(args, type);
        while ((a = va_arg(args, const char*)))
            aliases[i++] = a;
        va_end(args);
    }

    ~TypeNamePair()
    {
        delete [] aliases;
    }
};

const static TypeNamePair typeMaps[] = {
    TypeNamePair(Node::kInt8,   "char", "Char_t", "Bool_t", NULL),
    TypeNamePair(Node::kInt32,  "int", "Int_t", NULL),
    TypeNamePair(Node::kUInt32, "UInt_t", NULL),
    TypeNamePair(Node::kInt64,  "Long64_t", NULL),
    TypeNamePair(Node::kFloat,  "float", "Float_t", NULL),
    TypeNamePair(Node::kDouble, "double", "Double_t", NULL),
    TypeNamePair(Node::kString, "TString", NULL)
};
const static size_t nPairs = sizeof(typeMaps) / sizeof(TypeNamePair);

Node::Type Node::TypeFromStr(const std::string& typeName)
{
    for (size_t i = 0; i < nPairs; ++i) {
        const TypeNamePair& pair = typeMaps[i];
        for (size_t j = 0; j < pair.nAliases; ++j) {
            if (typeName.compare(pair.aliases[j]) == 0)
                return pair.type;
        }
    }
    return kUnknown;
}

Node::Node(Type type, const std::string& name, const void* addr):
    type (type), name(name), address(addr)
{
}

Node::Node(const std::string& type, const std::string& name, const void* addr):
    typeName(type), type (TypeFromStr(type)), name(name), address(addr)
{
}

Node::Node(const std::string& typeName, Type type, const std::string& name, const void* addr):
    typeName(typeName), type (type), name(name), address(addr)
{
}

Node::Node(const TObject& obj):
    typeName(obj.ClassName()), type(kDictionary), name(obj.GetName()), address(&obj)
{
}

std::string Node::getTypeName() const
{
    return typeName;
}

Node::Type Node::getType() const
{
    return type;
}

std::string Node::getName() const
{
    return name;
}

const void* Node::getAddress() const
{
    return address;
}

void Node::setType(Type t)
{
    type = t;
}

bool Node::isBasic() const
{
	return type > Node::kCollection && type <= Node::kString;
}

std::ostream& raw::root::operator << (std::ostream& os, Node::Type type)
{
   switch (type) {
       case Node::kDictionary:
           os << "dictionary";
           break;
       case Node::kCollection:
           os << "collection";
           break;
       case Node::kInt8: case Node::kInt32: case Node::kInt64:
           os << "integer";
           break;
       case Node::kUInt8: case Node::kUInt32: case Node::kUInt64:
           os << "unsigned";
           break;
       case Node::kFloat:
           os << "float";
           break;
       case Node::kDouble:
           os << "double";
           break;
       case Node::kString:
           os << "string";
           break;
       case Node::kUnknown:
           os << "unknown";
           break;
       default:
           os << (int)(type);
           break;
   }
   return os;
}
<commit_msg>Added string and unsigned int to TypeNamePair<commit_after>#include "Node.hpp"

using namespace raw::root;

struct TypeNamePair {
    Node::Type   type;
    const char** aliases;
    size_t       nAliases;

    TypeNamePair(Node::Type type, ...): type(type), nAliases(0)
    {
        va_list args;
        const char* a;

        va_start(args, type);
        while ((a = va_arg(args, const char*)))
            ++nAliases;
        va_end(args);

        aliases = new const char*[nAliases];

        size_t i = 0;
        va_start(args, type);
        while ((a = va_arg(args, const char*)))
            aliases[i++] = a;
        va_end(args);
    }

    ~TypeNamePair()
    {
        delete [] aliases;
    }
};

const static TypeNamePair typeMaps[] = {
    TypeNamePair(Node::kInt8,   "char", "Char_t", "Bool_t", NULL),
    TypeNamePair(Node::kInt32,  "int", "Int_t", NULL),
    TypeNamePair(Node::kUInt32, "unsigned int", "UInt_t", NULL),
    TypeNamePair(Node::kInt64,  "Long64_t", NULL),
    TypeNamePair(Node::kFloat,  "float", "Float_t", NULL),
    TypeNamePair(Node::kDouble, "double", "Double_t", NULL),
    TypeNamePair(Node::kString, "TString", "string", NULL)
};
const static size_t nPairs = sizeof(typeMaps) / sizeof(TypeNamePair);

Node::Type Node::TypeFromStr(const std::string& typeName)
{
    for (size_t i = 0; i < nPairs; ++i) {
        const TypeNamePair& pair = typeMaps[i];
        for (size_t j = 0; j < pair.nAliases; ++j) {
            if (typeName.compare(pair.aliases[j]) == 0)
                return pair.type;
        }
    }
    return kUnknown;
}

Node::Node(Type type, const std::string& name, const void* addr):
    type (type), name(name), address(addr)
{
}

Node::Node(const std::string& type, const std::string& name, const void* addr):
    typeName(type), type (TypeFromStr(type)), name(name), address(addr)
{
}

Node::Node(const std::string& typeName, Type type, const std::string& name, const void* addr):
    typeName(typeName), type (type), name(name), address(addr)
{
}

Node::Node(const TObject& obj):
    typeName(obj.ClassName()), type(kDictionary), name(obj.GetName()), address(&obj)
{
}

std::string Node::getTypeName() const
{
    return typeName;
}

Node::Type Node::getType() const
{
    return type;
}

std::string Node::getName() const
{
    return name;
}

const void* Node::getAddress() const
{
    return address;
}

void Node::setType(Type t)
{
    type = t;
}

bool Node::isBasic() const
{
	return type > Node::kCollection && type <= Node::kString;
}

std::ostream& raw::root::operator << (std::ostream& os, Node::Type type)
{
   switch (type) {
       case Node::kDictionary:
           os << "dictionary";
           break;
       case Node::kCollection:
           os << "collection";
           break;
       case Node::kInt8: case Node::kInt32: case Node::kInt64:
           os << "integer";
           break;
       case Node::kUInt8: case Node::kUInt32: case Node::kUInt64:
           os << "unsigned";
           break;
       case Node::kFloat:
           os << "float";
           break;
       case Node::kDouble:
           os << "double";
           break;
       case Node::kString:
           os << "string";
           break;
       case Node::kUnknown:
           os << "unknown";
           break;
       default:
           os << (int)(type);
           break;
   }
   return os;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2009 by Dr. Marc Boris Duerner
 * Copyright (C) 2009 by Tommi Meakitalo
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * As a special exception, you may use this file as part of a free
 * software library without restriction. Specifically, if other files
 * instantiate templates or use macros or inline functions from this
 * file, or you compile this file and link it with other files to
 * produce an executable, this file does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License. This exception does not however invalidate any other
 * reasons why the executable file might be covered by the GNU Library
 * General Public License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "cxxtools/xmlrpc/client.h"
#include "clientimpl.h"
#include "cxxtools/xmlrpc/remoteprocedure.h"
#include "cxxtools/xml/xmlerror.h"
#include "cxxtools/xml/startelement.h"
#include "cxxtools/xml/characters.h"
#include "cxxtools/xml/endelement.h"
#include "cxxtools/http/replyheader.h"
#include "cxxtools/selector.h"
#include "cxxtools/utf8codec.h"
#include "cxxtools/log.h"

namespace cxxtools {

namespace xmlrpc {

ClientImpl::ClientImpl()
: _state(OnBegin)
, _ts( new Utf8Codec )
, _reader(_ts)
, _formatter(_writer)
, _method(0)
, _timeout(Selectable::WaitInfinite)
{
    _writer.useIndent(false);
    _writer.useEndl(false);

    _formatter.addAlias("bool", "boolean");
}

ClientImpl::~ClientImpl()
{
}

void ClientImpl::beginCall(IDeserializer& r, IRemoteProcedure& method, ISerializer** argv, unsigned argc)
{
    _method = &method;
    _state = OnBegin;

    prepareRequest(method.name(), argv, argc);

    beginExecute();

    _reader.reset(_ts);
    _scanner.begin(r, _context);
}

void ClientImpl::call(IDeserializer& r, IRemoteProcedure& method, ISerializer** argv, unsigned argc)
{
    _method = &method;
    _state = OnBegin;

    prepareRequest(method.name(), argv, argc);

    std::istringstream is(execute());
    _ts.attach(is);
    _reader.reset(_ts);
    _scanner.begin(r, _context);

    while( _reader.get().type() !=  cxxtools::xml::Node::EndDocument )
    {
        const cxxtools::xml::Node& node = _reader.get();
        advance(node);
        _reader.next();
    }

    // let xml::ParseError SerializationError, ConversionError propagate

    if (_method->failed() )
    {
        _state = OnBegin;
        throw _fault;
    }

    _state = OnBegin;

    // _method contains a valid return value now
}

const IRemoteProcedure* ClientImpl::activeProcedure() const
{
    return _method;
}

void ClientImpl::cancel()
{
    _method = 0;
}

void ClientImpl::onReadReplyBegin(std::istream& is)
{
    _ts.attach(is);
}

std::size_t ClientImpl::onReadReply()
{
    std::size_t n = 0;

    try
    {
        while(true)
        {
            std::streamsize m = _ts.buffer().import();
            if( ! m )
                break;

            n += m;

            while( _reader.advance() ) // xml::ParseError
            {
                const cxxtools::xml::Node& node = _reader.get();
                advance(node); // SerializationError, ConversionError
            }
        }
    }
    catch(const xml::XmlError& error)
    {
        _method->setFault(Fault::invalidXmlRpc, error.what());
        throw;
    }
    catch(const SerializationError& error)
    {
        _method->setFault(Fault::invalidMethodParameters, error.what());
        throw;
    }
    catch(const ConversionError& error)
    {
        _method->setFault(Fault::invalidMethodParameters, error.what());
        throw;
    }

    return n;
}

void ClientImpl::onReplyFinished()
{
    IRemoteProcedure* method = _method;
    _method = 0;
    method->onFinished();
}

void ClientImpl::onErrorOccured(const std::exception& e)
{
    if (_method)
    {
        // TODO do not map local exceptions to cxxtools::xmlrpc::Fault

        if (!_method->failed())
            _method->setFault(Fault::systemError, e.what());

        IRemoteProcedure* method = _method;
        _method = 0;
        method->onFinished();
    }
    else
    {
        throw;
    }
}

void ClientImpl::prepareRequest(const std::string& name, ISerializer** argv, unsigned argc)
{
    _writer.begin( prepareRequest() );
    _writer.writeStartElement( L"methodCall" );
    _writer.writeElement( L"methodName", cxxtools::String::widen(name) );
    _writer.writeStartElement( L"params" );

    for(unsigned n = 0; n < argc; ++n)
    {
        _writer.writeStartElement( L"param" );
        argv[n]->format(_formatter);
        _writer.writeEndElement();
    }

    _writer.writeEndElement();
    _writer.writeEndElement();
    _writer.flush();
}

void ClientImpl::advance(const cxxtools::xml::Node& node)
{
    switch(_state)
    {
        case OnBegin:
        {
            if(node.type() == xml::Node::StartElement)
            {
                const xml::StartElement& se = static_cast<const xml::StartElement&>(node);
                if( se.name() != L"methodResponse" )
                    throw SerializationError("invalid XML-RPC methodCall");

                _state = OnMethodResponseBegin;
            }

            break;
        }

        case OnMethodResponseBegin:
        {
            if(node.type() == xml::Node::StartElement) // <params> or <fault>
            {
                const xml::StartElement& se = static_cast<const xml::StartElement&>(node);
                if( se.name() == L"params")
                {
                    _state = OnParamsBegin;
                    break;
                }

                else if( se.name() == L"fault")
                {
                    _fh.begin(_fault);
                    _scanner.begin(_fh, _context);
                    _state = OnFaultBegin;
                    break;
                }

                throw SerializationError("invalid XML-RPC methodCall");
            }
            break;
        }

        case OnFaultBegin:
        {
            bool finished = _scanner.advance(node); // start with <value>
            if(finished)
            {
                // </fault>
                _state = OnFaultEnd;
            }

            break;
        }

        case OnFaultEnd:
        {
            if(node.type() == xml::Node::EndElement) // </methodResponse>
            {
                const xml::EndElement& ee = static_cast<const xml::EndElement&>(node);
                if( ee.name() != L"methodResponse" )
                    throw SerializationError("invalid XML-RPC methodCall");

                _method->setFault(_fault.rc(), _fault.text());

                _state = OnFaultResponseEnd;
            }
            break;
        }

        case OnFaultResponseEnd:
        {
            _state = OnFaultResponseEnd;
            break;
        }

        case OnParamsBegin:
        {
            if(node.type() == xml::Node::StartElement) // <param>
            {
                const xml::StartElement& se = static_cast<const xml::StartElement&>(node);
                if( se.name() != L"param" )
                    throw SerializationError("invalid XML-RPC methodCall");

                _state = OnParam;
            }

            break;
        }

        case OnParam:
        {
            bool finished = _scanner.advance(node); // start with <value>
            if(finished)
            {
                // </param>
                _state = OnParamEnd;
            }

            break;
        }

        case OnParamEnd:
        {
            if(node.type() == xml::Node::EndElement) // </params>
            {
                const xml::EndElement& ee = static_cast<const xml::EndElement&>(node);
                if( ee.name() != L"params" )
                    throw SerializationError("invalid XML-RPC methodCall");

                _state = OnParamsEnd;
            }
            break;
        }

        case OnParamsEnd:
        {
            if(node.type() == xml::Node::EndElement) // </methodResponse>
            {
                const xml::EndElement& ee = static_cast<const xml::EndElement&>(node);
                if( ee.name() != L"methodResponse" )
                    throw SerializationError("invalid XML-RPC methodCall");

                _state = OnMethodResponseEnd;
            }
            break;
        }

        case OnMethodResponseEnd:
        {
            _state = OnMethodResponseEnd;
            break;
        }
    }
}

}

}
<commit_msg>fix bug where syncronous xmlrpc request prevented keep alive in xmlrpc client<commit_after>/*
 * Copyright (C) 2009 by Dr. Marc Boris Duerner
 * Copyright (C) 2009 by Tommi Meakitalo
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * As a special exception, you may use this file as part of a free
 * software library without restriction. Specifically, if other files
 * instantiate templates or use macros or inline functions from this
 * file, or you compile this file and link it with other files to
 * produce an executable, this file does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License. This exception does not however invalidate any other
 * reasons why the executable file might be covered by the GNU Library
 * General Public License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "cxxtools/xmlrpc/client.h"
#include "clientimpl.h"
#include "cxxtools/xmlrpc/remoteprocedure.h"
#include "cxxtools/xml/xmlerror.h"
#include "cxxtools/xml/startelement.h"
#include "cxxtools/xml/characters.h"
#include "cxxtools/xml/endelement.h"
#include "cxxtools/http/replyheader.h"
#include "cxxtools/selector.h"
#include "cxxtools/utf8codec.h"
#include "cxxtools/log.h"

namespace cxxtools {

namespace xmlrpc {

ClientImpl::ClientImpl()
: _state(OnBegin)
, _ts( new Utf8Codec )
, _reader(_ts)
, _formatter(_writer)
, _method(0)
, _timeout(Selectable::WaitInfinite)
{
    _writer.useIndent(false);
    _writer.useEndl(false);

    _formatter.addAlias("bool", "boolean");
}

ClientImpl::~ClientImpl()
{
}

void ClientImpl::beginCall(IDeserializer& r, IRemoteProcedure& method, ISerializer** argv, unsigned argc)
{
    _method = &method;
    _state = OnBegin;

    prepareRequest(method.name(), argv, argc);

    beginExecute();

    _reader.reset(_ts);
    _scanner.begin(r, _context);
}

void ClientImpl::call(IDeserializer& r, IRemoteProcedure& method, ISerializer** argv, unsigned argc)
{
    _method = &method;
    _state = OnBegin;

    prepareRequest(method.name(), argv, argc);

    std::istringstream is(execute());
    _ts.attach(is);
    _reader.reset(_ts);
    _scanner.begin(r, _context);

    while( _reader.get().type() !=  cxxtools::xml::Node::EndDocument )
    {
        const cxxtools::xml::Node& node = _reader.get();
        advance(node);
        _reader.next();
    }

    // let xml::ParseError SerializationError, ConversionError propagate

    if (_method->failed() )
    {
        _method = 0;
        _state = OnBegin;
        throw _fault;
    }

    _method = 0;
    _state = OnBegin;

    // _method contains a valid return value now
}

const IRemoteProcedure* ClientImpl::activeProcedure() const
{
    return _method;
}

void ClientImpl::cancel()
{
    _method = 0;
}

void ClientImpl::onReadReplyBegin(std::istream& is)
{
    _ts.attach(is);
}

std::size_t ClientImpl::onReadReply()
{
    std::size_t n = 0;

    try
    {
        while(true)
        {
            std::streamsize m = _ts.buffer().import();
            if( ! m )
                break;

            n += m;

            while( _reader.advance() ) // xml::ParseError
            {
                const cxxtools::xml::Node& node = _reader.get();
                advance(node); // SerializationError, ConversionError
            }
        }
    }
    catch(const xml::XmlError& error)
    {
        _method->setFault(Fault::invalidXmlRpc, error.what());
        throw;
    }
    catch(const SerializationError& error)
    {
        _method->setFault(Fault::invalidMethodParameters, error.what());
        throw;
    }
    catch(const ConversionError& error)
    {
        _method->setFault(Fault::invalidMethodParameters, error.what());
        throw;
    }

    return n;
}

void ClientImpl::onReplyFinished()
{
    IRemoteProcedure* method = _method;
    _method = 0;
    method->onFinished();
}

void ClientImpl::onErrorOccured(const std::exception& e)
{
    if (_method)
    {
        // TODO do not map local exceptions to cxxtools::xmlrpc::Fault

        if (!_method->failed())
            _method->setFault(Fault::systemError, e.what());

        IRemoteProcedure* method = _method;
        _method = 0;
        method->onFinished();
    }
    else
    {
        throw;
    }
}

void ClientImpl::prepareRequest(const std::string& name, ISerializer** argv, unsigned argc)
{
    _writer.begin( prepareRequest() );
    _writer.writeStartElement( L"methodCall" );
    _writer.writeElement( L"methodName", cxxtools::String::widen(name) );
    _writer.writeStartElement( L"params" );

    for(unsigned n = 0; n < argc; ++n)
    {
        _writer.writeStartElement( L"param" );
        argv[n]->format(_formatter);
        _writer.writeEndElement();
    }

    _writer.writeEndElement();
    _writer.writeEndElement();
    _writer.flush();
}

void ClientImpl::advance(const cxxtools::xml::Node& node)
{
    switch(_state)
    {
        case OnBegin:
        {
            if(node.type() == xml::Node::StartElement)
            {
                const xml::StartElement& se = static_cast<const xml::StartElement&>(node);
                if( se.name() != L"methodResponse" )
                    throw SerializationError("invalid XML-RPC methodCall");

                _state = OnMethodResponseBegin;
            }

            break;
        }

        case OnMethodResponseBegin:
        {
            if(node.type() == xml::Node::StartElement) // <params> or <fault>
            {
                const xml::StartElement& se = static_cast<const xml::StartElement&>(node);
                if( se.name() == L"params")
                {
                    _state = OnParamsBegin;
                    break;
                }

                else if( se.name() == L"fault")
                {
                    _fh.begin(_fault);
                    _scanner.begin(_fh, _context);
                    _state = OnFaultBegin;
                    break;
                }

                throw SerializationError("invalid XML-RPC methodCall");
            }
            break;
        }

        case OnFaultBegin:
        {
            bool finished = _scanner.advance(node); // start with <value>
            if(finished)
            {
                // </fault>
                _state = OnFaultEnd;
            }

            break;
        }

        case OnFaultEnd:
        {
            if(node.type() == xml::Node::EndElement) // </methodResponse>
            {
                const xml::EndElement& ee = static_cast<const xml::EndElement&>(node);
                if( ee.name() != L"methodResponse" )
                    throw SerializationError("invalid XML-RPC methodCall");

                _method->setFault(_fault.rc(), _fault.text());

                _state = OnFaultResponseEnd;
            }
            break;
        }

        case OnFaultResponseEnd:
        {
            _state = OnFaultResponseEnd;
            break;
        }

        case OnParamsBegin:
        {
            if(node.type() == xml::Node::StartElement) // <param>
            {
                const xml::StartElement& se = static_cast<const xml::StartElement&>(node);
                if( se.name() != L"param" )
                    throw SerializationError("invalid XML-RPC methodCall");

                _state = OnParam;
            }

            break;
        }

        case OnParam:
        {
            bool finished = _scanner.advance(node); // start with <value>
            if(finished)
            {
                // </param>
                _state = OnParamEnd;
            }

            break;
        }

        case OnParamEnd:
        {
            if(node.type() == xml::Node::EndElement) // </params>
            {
                const xml::EndElement& ee = static_cast<const xml::EndElement&>(node);
                if( ee.name() != L"params" )
                    throw SerializationError("invalid XML-RPC methodCall");

                _state = OnParamsEnd;
            }
            break;
        }

        case OnParamsEnd:
        {
            if(node.type() == xml::Node::EndElement) // </methodResponse>
            {
                const xml::EndElement& ee = static_cast<const xml::EndElement&>(node);
                if( ee.name() != L"methodResponse" )
                    throw SerializationError("invalid XML-RPC methodCall");

                _state = OnMethodResponseEnd;
            }
            break;
        }

        case OnMethodResponseEnd:
        {
            _state = OnMethodResponseEnd;
            break;
        }
    }
}

}

}
<|endoftext|>


<commit_before>/* Copyright (C) 2018 INRA
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef ORG_VLEPROJECT_BARYONYX_SOLVER_SPARSE_MATRIX_HPP
#define ORG_VLEPROJECT_BARYONYX_SOLVER_SPARSE_MATRIX_HPP

#include <baryonyx/core>

#include <algorithm>
#include <memory>
#include <string>

#include "debug.hpp"
#include "fixed-array.hpp"

namespace baryonyx {

template<typename Index>
class sparse_matrix
{
public:
    using size_type = std::size_t;
    using index_type = Index;

    struct col_value
    {
        col_value() = default;

        col_value(int value_, int row_)
          : value(value_)
          , row(row_)
        {}

        int value = -1;
        int row = -1;
    };

    struct row_value
    {
        row_value() = default;

        row_value(int value_, int column_)
          : value(value_)
          , column(column_)
        {}

        int value = -1;
        int column = -1;
    };

protected:
    using vector_access = fixed_array<int>;
    using row_vector_access = fixed_array<row_value>;
    using col_vector_access = fixed_array<col_value>;

    vector_access m_rows_access;
    vector_access m_cols_access;
    row_vector_access m_rows_values;
    col_vector_access m_cols_values;

public:
    using row_iterator = typename row_vector_access::iterator;
    using col_iterator = typename col_vector_access::iterator;
    using const_row_iterator = typename row_vector_access::const_iterator;
    using const_col_iterator = typename col_vector_access::const_iterator;

    template<typename Constraint>
    explicit sparse_matrix(const std::vector<Constraint>& csts,
                           const int rows,
                           const int cols)
      : m_rows_access(rows + 1)
      , m_cols_access(cols + 1)
    {
        int elem = 0;

        for (int i = 0, e = rows; i != e; ++i)
            elem += static_cast<int>(csts[i].elements.size());

        row_vector_access(elem).swap(m_rows_values);
        col_vector_access(elem).swap(m_cols_values);

        //
        // First, we build a vector of access to all elements in the matrix
        // different to 0. Then, we sort accessors according to the column id
        // then we assign a unique identifier for each accessors and then we
        // sort accessors according to the row id. This code enable the
        // construction of the r_rows_values with pointers to the
        // r_cols_values.
        //

        struct access
        {
            access() = default;

            access(int row_, int col_, function_element fct_)
              : row(row_)
              , col(col_)
              , fct(fct_)
            {}

            int row;
            int col;
            int id;
            function_element fct;
        };

        std::vector<access> accessors;
        accessors.reserve(elem);
        for (int i = 0; i != rows; ++i)
            for (const auto& fct_elem : csts[i].elements)
                accessors.emplace_back(i, fct_elem.variable_index, fct_elem);

        // std::stable_sort(
        //   accessors.begin(),
        //   accessors.end(),
        //   [](const auto& lhs, const auto& rhs) { return lhs.row < rhs.row;
        //   });

        fixed_array<int> rinit(rows, 0);
        int row = accessors[0].row;
        int nb_row = 0;
        for (int i = 0; i != elem; ++i) {
            if (row != accessors[i].row) {
                rinit[row] = nb_row;
                nb_row = 0;
                row = accessors[i].row;
            }
            ++nb_row;

            accessors[i].id = i;
            m_rows_values[i] = { i, accessors[i].col };
        }

        rinit[row] = nb_row; /* Be sure to affect the good number of elements
                                for this constaints. */

        for (int i = 0; i != rows; ++i)
            bx_assert(rinit[i] == static_cast<int>(csts[i].elements.size()));

        std::stable_sort(
          accessors.begin(),
          accessors.end(),
          [](const auto& lhs, const auto& rhs) { return lhs.col < rhs.col; });

        fixed_array<int> cinit(cols, 0);
        int col = accessors[0].col;
        int nb_col = 0;

        for (int i = 0; i != elem; ++i) {
            if (col != accessors[i].col) {
                cinit[col] = nb_col;
                nb_col = 0;
                col = accessors[i].col;
            }
            ++nb_col;

            m_cols_values[i] = { m_rows_values[accessors[i].id].value,
                                 accessors[i].row };
        }

        cinit[col] = nb_col; /* Be sure to affect the good number of elements
                                for this constaints. */

        m_rows_access[0] = 0;
        for (int i = 0; i != rows; ++i)
            m_rows_access[i + 1] = m_rows_access[i] + rinit[i];

        m_cols_access[0] = 0;
        for (int i = 0; i != cols; ++i)
            m_cols_access[i + 1] = m_cols_access[i] + cinit[i];
    }

    std::tuple<row_iterator, row_iterator> row(int row) noexcept
    {
        m_check_index(row, 0);

        row_iterator begin = m_rows_values.begin() + m_rows_access[row];
        row_iterator end = m_rows_values.begin() + m_rows_access[row + 1];

        return std::make_tuple(begin, end);
    }

    std::tuple<col_iterator, col_iterator> column(int col) noexcept
    {
        m_check_index(0, col);

        col_iterator begin = m_cols_values.begin() + m_cols_access[col];
        col_iterator end = m_cols_values.begin() + m_cols_access[col + 1];

        return std::make_tuple(begin, end);
    }

    std::tuple<const_row_iterator, const_row_iterator> row(int row) const
      noexcept
    {
        m_check_index(row, 0);

        const_row_iterator begin = m_rows_values.begin() + m_rows_access[row];
        const_row_iterator end =
          m_rows_values.begin() + m_rows_access[row + 1];

        return std::make_tuple(begin, end);
    }

    std::tuple<const_col_iterator, const_col_iterator> column(int col) const
      noexcept
    {
        m_check_index(0, col);

        const_col_iterator begin = m_cols_values.begin() + m_cols_access[col];
        const_col_iterator end =
          m_cols_values.begin() + m_cols_access[col + 1];

        return std::make_tuple(begin, end);
    }

    size_type size() const noexcept
    {
        return m_rows_values.size();
    }

    int length() const noexcept
    {
        return static_cast<int>(size());
    }

private:
    void m_check_index(index_type row, index_type col) const noexcept
    {
        bx_expects(col >= 0 &&
                   static_cast<size_type>(col) < (m_cols_access.size() - 1));

        bx_expects(row >= 0 &&
                   static_cast<size_type>(row) < (m_rows_access.size() - 1));
    }
};
}

#endif
<commit_msg>sparse-matrix: remove the memory friendly access<commit_after>/* Copyright (C) 2018 INRA
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef ORG_VLEPROJECT_BARYONYX_SOLVER_SPARSE_MATRIX_HPP
#define ORG_VLEPROJECT_BARYONYX_SOLVER_SPARSE_MATRIX_HPP

#include <baryonyx/core>

#include <algorithm>
#include <memory>
#include <string>

#include "debug.hpp"
#include "fixed-array.hpp"

namespace baryonyx {

template<typename Index>
class sparse_matrix
{
public:
    using size_type = std::size_t;
    using index_type = Index;

    struct col_value
    {
        col_value() = default;

        col_value(int value_, int row_)
          : value(value_)
          , row(row_)
        {}

        int value = -1;
        int row = -1;
    };

    struct row_value
    {
        row_value() = default;

        row_value(int value_, int column_)
          : value(value_)
          , column(column_)
        {}

        int value = -1;
        int column = -1;
    };

protected:
    using vector_access = fixed_array<int>;
    using row_vector_access = fixed_array<row_value>;
    using col_vector_access = fixed_array<col_value>;

    vector_access m_rows_access;
    vector_access m_cols_access;
    row_vector_access m_rows_values;
    col_vector_access m_cols_values;

public:
    using row_iterator = typename row_vector_access::iterator;
    using col_iterator = typename col_vector_access::iterator;
    using const_row_iterator = typename row_vector_access::const_iterator;
    using const_col_iterator = typename col_vector_access::const_iterator;

    template<typename Constraint>
    explicit sparse_matrix(const std::vector<Constraint>& csts,
                           const int rows,
                           const int cols)
      : m_rows_access(rows + 1)
      , m_cols_access(cols + 1)
    {
        int elem = 0;

        for (int i = 0, e = rows; i != e; ++i)
            elem += static_cast<int>(csts[i].elements.size());

        row_vector_access(elem).swap(m_rows_values);
        col_vector_access(elem).swap(m_cols_values);

        //
        // First, we build a vector of access to all elements in the matrix
        // different to 0. Then, we sort accessors according to the column id
        // then we assign a unique identifier for each accessors and then we
        // sort accessors according to the row id. This code enable the
        // construction of the r_rows_values with pointers to the
        // r_cols_values.
        //

        struct access
        {
            access() = default;

            access(int row_, int col_, function_element fct_)
              : row(row_)
              , col(col_)
              , fct(fct_)
            {}

            int row;
            int col;
            int id;
            function_element fct;
        };

        std::vector<access> accessors;
        accessors.reserve(elem);
        for (int i = 0; i != rows; ++i)
            for (const auto& fct_elem : csts[i].elements)
                accessors.emplace_back(i, fct_elem.variable_index, fct_elem);

        fixed_array<int> rinit(rows, 0);
        int row = accessors[0].row;
        int nb_row = 0;
        for (int i = 0; i != elem; ++i) {
            if (row != accessors[i].row) {
                rinit[row] = nb_row;
                nb_row = 0;
                row = accessors[i].row;
            }
            ++nb_row;

            accessors[i].id = i;
            m_rows_values[i] = { i, accessors[i].col };
        }

        rinit[row] = nb_row; /* Be sure to affect the good number of elements
                                for this constaints. */

        for (int i = 0; i != rows; ++i)
            bx_assert(rinit[i] == static_cast<int>(csts[i].elements.size()));

        // We sort the accessors to improve memory friendly access.

        std::stable_sort(
          accessors.begin(),
          accessors.end(),
          [](const auto& lhs, const auto& rhs) { return lhs.col < rhs.col; });

        fixed_array<int> cinit(cols, 0);
        int col = accessors[0].col;
        int nb_col = 0;

        for (int i = 0; i != elem; ++i) {
            if (col != accessors[i].col) {
                cinit[col] = nb_col;
                nb_col = 0;
                col = accessors[i].col;
            }
            ++nb_col;

            m_cols_values[i] = { m_rows_values[accessors[i].id].value,
                                 accessors[i].row };
        }

        cinit[col] = nb_col; /* Be sure to affect the good number of elements
                                for this constaints. */

        m_rows_access[0] = 0;
        for (int i = 0; i != rows; ++i)
            m_rows_access[i + 1] = m_rows_access[i] + rinit[i];

        m_cols_access[0] = 0;
        for (int i = 0; i != cols; ++i)
            m_cols_access[i + 1] = m_cols_access[i] + cinit[i];
    }

    std::tuple<row_iterator, row_iterator> row(int row) noexcept
    {
        m_check_index(row, 0);

        row_iterator begin = m_rows_values.begin() + m_rows_access[row];
        row_iterator end = m_rows_values.begin() + m_rows_access[row + 1];

        return std::make_tuple(begin, end);
    }

    std::tuple<col_iterator, col_iterator> column(int col) noexcept
    {
        m_check_index(0, col);

        col_iterator begin = m_cols_values.begin() + m_cols_access[col];
        col_iterator end = m_cols_values.begin() + m_cols_access[col + 1];

        return std::make_tuple(begin, end);
    }

    std::tuple<const_row_iterator, const_row_iterator> row(int row) const
      noexcept
    {
        m_check_index(row, 0);

        const_row_iterator begin = m_rows_values.begin() + m_rows_access[row];
        const_row_iterator end =
          m_rows_values.begin() + m_rows_access[row + 1];

        return std::make_tuple(begin, end);
    }

    std::tuple<const_col_iterator, const_col_iterator> column(int col) const
      noexcept
    {
        m_check_index(0, col);

        const_col_iterator begin = m_cols_values.begin() + m_cols_access[col];
        const_col_iterator end =
          m_cols_values.begin() + m_cols_access[col + 1];

        return std::make_tuple(begin, end);
    }

    size_type size() const noexcept
    {
        return m_rows_values.size();
    }

    int length() const noexcept
    {
        return static_cast<int>(size());
    }

private:
    void m_check_index(index_type row, index_type col) const noexcept
    {
        bx_expects(col >= 0 &&
                   static_cast<size_type>(col) < (m_cols_access.size() - 1));

        bx_expects(row >= 0 &&
                   static_cast<size_type>(row) < (m_rows_access.size() - 1));
    }
};
}

#endif
<|endoftext|>


<commit_before>//Copyright (c) 2021 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "sliceDataStorage.h"
#include "TreeModelVolumes.h"

namespace cura
{

TreeModelVolumes::TreeModelVolumes(const SliceDataStorage& storage, const Settings& settings)
    : machine_border_(calculateMachineBorderCollision(storage.getMachineBorder()))
    , xy_distance_(settings.get<coord_t>("support_xy_distance"))
    , xy_distance_overhang(settings.get<coord_t>("support_xy_distance_overhang"))
    , distance_priority(settings.get<SupportDistPriority>("support_xy_overrides_z"))
    , radius_sample_resolution_(settings.get<coord_t>("support_tree_collision_resolution"))
{
    const coord_t layer_height = settings.get<coord_t>("layer_height");
    const AngleRadians angle = settings.get<AngleRadians>("support_tree_angle");
    max_move_ = (angle < TAU / 4) ? (coord_t)(tan(angle) * layer_height) : std::numeric_limits<coord_t>::max();
    z_distance_layers = round_up_divide(settings.get<coord_t>("support_top_distance"), layer_height) + 1;
    for (std::size_t layer_idx  = 0; layer_idx < storage.support.supportLayers.size(); ++layer_idx)
    {
        constexpr bool include_support = false;
        constexpr bool include_prime_tower = true;
        layer_outlines_.push_back(storage.getLayerOutlines(layer_idx, include_support, include_prime_tower));
    }
}

const Polygons& TreeModelVolumes::getCollision(coord_t radius, LayerIndex layer_idx) const
{
    radius = ceilRadius(radius);
    RadiusLayerPair key{radius, layer_idx};
    const auto it = collision_cache_.find(key);
    if (it != collision_cache_.end())
    {
        return it->second;
    }
    else
    {
        return calculateCollision(key);
    }
}

const Polygons& TreeModelVolumes::getAvoidance(coord_t radius, LayerIndex layer_idx) const
{
    radius = ceilRadius(radius);
    RadiusLayerPair key{radius, layer_idx};
    const auto it = avoidance_cache_.find(key);
    if (it != avoidance_cache_.end())
    {
        return it->second;
    }
    else
    {
        return calculateAvoidance(key);
    }
}

const Polygons& TreeModelVolumes::getInternalModel(coord_t radius, LayerIndex layer_idx) const
{
    radius = ceilRadius(radius);
    RadiusLayerPair key{radius, layer_idx};
    const auto it = internal_model_cache_.find(key);
    if (it != internal_model_cache_.end())
    {
        return it->second;
    }
    else
    {
        return calculateInternalModel(key);
    }
}

coord_t TreeModelVolumes::ceilRadius(coord_t radius) const
{
    const auto remainder = radius % radius_sample_resolution_;
    const auto delta = remainder != 0 ? radius_sample_resolution_- remainder : 0;
    return radius + delta;
}

const Polygons& TreeModelVolumes::calculateCollision(const RadiusLayerPair& key) const
{
    const coord_t radius = key.first;
    const LayerIndex layer_idx = key.second;

    Polygons collision_areas = machine_border_;
    if(layer_idx < static_cast<LayerIndex>(layer_outlines_.size()))
    {
        if(distance_priority == SupportDistPriority::XY_OVERRIDES_Z)
        {
            //If X/Y overrides Z, simply use the X/Y distance as distance to keep away from the model.
            collision_areas = collision_areas.unionPolygons(layer_outlines_[layer_idx].offset(xy_distance_ + radius, ClipperLib::JoinType::jtRound));
        }
        else if(layer_idx + z_distance_layers < static_cast<LayerIndex>(layer_outlines_.size()))
        {
            //If Z overrides X/Y, use X/Y distance if the surface is vertical, or Min X/Y distance if not.
            Polygons z_influenced = layer_outlines_[layer_idx + z_distance_layers].difference(layer_outlines_[layer_idx]).offset(xy_distance_); //In-between layers are ignored for performance.
            Polygons collision_not_overhang = layer_outlines_[layer_idx].offset(xy_distance_); //In places where there is no overhang nearby, use the normal X/Y distance.
            if(!z_influenced.empty())
            {
                collision_not_overhang = collision_not_overhang.difference(z_influenced);
            }
            Polygons collision_model = collision_not_overhang.unionPolygons(layer_outlines_[layer_idx].offset(xy_distance_overhang)); //Apply the minimum distance everywhere else.
            collision_areas = collision_areas.unionPolygons(collision_model.offset(radius));
        }
    }
    const auto ret = collision_cache_.insert({key, std::move(collision_areas)});
    assert(ret.second);
    return ret.first->second;
}

const Polygons& TreeModelVolumes::calculateAvoidance(const RadiusLayerPair& key) const
{
    const auto& radius = key.first;
    const auto& layer_idx = key.second;

    if (layer_idx == 0)
    {
        avoidance_cache_[key] = getCollision(radius, 0);
        return avoidance_cache_[key];
    }

    // Avoidance for a given layer depends on all layers beneath it so could have very deep recursion depths if
    // called at high layer heights. We can limit the reqursion depth to N by checking if the if the layer N
    // below the current one exists and if not, forcing the calculation of that layer. This may cause another recursion
    // if the layer at 2N below the current one but we won't exceed our limit unless there are N*N uncalculated layers
    // below our current one.
    constexpr auto max_recursion_depth = 100;
    // Check if we would exceed the recursion limit by trying to process this layer
    if (layer_idx >= max_recursion_depth
        && avoidance_cache_.find({radius, layer_idx - max_recursion_depth}) == avoidance_cache_.end())
    {
        // Force the calculation of the layer `max_recursion_depth` below our current one, ignoring the result.
        getAvoidance(radius, layer_idx - max_recursion_depth);
    }
    auto avoidance_areas = getAvoidance(radius, layer_idx - 1).offset(-max_move_).smooth(5);
    avoidance_areas = avoidance_areas.unionPolygons(getCollision(radius, layer_idx));
    const auto ret = avoidance_cache_.insert({key, std::move(avoidance_areas)});
    assert(ret.second);
    return ret.first->second;
}

const Polygons& TreeModelVolumes::calculateInternalModel(const RadiusLayerPair& key) const
{
    const auto& radius = key.first;
    const auto& layer_idx = key.second;

    const auto& internal_areas = getAvoidance(radius, layer_idx).difference(getCollision(radius, layer_idx));
    const auto ret = internal_model_cache_.insert({key, internal_areas});
    assert(ret.second);
    return ret.first->second;
}

Polygons TreeModelVolumes::calculateMachineBorderCollision(Polygon machine_border)
{
    Polygons machine_volume_border;
    machine_volume_border.add(machine_border.offset(MM2INT(1000))); //Put a border of 1m around the print volume so that we don't collide.
    machine_border.reverse(); //Makes the polygon negative so that we subtract the actual volume from the collision area.
    machine_volume_border.add(machine_border);
    return machine_volume_border;
}

}<commit_msg>No unnecessary round join types<commit_after>//Copyright (c) 2021 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "sliceDataStorage.h"
#include "TreeModelVolumes.h"

namespace cura
{

TreeModelVolumes::TreeModelVolumes(const SliceDataStorage& storage, const Settings& settings)
    : machine_border_(calculateMachineBorderCollision(storage.getMachineBorder()))
    , xy_distance_(settings.get<coord_t>("support_xy_distance"))
    , xy_distance_overhang(settings.get<coord_t>("support_xy_distance_overhang"))
    , distance_priority(settings.get<SupportDistPriority>("support_xy_overrides_z"))
    , radius_sample_resolution_(settings.get<coord_t>("support_tree_collision_resolution"))
{
    const coord_t layer_height = settings.get<coord_t>("layer_height");
    const AngleRadians angle = settings.get<AngleRadians>("support_tree_angle");
    max_move_ = (angle < TAU / 4) ? (coord_t)(tan(angle) * layer_height) : std::numeric_limits<coord_t>::max();
    z_distance_layers = round_up_divide(settings.get<coord_t>("support_top_distance"), layer_height) + 1;
    for (std::size_t layer_idx  = 0; layer_idx < storage.support.supportLayers.size(); ++layer_idx)
    {
        constexpr bool include_support = false;
        constexpr bool include_prime_tower = true;
        layer_outlines_.push_back(storage.getLayerOutlines(layer_idx, include_support, include_prime_tower));
    }
}

const Polygons& TreeModelVolumes::getCollision(coord_t radius, LayerIndex layer_idx) const
{
    radius = ceilRadius(radius);
    RadiusLayerPair key{radius, layer_idx};
    const auto it = collision_cache_.find(key);
    if (it != collision_cache_.end())
    {
        return it->second;
    }
    else
    {
        return calculateCollision(key);
    }
}

const Polygons& TreeModelVolumes::getAvoidance(coord_t radius, LayerIndex layer_idx) const
{
    radius = ceilRadius(radius);
    RadiusLayerPair key{radius, layer_idx};
    const auto it = avoidance_cache_.find(key);
    if (it != avoidance_cache_.end())
    {
        return it->second;
    }
    else
    {
        return calculateAvoidance(key);
    }
}

const Polygons& TreeModelVolumes::getInternalModel(coord_t radius, LayerIndex layer_idx) const
{
    radius = ceilRadius(radius);
    RadiusLayerPair key{radius, layer_idx};
    const auto it = internal_model_cache_.find(key);
    if (it != internal_model_cache_.end())
    {
        return it->second;
    }
    else
    {
        return calculateInternalModel(key);
    }
}

coord_t TreeModelVolumes::ceilRadius(coord_t radius) const
{
    const auto remainder = radius % radius_sample_resolution_;
    const auto delta = remainder != 0 ? radius_sample_resolution_- remainder : 0;
    return radius + delta;
}

const Polygons& TreeModelVolumes::calculateCollision(const RadiusLayerPair& key) const
{
    const coord_t radius = key.first;
    const LayerIndex layer_idx = key.second;

    Polygons collision_areas = machine_border_;
    if(layer_idx < static_cast<LayerIndex>(layer_outlines_.size()))
    {
        if(distance_priority == SupportDistPriority::XY_OVERRIDES_Z)
        {
            //If X/Y overrides Z, simply use the X/Y distance as distance to keep away from the model.
            collision_areas = collision_areas.unionPolygons(layer_outlines_[layer_idx].offset(xy_distance_ + radius));
        }
        else if(layer_idx + z_distance_layers < static_cast<LayerIndex>(layer_outlines_.size()))
        {
            //If Z overrides X/Y, use X/Y distance if the surface is vertical, or Min X/Y distance if not.
            Polygons z_influenced = layer_outlines_[layer_idx + z_distance_layers].difference(layer_outlines_[layer_idx]).offset(xy_distance_); //In-between layers are ignored for performance.
            Polygons collision_not_overhang = layer_outlines_[layer_idx].offset(xy_distance_); //In places where there is no overhang nearby, use the normal X/Y distance.
            if(!z_influenced.empty())
            {
                collision_not_overhang = collision_not_overhang.difference(z_influenced);
            }
            Polygons collision_model = collision_not_overhang.unionPolygons(layer_outlines_[layer_idx].offset(xy_distance_overhang)); //Apply the minimum distance everywhere else.
            collision_areas = collision_areas.unionPolygons(collision_model.offset(radius));
        }
    }
    const auto ret = collision_cache_.insert({key, std::move(collision_areas)});
    assert(ret.second);
    return ret.first->second;
}

const Polygons& TreeModelVolumes::calculateAvoidance(const RadiusLayerPair& key) const
{
    const auto& radius = key.first;
    const auto& layer_idx = key.second;

    if (layer_idx == 0)
    {
        avoidance_cache_[key] = getCollision(radius, 0);
        return avoidance_cache_[key];
    }

    // Avoidance for a given layer depends on all layers beneath it so could have very deep recursion depths if
    // called at high layer heights. We can limit the reqursion depth to N by checking if the if the layer N
    // below the current one exists and if not, forcing the calculation of that layer. This may cause another recursion
    // if the layer at 2N below the current one but we won't exceed our limit unless there are N*N uncalculated layers
    // below our current one.
    constexpr auto max_recursion_depth = 100;
    // Check if we would exceed the recursion limit by trying to process this layer
    if (layer_idx >= max_recursion_depth
        && avoidance_cache_.find({radius, layer_idx - max_recursion_depth}) == avoidance_cache_.end())
    {
        // Force the calculation of the layer `max_recursion_depth` below our current one, ignoring the result.
        getAvoidance(radius, layer_idx - max_recursion_depth);
    }
    auto avoidance_areas = getAvoidance(radius, layer_idx - 1).offset(-max_move_).smooth(5);
    avoidance_areas = avoidance_areas.unionPolygons(getCollision(radius, layer_idx));
    const auto ret = avoidance_cache_.insert({key, std::move(avoidance_areas)});
    assert(ret.second);
    return ret.first->second;
}

const Polygons& TreeModelVolumes::calculateInternalModel(const RadiusLayerPair& key) const
{
    const auto& radius = key.first;
    const auto& layer_idx = key.second;

    const auto& internal_areas = getAvoidance(radius, layer_idx).difference(getCollision(radius, layer_idx));
    const auto ret = internal_model_cache_.insert({key, internal_areas});
    assert(ret.second);
    return ret.first->second;
}

Polygons TreeModelVolumes::calculateMachineBorderCollision(Polygon machine_border)
{
    Polygons machine_volume_border;
    machine_volume_border.add(machine_border.offset(MM2INT(1000))); //Put a border of 1m around the print volume so that we don't collide.
    machine_border.reverse(); //Makes the polygon negative so that we subtract the actual volume from the collision area.
    machine_volume_border.add(machine_border);
    return machine_volume_border;
}

}<|endoftext|>


<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
#if !defined(FUNCTIONKEY_HEADER_GUARD_1357924680)
#define FUNCTIONKEY_HEADER_GUARD_1357924680

// Base header file.  Must be first.
#include <XSLT/XSLTDefinitions.hpp>

#include <vector>

#include <XPath/Function.hpp>

class DOM_Node;
class XObject;
class XPathExecutionContext;

// Implementation of the XSLT function key().
//
class XALAN_XSLT_EXPORT FunctionKey : public Function
{
public:

#if defined(XALAN_NO_NAMESPACES)
	typedef vector<XObject*> XObjectPtrVectorType;
#else
	typedef std::vector<XObject*> XObjectPtrVectorType;
#endif

	FunctionKey();

	virtual
	~FunctionKey();

	// These methods are inherited from XPath/Function ...

	virtual XObject*
	execute(
			XPathExecutionContext&			executionContext,
			XalanNode*						context,
			int								opPos,
			const XObjectArgVectorType&		args);

#if defined(XALAN_NO_COVARIANT_RETURN_TYPE)
	virtual Function*
#else
	virtual FunctionKey*
#endif
	clone() const;

private:

	// Not implemented...
	FunctionKey&
	operator=(const FunctionKey&);

	bool
	operator==(const FunctionKey&) const;
};

#endif	// FUNCTIONKEY_HEADER_GUARD_1357924680
<commit_msg>Removed redundant typedef.<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
#if !defined(FUNCTIONKEY_HEADER_GUARD_1357924680)
#define FUNCTIONKEY_HEADER_GUARD_1357924680

// Base header file.  Must be first.
#include <XSLT/XSLTDefinitions.hpp>

#include <vector>

#include <XPath/Function.hpp>

class XObject;
class XPathExecutionContext;

// Implementation of the XSLT function key().
//
class XALAN_XSLT_EXPORT FunctionKey : public Function
{
public:

	FunctionKey();

	virtual
	~FunctionKey();

	// These methods are inherited from XPath/Function ...

	virtual XObject*
	execute(
			XPathExecutionContext&			executionContext,
			XalanNode*						context,
			int								opPos,
			const XObjectArgVectorType&		args);

#if defined(XALAN_NO_COVARIANT_RETURN_TYPE)
	virtual Function*
#else
	virtual FunctionKey*
#endif
	clone() const;

private:

	// Not implemented...
	FunctionKey&
	operator=(const FunctionKey&);

	bool
	operator==(const FunctionKey&) const;
};

#endif	// FUNCTIONKEY_HEADER_GUARD_1357924680
<|endoftext|>