<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2013-2015 Inviwo 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 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.
 *
 *********************************************************************************/

#include <modules/python3/pythonincluder.h>

#include "pyproperties.h"

#include <inviwo/core/common/inviwoapplication.h>
#include <inviwo/core/processors/processor.h>
#include <inviwo/core/network/processornetwork.h>

#include <inviwo/core/properties/optionproperty.h>
#include <inviwo/core/properties/boolproperty.h>
#include <inviwo/core/properties/buttonproperty.h>
#include <inviwo/core/properties/cameraproperty.h>
#include <inviwo/core/properties/directoryproperty.h>
#include <inviwo/core/properties/fileproperty.h>
#include <inviwo/core/properties/optionproperty.h>
#include <inviwo/core/properties/ordinalproperty.h>

#include <inviwo/core/properties/stringproperty.h>
#include <inviwo/core/properties/transferfunctionproperty.h>
#include <modules/python3/pythoninterface/pyvalueparser.h>

namespace inviwo {

PyObject* py_setPropertyValue(PyObject* self, PyObject* args) {
    static PySetPropertyValueMethod p;

    if (!p.testParams(args)) return nullptr;
    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));
    PyObject* parameter = PyTuple_GetItem(args, 1);

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    PyValueParser::setProperty(theProperty, parameter);
    Py_RETURN_NONE;
}

PyObject* py_setPropertyMaxValue(PyObject* /*self*/, PyObject* args) {
    static PySetPropertyMaxValueMethod p;

    if (!p.testParams(args)) return nullptr;

    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));
    PyObject* parameter = PyTuple_GetItem(args, 1);

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    OrdinalProperty<float>* ordinalFloat = dynamic_cast<OrdinalProperty<float>*>(theProperty);
    OrdinalProperty<int>* ordinalInt = dynamic_cast<OrdinalProperty<int>*>(theProperty);
    OrdinalProperty<ivec2>* ordinalIvec2 = dynamic_cast<OrdinalProperty<ivec2>*>(theProperty);
    OrdinalProperty<ivec3>* ordinalIvec3 = dynamic_cast<OrdinalProperty<ivec3>*>(theProperty);
    OrdinalProperty<ivec4>* ordinalIvec4 = dynamic_cast<OrdinalProperty<ivec4>*>(theProperty);
    OrdinalProperty<mat2>* ordinalMat2 = dynamic_cast<OrdinalProperty<mat2>*>(theProperty);
    OrdinalProperty<mat3>* ordinalMat3 = dynamic_cast<OrdinalProperty<mat3>*>(theProperty);
    OrdinalProperty<mat4>* ordinalMat4 = dynamic_cast<OrdinalProperty<mat4>*>(theProperty);
    OrdinalProperty<vec2>* ordinalVec2 = dynamic_cast<OrdinalProperty<vec2>*>(theProperty);
    OrdinalProperty<vec3>* ordinalVec3 = dynamic_cast<OrdinalProperty<vec3>*>(theProperty);
    OrdinalProperty<vec4>* ordinalVec4 = dynamic_cast<OrdinalProperty<vec4>*>(theProperty);

    if (ordinalFloat) {
        ordinalFloat->setMaxValue(PyValueParser::parse<float>(parameter));
    } else if (ordinalInt) {
        ordinalInt->setMaxValue(PyValueParser::parse<int>(parameter));
    } else if (ordinalIvec2) {
        ordinalIvec2->setMaxValue(PyValueParser::parse<ivec2>(parameter));
    } else if (ordinalIvec3) {
        ordinalIvec3->setMaxValue(PyValueParser::parse<ivec3>(parameter));
    } else if (ordinalIvec4) {
        ordinalIvec4->setMaxValue(PyValueParser::parse<ivec4>(parameter));
    } else if (ordinalMat2) {
        ordinalMat2->setMaxValue(PyValueParser::parse<mat2>(parameter));
    } else if (ordinalMat3) {
        ordinalMat3->setMaxValue(PyValueParser::parse<mat3>(parameter));
    } else if (ordinalMat4) {
        ordinalMat4->setMaxValue(PyValueParser::parse<mat4>(parameter));
    } else if (ordinalVec2) {
        ordinalVec2->setMaxValue(PyValueParser::parse<vec2>(parameter));
    } else if (ordinalVec3) {
        ordinalVec3->setMaxValue(PyValueParser::parse<vec3>(parameter));
    } else if (ordinalVec4) {
        ordinalVec4->setMaxValue(PyValueParser::parse<vec4>(parameter));
    } else {
        LogErrorCustom("inviwo_setPropertyMaxValue",
                       "Unknown parameter type: " << theProperty->getClassIdentifier());
    }

    Py_RETURN_NONE;
}

PyObject* py_setPropertyMinValue(PyObject* /*self*/, PyObject* args) {
    static PySetPropertyMinValueMethod p;

    if (!p.testParams(args)) return nullptr;

    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));
    PyObject* parameter = PyTuple_GetItem(args, 1);

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    OrdinalProperty<float>* ordinalFloat = dynamic_cast<OrdinalProperty<float>*>(theProperty);
    OrdinalProperty<int>* ordinalInt = dynamic_cast<OrdinalProperty<int>*>(theProperty);
    OrdinalProperty<ivec2>* ordinalIvec2 = dynamic_cast<OrdinalProperty<ivec2>*>(theProperty);
    OrdinalProperty<ivec3>* ordinalIvec3 = dynamic_cast<OrdinalProperty<ivec3>*>(theProperty);
    OrdinalProperty<ivec4>* ordinalIvec4 = dynamic_cast<OrdinalProperty<ivec4>*>(theProperty);
    OrdinalProperty<mat2>* ordinalMat2 = dynamic_cast<OrdinalProperty<mat2>*>(theProperty);
    OrdinalProperty<mat3>* ordinalMat3 = dynamic_cast<OrdinalProperty<mat3>*>(theProperty);
    OrdinalProperty<mat4>* ordinalMat4 = dynamic_cast<OrdinalProperty<mat4>*>(theProperty);
    OrdinalProperty<vec2>* ordinalVec2 = dynamic_cast<OrdinalProperty<vec2>*>(theProperty);
    OrdinalProperty<vec3>* ordinalVec3 = dynamic_cast<OrdinalProperty<vec3>*>(theProperty);
    OrdinalProperty<vec4>* ordinalVec4 = dynamic_cast<OrdinalProperty<vec4>*>(theProperty);

    if (ordinalFloat) {
        ordinalFloat->setMinValue(PyValueParser::parse<float>(parameter));
    } else if (ordinalInt) {
        ordinalInt->setMinValue(PyValueParser::parse<int>(parameter));
    } else if (ordinalIvec2) {
        ordinalIvec2->setMinValue(PyValueParser::parse<ivec2>(parameter));
    } else if (ordinalIvec3) {
        ordinalIvec3->setMinValue(PyValueParser::parse<ivec3>(parameter));
    } else if (ordinalIvec4) {
        ordinalIvec4->setMinValue(PyValueParser::parse<ivec4>(parameter));
    } else if (ordinalMat2) {
        ordinalMat2->setMinValue(PyValueParser::parse<mat2>(parameter));
    } else if (ordinalMat3) {
        ordinalMat3->setMinValue(PyValueParser::parse<mat3>(parameter));
    } else if (ordinalMat4) {
        ordinalMat4->setMinValue(PyValueParser::parse<mat4>(parameter));
    } else if (ordinalVec2) {
        ordinalVec2->setMinValue(PyValueParser::parse<vec2>(parameter));
    } else if (ordinalVec3) {
        ordinalVec3->setMinValue(PyValueParser::parse<vec3>(parameter));
    } else if (ordinalVec4) {
        ordinalVec4->setMinValue(PyValueParser::parse<vec4>(parameter));
    } else {
        LogErrorCustom("inviwo_setPropertyMinValue", "Unknown parameter type");
    }

    Py_RETURN_NONE;
}

PyObject* py_getPropertyValue(PyObject* /*self*/, PyObject* args) {
    static PyGetPropertyValueMethod p;

    if (!p.testParams(args)) return nullptr;

    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    return PyValueParser::getProperty(theProperty);
}

#define CAST_N_GETMAX(PropType, prop, parser)                        \
    {                                                                \
        PropType* casted = dynamic_cast<PropType*>(prop);            \
        if (casted) {                                                \
            return PyValueParser::toPyObject(casted->getMaxValue()); \
        }                                                            \
    }
#define CAST_N_GETMIN(PropType, prop, parser)                        \
    {                                                                \
        PropType* casted = dynamic_cast<PropType*>(prop);            \
        if (casted) {                                                \
            return PyValueParser::toPyObject(casted->getMinValue()); \
        }                                                            \
    }

PyObject* py_getPropertyMaxValue(PyObject* /*self*/, PyObject* args) {
    static PyGetPropertyMaxValueMethod p;

    if (!p.testParams(args)) return nullptr;

    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    CAST_N_GETMAX(FloatProperty, theProperty, parser);
    CAST_N_GETMAX(IntProperty, theProperty, parser);
    CAST_N_GETMAX(IntVec2Property, theProperty, parser);
    CAST_N_GETMAX(IntVec3Property, theProperty, parser);
    CAST_N_GETMAX(IntVec4Property, theProperty, parser);
    CAST_N_GETMAX(FloatMat2Property, theProperty, parser);
    CAST_N_GETMAX(FloatMat3Property, theProperty, parser);
    CAST_N_GETMAX(FloatMat4Property, theProperty, parser);
    CAST_N_GETMAX(FloatVec2Property, theProperty, parser);
    CAST_N_GETMAX(FloatVec3Property, theProperty, parser);
    CAST_N_GETMAX(FloatVec4Property, theProperty, parser);
    LogErrorCustom("inviwo_getPropertyMaxValue", "Unknown parameter type");
    Py_RETURN_NONE;
}

PyObject* py_getPropertyMinValue(PyObject* /*self*/, PyObject* args) {
    static PyGetPropertyMinValueMethod p;

    if (!p.testParams(args)) return nullptr;

    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    CAST_N_GETMIN(FloatProperty, theProperty, parser);
    CAST_N_GETMIN(IntProperty, theProperty, parser);
    CAST_N_GETMIN(IntVec2Property, theProperty, parser);
    CAST_N_GETMIN(IntVec3Property, theProperty, parser);
    CAST_N_GETMIN(IntVec4Property, theProperty, parser);
    CAST_N_GETMIN(FloatMat2Property, theProperty, parser);
    CAST_N_GETMIN(FloatMat3Property, theProperty, parser);
    CAST_N_GETMIN(FloatMat4Property, theProperty, parser);
    CAST_N_GETMIN(FloatVec2Property, theProperty, parser);
    CAST_N_GETMIN(FloatVec3Property, theProperty, parser);
    CAST_N_GETMIN(FloatVec4Property, theProperty, parser);
    LogErrorCustom("inviwo_getPropertyMaxValue", "Unknown parameter type");
    Py_RETURN_NONE;
}

PyObject* py_clickButton(PyObject* /*self*/, PyObject* args) {
    static PyClickButtonMethod p;

    if (!p.testParams(args)) return nullptr;

    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    ButtonProperty* button = dynamic_cast<ButtonProperty*>(theProperty);

    if (!button) {
        std::string msg = std::string("clickButton() found property is not a ButtonProperty: ") +
                          theProperty->getClassIdentifier();
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    button->pressButton();
    Py_RETURN_NONE;
}

PySetPropertyValueMethod::PySetPropertyValueMethod() : path_("path"), value_("value") {
    addParam(&path_);
    addParam(&value_);
}

PySetPropertyMaxValueMethod::PySetPropertyMaxValueMethod() : path_("path"), maxValue_("maxValue") {
    addParam(&path_);
    addParam(&maxValue_);
}

PySetPropertyMinValueMethod::PySetPropertyMinValueMethod() : path_("path"), minValue_("minValue") {
    addParam(&path_);
    addParam(&minValue_);
}

PyGetPropertyValueMethod::PyGetPropertyValueMethod() : path_("path") { addParam(&path_); }

PyGetPropertyMaxValueMethod::PyGetPropertyMaxValueMethod() : path_("path") { addParam(&path_); }

PyGetPropertyMinValueMethod::PyGetPropertyMinValueMethod() : path_("path") { addParam(&path_); }

PyClickButtonMethod::PyClickButtonMethod() : path_("path") { addParam(&path_); }
}<commit_msg>Python: Now supports double properties for get/set functions<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2013-2015 Inviwo 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 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.
 *
 *********************************************************************************/

#include <modules/python3/pythonincluder.h>

#include "pyproperties.h"

#include <inviwo/core/common/inviwoapplication.h>
#include <inviwo/core/processors/processor.h>
#include <inviwo/core/network/processornetwork.h>

#include <inviwo/core/properties/optionproperty.h>
#include <inviwo/core/properties/boolproperty.h>
#include <inviwo/core/properties/buttonproperty.h>
#include <inviwo/core/properties/cameraproperty.h>
#include <inviwo/core/properties/directoryproperty.h>
#include <inviwo/core/properties/fileproperty.h>
#include <inviwo/core/properties/optionproperty.h>
#include <inviwo/core/properties/ordinalproperty.h>

#include <inviwo/core/properties/stringproperty.h>
#include <inviwo/core/properties/transferfunctionproperty.h>
#include <modules/python3/pythoninterface/pyvalueparser.h>

namespace inviwo {

PyObject* py_setPropertyValue(PyObject* self, PyObject* args) {
    static PySetPropertyValueMethod p;

    if (!p.testParams(args)) return nullptr;
    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));
    PyObject* parameter = PyTuple_GetItem(args, 1);

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    PyValueParser::setProperty(theProperty, parameter);
    Py_RETURN_NONE;
}

PyObject* py_setPropertyMaxValue(PyObject* /*self*/, PyObject* args) {
    static PySetPropertyMaxValueMethod p;

    if (!p.testParams(args)) return nullptr;

    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));
    PyObject* parameter = PyTuple_GetItem(args, 1);

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    OrdinalProperty<float>* ordinalFloat = dynamic_cast<OrdinalProperty<float>*>(theProperty);
    OrdinalProperty<double>* ordinalDouble = dynamic_cast<OrdinalProperty<double>*>(theProperty);
    OrdinalProperty<int>* ordinalInt = dynamic_cast<OrdinalProperty<int>*>(theProperty);
    OrdinalProperty<ivec2>* ordinalIvec2 = dynamic_cast<OrdinalProperty<ivec2>*>(theProperty);
    OrdinalProperty<ivec3>* ordinalIvec3 = dynamic_cast<OrdinalProperty<ivec3>*>(theProperty);
    OrdinalProperty<ivec4>* ordinalIvec4 = dynamic_cast<OrdinalProperty<ivec4>*>(theProperty);
    OrdinalProperty<mat2>* ordinalMat2 = dynamic_cast<OrdinalProperty<mat2>*>(theProperty);
    OrdinalProperty<mat3>* ordinalMat3 = dynamic_cast<OrdinalProperty<mat3>*>(theProperty);
    OrdinalProperty<mat4>* ordinalMat4 = dynamic_cast<OrdinalProperty<mat4>*>(theProperty);
    OrdinalProperty<vec2>* ordinalVec2 = dynamic_cast<OrdinalProperty<vec2>*>(theProperty);
    OrdinalProperty<vec3>* ordinalVec3 = dynamic_cast<OrdinalProperty<vec3>*>(theProperty);
    OrdinalProperty<vec4>* ordinalVec4 = dynamic_cast<OrdinalProperty<vec4>*>(theProperty);

    if (ordinalFloat) {
        ordinalFloat->setMaxValue(PyValueParser::parse<float>(parameter));
    } else if (ordinalDouble) {
        ordinalDouble->setMaxValue(PyValueParser::parse<double>(parameter));
    } else if (ordinalInt) {
        ordinalInt->setMaxValue(PyValueParser::parse<int>(parameter));
    } else if (ordinalIvec2) {
        ordinalIvec2->setMaxValue(PyValueParser::parse<ivec2>(parameter));
    } else if (ordinalIvec3) {
        ordinalIvec3->setMaxValue(PyValueParser::parse<ivec3>(parameter));
    } else if (ordinalIvec4) {
        ordinalIvec4->setMaxValue(PyValueParser::parse<ivec4>(parameter));
    } else if (ordinalMat2) {
        ordinalMat2->setMaxValue(PyValueParser::parse<mat2>(parameter));
    } else if (ordinalMat3) {
        ordinalMat3->setMaxValue(PyValueParser::parse<mat3>(parameter));
    } else if (ordinalMat4) {
        ordinalMat4->setMaxValue(PyValueParser::parse<mat4>(parameter));
    } else if (ordinalVec2) {
        ordinalVec2->setMaxValue(PyValueParser::parse<vec2>(parameter));
    } else if (ordinalVec3) {
        ordinalVec3->setMaxValue(PyValueParser::parse<vec3>(parameter));
    } else if (ordinalVec4) {
        ordinalVec4->setMaxValue(PyValueParser::parse<vec4>(parameter));
    } else {
        LogErrorCustom("inviwo_setPropertyMaxValue",
                       "Unknown parameter type: " << theProperty->getClassIdentifier());
    }

    Py_RETURN_NONE;
}

PyObject* py_setPropertyMinValue(PyObject* /*self*/, PyObject* args) {
    static PySetPropertyMinValueMethod p;

    if (!p.testParams(args)) return nullptr;

    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));
    PyObject* parameter = PyTuple_GetItem(args, 1);

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    OrdinalProperty<float>* ordinalFloat = dynamic_cast<OrdinalProperty<float>*>(theProperty);
    OrdinalProperty<int>* ordinalInt = dynamic_cast<OrdinalProperty<int>*>(theProperty);
    OrdinalProperty<ivec2>* ordinalIvec2 = dynamic_cast<OrdinalProperty<ivec2>*>(theProperty);
    OrdinalProperty<ivec3>* ordinalIvec3 = dynamic_cast<OrdinalProperty<ivec3>*>(theProperty);
    OrdinalProperty<ivec4>* ordinalIvec4 = dynamic_cast<OrdinalProperty<ivec4>*>(theProperty);
    OrdinalProperty<mat2>* ordinalMat2 = dynamic_cast<OrdinalProperty<mat2>*>(theProperty);
    OrdinalProperty<mat3>* ordinalMat3 = dynamic_cast<OrdinalProperty<mat3>*>(theProperty);
    OrdinalProperty<mat4>* ordinalMat4 = dynamic_cast<OrdinalProperty<mat4>*>(theProperty);
    OrdinalProperty<vec2>* ordinalVec2 = dynamic_cast<OrdinalProperty<vec2>*>(theProperty);
    OrdinalProperty<vec3>* ordinalVec3 = dynamic_cast<OrdinalProperty<vec3>*>(theProperty);
    OrdinalProperty<vec4>* ordinalVec4 = dynamic_cast<OrdinalProperty<vec4>*>(theProperty);

    if (ordinalFloat) {
        ordinalFloat->setMinValue(PyValueParser::parse<float>(parameter));
    } else if (ordinalInt) {
        ordinalInt->setMinValue(PyValueParser::parse<int>(parameter));
    } else if (ordinalIvec2) {
        ordinalIvec2->setMinValue(PyValueParser::parse<ivec2>(parameter));
    } else if (ordinalIvec3) {
        ordinalIvec3->setMinValue(PyValueParser::parse<ivec3>(parameter));
    } else if (ordinalIvec4) {
        ordinalIvec4->setMinValue(PyValueParser::parse<ivec4>(parameter));
    } else if (ordinalMat2) {
        ordinalMat2->setMinValue(PyValueParser::parse<mat2>(parameter));
    } else if (ordinalMat3) {
        ordinalMat3->setMinValue(PyValueParser::parse<mat3>(parameter));
    } else if (ordinalMat4) {
        ordinalMat4->setMinValue(PyValueParser::parse<mat4>(parameter));
    } else if (ordinalVec2) {
        ordinalVec2->setMinValue(PyValueParser::parse<vec2>(parameter));
    } else if (ordinalVec3) {
        ordinalVec3->setMinValue(PyValueParser::parse<vec3>(parameter));
    } else if (ordinalVec4) {
        ordinalVec4->setMinValue(PyValueParser::parse<vec4>(parameter));
    } else {
        LogErrorCustom("inviwo_setPropertyMinValue", "Unknown parameter type");
    }

    Py_RETURN_NONE;
}

PyObject* py_getPropertyValue(PyObject* /*self*/, PyObject* args) {
    static PyGetPropertyValueMethod p;

    if (!p.testParams(args)) return nullptr;

    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    return PyValueParser::getProperty(theProperty);
}

#define CAST_N_GETMAX(PropType, prop, parser)                        \
    {                                                                \
        PropType* casted = dynamic_cast<PropType*>(prop);            \
        if (casted) {                                                \
            return PyValueParser::toPyObject(casted->getMaxValue()); \
        }                                                            \
    }
#define CAST_N_GETMIN(PropType, prop, parser)                        \
    {                                                                \
        PropType* casted = dynamic_cast<PropType*>(prop);            \
        if (casted) {                                                \
            return PyValueParser::toPyObject(casted->getMinValue()); \
        }                                                            \
    }

PyObject* py_getPropertyMaxValue(PyObject* /*self*/, PyObject* args) {
    static PyGetPropertyMaxValueMethod p;

    if (!p.testParams(args)) return nullptr;

    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    CAST_N_GETMAX(FloatProperty, theProperty, parser);
    CAST_N_GETMAX(DoubleProperty, theProperty, parser);
    CAST_N_GETMAX(IntProperty, theProperty, parser);
    CAST_N_GETMAX(IntVec2Property, theProperty, parser);
    CAST_N_GETMAX(IntVec3Property, theProperty, parser);
    CAST_N_GETMAX(IntVec4Property, theProperty, parser);
    CAST_N_GETMAX(FloatMat2Property, theProperty, parser);
    CAST_N_GETMAX(FloatMat3Property, theProperty, parser);
    CAST_N_GETMAX(FloatMat4Property, theProperty, parser);
    CAST_N_GETMAX(FloatVec2Property, theProperty, parser);
    CAST_N_GETMAX(FloatVec3Property, theProperty, parser);
    CAST_N_GETMAX(FloatVec4Property, theProperty, parser);
    LogErrorCustom("inviwo_getPropertyMaxValue", "Unknown parameter type: " << theProperty->getClassIdentifier());
    Py_RETURN_NONE;
}

PyObject* py_getPropertyMinValue(PyObject* /*self*/, PyObject* args) {
    static PyGetPropertyMinValueMethod p;

    if (!p.testParams(args)) return nullptr;

    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    CAST_N_GETMIN(FloatProperty, theProperty, parser);
    CAST_N_GETMIN(DoubleProperty, theProperty, parser);
    CAST_N_GETMIN(IntProperty, theProperty, parser);
    CAST_N_GETMIN(IntVec2Property, theProperty, parser);
    CAST_N_GETMIN(IntVec3Property, theProperty, parser);
    CAST_N_GETMIN(IntVec4Property, theProperty, parser);
    CAST_N_GETMIN(FloatMat2Property, theProperty, parser);
    CAST_N_GETMIN(FloatMat3Property, theProperty, parser);
    CAST_N_GETMIN(FloatMat4Property, theProperty, parser);
    CAST_N_GETMIN(FloatVec2Property, theProperty, parser);
    CAST_N_GETMIN(FloatVec3Property, theProperty, parser);
    CAST_N_GETMIN(FloatVec4Property, theProperty, parser);
    LogErrorCustom("inviwo_getPropertyMaxValue", "Unknown parameter type: " << theProperty->getClassIdentifier());
    Py_RETURN_NONE;
}

PyObject* py_clickButton(PyObject* /*self*/, PyObject* args) {
    static PyClickButtonMethod p;

    if (!p.testParams(args)) return nullptr;

    std::string path = std::string(PyValueParser::parse<std::string>(PyTuple_GetItem(args, 0)));

    Property* theProperty =
        InviwoApplication::getPtr()->getProcessorNetwork()->getProperty(splitString(path, '.'));

    if (!theProperty) {
        std::string msg = std::string("setPropertyValue() no property with path: ") + path;
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    ButtonProperty* button = dynamic_cast<ButtonProperty*>(theProperty);

    if (!button) {
        std::string msg = std::string("clickButton() found property is not a ButtonProperty: ") +
                          theProperty->getClassIdentifier();
        PyErr_SetString(PyExc_TypeError, msg.c_str());
        return nullptr;
    }

    button->pressButton();
    Py_RETURN_NONE;
}

PySetPropertyValueMethod::PySetPropertyValueMethod() : path_("path"), value_("value") {
    addParam(&path_);
    addParam(&value_);
}

PySetPropertyMaxValueMethod::PySetPropertyMaxValueMethod() : path_("path"), maxValue_("maxValue") {
    addParam(&path_);
    addParam(&maxValue_);
}

PySetPropertyMinValueMethod::PySetPropertyMinValueMethod() : path_("path"), minValue_("minValue") {
    addParam(&path_);
    addParam(&minValue_);
}

PyGetPropertyValueMethod::PyGetPropertyValueMethod() : path_("path") { addParam(&path_); }

PyGetPropertyMaxValueMethod::PyGetPropertyMaxValueMethod() : path_("path") { addParam(&path_); }

PyGetPropertyMinValueMethod::PyGetPropertyMinValueMethod() : path_("path") { addParam(&path_); }

PyClickButtonMethod::PyClickButtonMethod() : path_("path") { addParam(&path_); }
}<|endoftext|>


<commit_before>/* Siconos is a program dedicated to modeling, simulation and control
 * of non smooth dynamical systems.
 *
 * Copyright 2016 INRIA.
 *
 * 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.
*/

/*! \file  MoreauJeanGOSI.hpp */

#ifndef MoreauJeanGOSI_H
#define MoreauJeanGOSI_H

#include "OneStepIntegrator.hpp"
#include "OneStepNSProblem.hpp"

#include <limits>

/**  \class MoreauJeanGOSI
 *   \brief One Step time Integrator for First Order Dynamical Systems  for
 *    mechanical Systems (LagrangianDS and NewtonEulerDS)
 *   \author SICONOS Development Team - copyright INRIA
 *   \version 3.0.0.
 *   \date (Creation) Apr 26, 2004
 *
 * This integrator is the work horse of the event--capturing time stepping schemes
 * for mechanical systems.  It is mainly based on the pioneering works of M. Jean and
 * J.J. Moreau for the time integration of mechanical systems
 * with unilateral contact, impact and Coulomb's friction with \f$\theta\f$ scheme
 *
 * For the linear Lagrangina system, the scheme reads as
 * \f{equation}{
 * \begin{cases}
 *  M (v_{k+1}-v_k)
 *  + h K q_{k+\theta} + h C v_{k+\theta}     -   h F_{k+\theta} = p_{k+1} = G P_{k+1},\label{eq:MoreauTS-motion}\\[1mm]
 *  q_{k+1} = q_{k} + h v_{k+\theta}, \quad \\[1mm]
 *  U_{k+1} = G^\top\, v_{k+1}, \\[1mm]
 *  \begin{array}{lcl}
 *    0 \leq U^\alpha_{k+1} + e  U^\alpha_{k} \perp P^\alpha_{k+1}  \geq 0,& \quad&\alpha \in \mathcal I_1, \\[1mm]
 *    P^\alpha_{k+1}  =0,&\quad& \alpha \in \mathcal I \setminus \mathcal I_1,
 * \end{array}
 * \end{cases} \f}
 * with  \f$\theta \in [0,1]\f$. The index set \f$\mathcal I_1\f$ is the discrete equivalent
 * to the rule that allows us to apply the Signorini  condition at the velocity level.
 * In the numerical practice, we choose to define this set by
 * \f{equation}{
 *   \label{eq:index-set1}
 *  \mathcal I_1 = \{\alpha \in \mathcal I \mid G^\top (q_{k} + h v_{k}) + w \leq 0\text{ and } U_k \leq 0 \}.
 * \f}.
 *
 * For more details, we refer to
 *
 * M. Jean and J.J. Moreau. Dynamics in the presence of unilateral contacts and dry friction: a numerical approach.
 * In G. Del Pietro and F. Maceri, editors, Unilateral problems in structural analysis.
 * II, pages 151–196. CISM 304, Spinger Verlag, 1987.
 *
 * J.J. Moreau. Unilateral contact and dry friction in finite freedom dynamics.
 * In J.J. Moreau and Panagiotopoulos P.D., editors, Nonsmooth Mechanics and Applications,
 * number 302 in CISM, Courses and lectures, pages 1–82. CISM 302, Spinger Verlag, Wien- New York, 1988a.
 *
 * J.J. Moreau. Numerical aspects of the sweeping process.
 * Computer Methods in Applied Mechanics and Engineering, 177:329–349, 1999.
 *
 * M. Jean. The non smooth contact dynamics method.
 * Computer Methods in Applied Mechanics and Engineering, 177:235–257, 1999.
 *
 * and for a review :
 *
 * V. Acary and B. Brogliato. Numerical Methods for Nonsmooth Dynamical Systems:
 * Applications in Mechanics and Electronics, volume 35 of Lecture Notes in
 * Applied and Computational Mechanics. Springer Verlag, 2008.
 *
 *
 * MoreauJeanGOSI class is used to define some time-integrators methods for a
 * list of dynamical systems. A MoreauJeanGOSI instance is defined by the value
 * of theta and the list of concerned dynamical systems.
 *
 * Each DynamicalSystem is associated to a SiconosMatrix, named "W", the "iteration" matrix"
 * W matrices are initialized and computed in initializeIterationMatrixW and computeW. Depending on the DS type,
 * they may depend on time t and DS state x.
 *
 * For mechanical systems, the implementation uses _p for storing the
 * the input due to the nonsmooth law. This MoreauJeanGOSI scheme assumes that the
 * relative degree is two.
 *
 * For Lagrangian systems, the implementation uses _p[1] for storing the
 * discrete impulse.
 *
 * Main functions:
 *
 * - computeFreeState(): computes xfree (or vfree), dynamical systems
 *   state without taking non-smooth part into account \n
 *
 * - updateState(): computes x (q,v), the complete dynamical systems
 *    states.
 * See User's guide for details.
 *
 */
class MoreauJeanGOSI : public OneStepIntegrator
{
protected:
  /** serialization hooks
  */
  ACCEPT_SERIALIZATION(MoreauJeanGOSI);

  /** Stl map that associates the columns of  W MoreauJeanGOSI matrix to each DynamicalSystem of the OSI if it has some boundary conditions */
  MapOfDSMatrices _WBoundaryConditionsMap;

  /** Stl map that associates a theta parameter for the integration
  *  scheme to each DynamicalSystem of the OSI */
  double _theta;

  /** A gamma parameter for the integration scheme to each DynamicalSystem of the OSI
   * This parameter is used to apply a theta-method to the input $r$
   */
  double _gamma;

  /** a boolean to know if the parameter must be used or not
   */
  bool _useGamma;

  /** a boolean to know if the parameter must be used or not
   */
  bool _useGammaForRelation;

  /** a boolean to force the evaluation of T in an explicit way
   */
  bool _explicitNewtonEulerDSOperators;

  /** nslaw effects
   */
  struct _NSLEffectOnFreeOutput;
  friend struct _NSLEffectOnFreeOutput;

public:

  enum {OSNSP_RHS,WORK_INTERACTION_LENGTH};

  /** constructor from theta value only
   *  \param theta value for all linked DS (default = 0.5).
   *  \param gamma value for all linked DS (default = NaN and gamma is not used).
   */
  MoreauJeanGOSI(double theta = 0.5, double gamma = std::numeric_limits<double>::quiet_NaN());

  /** destructor
   */
  virtual ~MoreauJeanGOSI() {};

  // --- GETTERS/SETTERS ---

  /** get the value of W corresponding to DynamicalSystem ds
   * \param ds a pointer to DynamicalSystem, optional, default =
   * NULL. get W[0] in that case
   *  \return SimpleMatrix
   */
  const SimpleMatrix getW(SP::DynamicalSystem ds = SP::DynamicalSystem());

  /** get W corresponding to DynamicalSystem ds
   * \param ds a pointer to DynamicalSystem
   * \return pointer to a SiconosMatrix
   */
  SP::SimpleMatrix W(SP::DynamicalSystem ds);

  /** set the value of W[ds] to newValue
   * \param newValue SiconosMatrix
   * \param ds a pointer to DynamicalSystem,
   */
  void setW(const SiconosMatrix& newValue, SP::DynamicalSystem ds);

  /** set W[ds] to pointer newPtr
   * \param newPtr
   * \param ds a pointer to DynamicalSystem
   */
  void setWPtr(SP::SimpleMatrix newPtr, SP::DynamicalSystem ds);

  // -- WBoundaryConditions --

  /** get WBoundaryConditions map
   *  \return a MapOfDSMatrices
   */
  inline MapOfDSMatrices getWBoundaryConditionsMap() const
  {
    return _WBoundaryConditionsMap;
  };

  /** get the value of WBoundaryConditions corresponding to DynamicalSystem ds
   * \param ds a pointer to DynamicalSystem, optional, default =
   * NULL. get WBoundaryConditions[0] in that case
   *  \return SimpleMatrix
   */
  const SimpleMatrix getWBoundaryConditions(SP::DynamicalSystem ds = SP::DynamicalSystem());

  /** get WBoundaryConditions corresponding to DynamicalSystem ds
   * \param ds a pointer to DynamicalSystem, optional, default =
   * NULL. get WBoundaryConditions[0] in that case
   * \return pointer to a SiconosMatrix
   */
  SP::SiconosMatrix WBoundaryConditions(SP::DynamicalSystem ds);

  // -- theta --

  /** get theta
   *  \return a double
   */
  inline double theta()
  {
    return _theta;
  };

  /** set the value of theta
   *  \param newTheta a double
   */
  inline void setTheta(double newTheta)
  {
    _theta = newTheta;
  };

  // -- gamma --

  /** get gamma
   *  \return a double
   */
  inline double gamma()
  {
    return _gamma;
  };

  /** set the value of gamma
   *  \param newGamma a double
   */
  inline void setGamma(double newGamma)
  {
    _gamma = newGamma;
    _useGamma = true;
  };

  // -- useGamma --

  /** get bool useGamma
   *  \return a bool
   */
  inline bool useGamma()
  {
    return _useGamma;
  };

  /** set the Boolean to indicate that we use gamma
   *  \param newUseGamma  a  Boolean variable
   */
  inline void setUseGamma(bool newUseGamma)
  {
    _useGamma = newUseGamma;
  };

  /** get bool gammaForRelation for the relation
   *  \return a Boolean
   */
  inline bool useGammaForRelation()
  {
    return _useGammaForRelation;
  };

  /** set the boolean to indicate that we use gamma for the relation
   *  \param newUseGammaForRelation a Boolean
   */
  inline void setUseGammaForRelation(bool newUseGammaForRelation)
  {
    _useGammaForRelation = newUseGammaForRelation;
    if(_useGammaForRelation) _useGamma = false;
  };

  /** get boolean _explicitNewtonEulerDSOperators for the relation
   *  \return a Boolean
   */
  inline bool explicitNewtonEulerDSOperators()
  {
    return _explicitNewtonEulerDSOperators;
  };

  /** set the boolean to indicate that we use gamma for the relation
   *  \param newUseGammaForRelation a Boolean
   */
  inline void setExplicitNewtonEulerDSOperators(bool newExplicitNewtonEulerDSOperators)
  {
    _explicitNewtonEulerDSOperators = newExplicitNewtonEulerDSOperators;
  };

  // --- OTHER FUNCTIONS ---

  virtual void initialize_nonsmooth_problems();

  /** initialization of the work vectors and matrices (properties) related to
   *  one dynamical system on the graph and needed by the osi
   * \param m the Model
   * \param t time of initialization
   * \param ds the dynamical system
   */
  void initializeDynamicalSystem(Model& m, double t, SP::DynamicalSystem ds);

  /** initialization of the work vectors and matrices (properties) related to
   *  one interaction on the graph and needed by the osi
   * \param inter the interaction
   * \param interProp the properties on the graph
   * \param DSG the dynamical systems graph
   */
  void fillDSLinks(Interaction &inter,
		     InteractionProperties& interProp,
		     DynamicalSystemsGraph & DSG);

  /** get the number of index sets required for the simulation
   * \return unsigned int
   */
  unsigned int numberOfIndexSets() const {return 2;};

  /** initialize iteration matrix W MoreauJeanGOSI matrix at time t
   *  \param time
   *  \param ds a pointer to DynamicalSystem
   */
  void initializeIterationMatrixW(double time, SP::DynamicalSystem ds);

  /** compute W MoreauJeanGOSI matrix at time t
   *  \param time (double)
   *  \param ds a pointer to DynamicalSystem
   *  \param W the matrix to compute
   */
  void computeW(double time, SP::DynamicalSystem ds,  SiconosMatrix& W);

  /** compute WBoundaryConditionsMap[ds] MoreauJeanGOSI matrix at time t
   *  \param ds a pointer to DynamicalSystem
   */
  void computeWBoundaryConditions(SP::DynamicalSystem ds);

  /** initialize iteration matrix WBoundaryConditionsMap[ds] MoreauJeanGOSI
   *  \param ds a pointer to DynamicalSystem
   */
  void initializeIterationMatrixWBoundaryConditions(SP::DynamicalSystem ds);

  /** compute the initial state of the Newton loop.
   */
  void computeInitialNewtonState();

  /** return the maximum of all norms for the "MoreauJeanGOSI-discretized" residus of DS
      \return a double
   */
  double computeResidu();

  /** Perform the integration of the dynamical systems linked to this integrator
   *  without taking into account the nonsmooth input (_r or _p)
   */
  virtual void computeFreeState();

  /** integrates the Interaction linked to this integrator, without taking non-smooth effects into account
   * \param vertex_inter vertex of the interaction graph
   * \param osnsp pointer to OneStepNSProblem
   */
//  virtual void computeFreeOutput(InteractionsGraph::VDescriptor& vertex_inter, OneStepNSProblem* osnsp);

  /** Apply the rule to one Interaction to known if is it should be included
   * in the IndexSet of level i
   * \param inter the Interaction to test
   * \param i level of the IndexSet
   * \return Boolean
   */
  virtual bool addInteractionInIndexSet(SP::Interaction inter, unsigned int i);

  /** Apply the rule to one Interaction to known if is it should be removed
   * in the IndexSet of level i
   * \param inter the Interaction to test
   * \param i level of the IndexSet
   * \return Boolean
   */
  virtual bool removeInteractionInIndexSet(SP::Interaction inter, unsigned int i);

  /** method to prepare the fist Newton iteration
   *   \param time
   */
  void prepareNewtonIteration(double time);

  /** integrate the system, between tinit and tend (->iout=true), with possible stop at tout (->iout=false)
   *  \param tinit the initial time
   *  \param tend the end time
   *  \param tout the real end time
   *  \param notUsed useless flag (for MoreauJeanGOSI, used in LsodarOSI)
   */
  void integrate(double& tinit, double& tend, double& tout, int& notUsed);

  /** update the state of the dynamical systems
      \param ds the dynamical to update
   */
  virtual void updatePosition(DynamicalSystem &ds);

  /** update the state of the dynamical systems
   *  \param level the level of interest for the dynamics: not used at the time
   */
  virtual void updateState(const unsigned int level);

  /** Compute the nonsmooth law contribution
   * \param inter the interaction (for y_k)
   * \param osnsp the non-smooth integrator
   */
  void NSLcontrib(SP::Interaction inter, OneStepNSProblem& osnsp);

  /** Displays the data of the MoreauJeanGOSI's integrator
   */
  void display();

  /** visitors hook
  */
  ACCEPT_STD_VISITORS();

};

#endif // MoreauJeanGOSI_H
<commit_msg>[kernel] remove unimplemented methods<commit_after>/* Siconos is a program dedicated to modeling, simulation and control
 * of non smooth dynamical systems.
 *
 * Copyright 2016 INRIA.
 *
 * 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.
*/

/*! \file  MoreauJeanGOSI.hpp */

#ifndef MoreauJeanGOSI_H
#define MoreauJeanGOSI_H

#include "OneStepIntegrator.hpp"
#include "OneStepNSProblem.hpp"

#include <limits>

/**  \class MoreauJeanGOSI
 *   \brief One Step time Integrator for First Order Dynamical Systems  for
 *    mechanical Systems (LagrangianDS and NewtonEulerDS)
 *   \author SICONOS Development Team - copyright INRIA
 *   \version 3.0.0.
 *   \date (Creation) Apr 26, 2004
 *
 * This integrator is the work horse of the event--capturing time stepping schemes
 * for mechanical systems.  It is mainly based on the pioneering works of M. Jean and
 * J.J. Moreau for the time integration of mechanical systems
 * with unilateral contact, impact and Coulomb's friction with \f$\theta\f$ scheme
 *
 * For the linear Lagrangina system, the scheme reads as
 * \f{equation}{
 * \begin{cases}
 *  M (v_{k+1}-v_k)
 *  + h K q_{k+\theta} + h C v_{k+\theta}     -   h F_{k+\theta} = p_{k+1} = G P_{k+1},\label{eq:MoreauTS-motion}\\[1mm]
 *  q_{k+1} = q_{k} + h v_{k+\theta}, \quad \\[1mm]
 *  U_{k+1} = G^\top\, v_{k+1}, \\[1mm]
 *  \begin{array}{lcl}
 *    0 \leq U^\alpha_{k+1} + e  U^\alpha_{k} \perp P^\alpha_{k+1}  \geq 0,& \quad&\alpha \in \mathcal I_1, \\[1mm]
 *    P^\alpha_{k+1}  =0,&\quad& \alpha \in \mathcal I \setminus \mathcal I_1,
 * \end{array}
 * \end{cases} \f}
 * with  \f$\theta \in [0,1]\f$. The index set \f$\mathcal I_1\f$ is the discrete equivalent
 * to the rule that allows us to apply the Signorini  condition at the velocity level.
 * In the numerical practice, we choose to define this set by
 * \f{equation}{
 *   \label{eq:index-set1}
 *  \mathcal I_1 = \{\alpha \in \mathcal I \mid G^\top (q_{k} + h v_{k}) + w \leq 0\text{ and } U_k \leq 0 \}.
 * \f}.
 *
 * For more details, we refer to
 *
 * M. Jean and J.J. Moreau. Dynamics in the presence of unilateral contacts and dry friction: a numerical approach.
 * In G. Del Pietro and F. Maceri, editors, Unilateral problems in structural analysis.
 * II, pages 151–196. CISM 304, Spinger Verlag, 1987.
 *
 * J.J. Moreau. Unilateral contact and dry friction in finite freedom dynamics.
 * In J.J. Moreau and Panagiotopoulos P.D., editors, Nonsmooth Mechanics and Applications,
 * number 302 in CISM, Courses and lectures, pages 1–82. CISM 302, Spinger Verlag, Wien- New York, 1988a.
 *
 * J.J. Moreau. Numerical aspects of the sweeping process.
 * Computer Methods in Applied Mechanics and Engineering, 177:329–349, 1999.
 *
 * M. Jean. The non smooth contact dynamics method.
 * Computer Methods in Applied Mechanics and Engineering, 177:235–257, 1999.
 *
 * and for a review :
 *
 * V. Acary and B. Brogliato. Numerical Methods for Nonsmooth Dynamical Systems:
 * Applications in Mechanics and Electronics, volume 35 of Lecture Notes in
 * Applied and Computational Mechanics. Springer Verlag, 2008.
 *
 *
 * MoreauJeanGOSI class is used to define some time-integrators methods for a
 * list of dynamical systems. A MoreauJeanGOSI instance is defined by the value
 * of theta and the list of concerned dynamical systems.
 *
 * Each DynamicalSystem is associated to a SiconosMatrix, named "W", the "iteration" matrix"
 * W matrices are initialized and computed in initializeIterationMatrixW and computeW. Depending on the DS type,
 * they may depend on time t and DS state x.
 *
 * For mechanical systems, the implementation uses _p for storing the
 * the input due to the nonsmooth law. This MoreauJeanGOSI scheme assumes that the
 * relative degree is two.
 *
 * For Lagrangian systems, the implementation uses _p[1] for storing the
 * discrete impulse.
 *
 * Main functions:
 *
 * - computeFreeState(): computes xfree (or vfree), dynamical systems
 *   state without taking non-smooth part into account \n
 *
 * - updateState(): computes x (q,v), the complete dynamical systems
 *    states.
 * See User's guide for details.
 *
 */
class MoreauJeanGOSI : public OneStepIntegrator
{
protected:
  /** serialization hooks
  */
  ACCEPT_SERIALIZATION(MoreauJeanGOSI);

  /** Stl map that associates the columns of  W MoreauJeanGOSI matrix to each DynamicalSystem of the OSI if it has some boundary conditions */
  MapOfDSMatrices _WBoundaryConditionsMap;

  /** Stl map that associates a theta parameter for the integration
  *  scheme to each DynamicalSystem of the OSI */
  double _theta;

  /** A gamma parameter for the integration scheme to each DynamicalSystem of the OSI
   * This parameter is used to apply a theta-method to the input $r$
   */
  double _gamma;

  /** a boolean to know if the parameter must be used or not
   */
  bool _useGamma;

  /** a boolean to know if the parameter must be used or not
   */
  bool _useGammaForRelation;

  /** a boolean to force the evaluation of T in an explicit way
   */
  bool _explicitNewtonEulerDSOperators;

  /** nslaw effects
   */
  struct _NSLEffectOnFreeOutput;
  friend struct _NSLEffectOnFreeOutput;

public:

  enum {OSNSP_RHS,WORK_INTERACTION_LENGTH};

  /** constructor from theta value only
   *  \param theta value for all linked DS (default = 0.5).
   *  \param gamma value for all linked DS (default = NaN and gamma is not used).
   */
  MoreauJeanGOSI(double theta = 0.5, double gamma = std::numeric_limits<double>::quiet_NaN());

  /** destructor
   */
  virtual ~MoreauJeanGOSI() {};

  // --- GETTERS/SETTERS ---

  // --- OTHER FUNCTIONS ---

  virtual void initialize_nonsmooth_problems();

  /** initialization of the work vectors and matrices (properties) related to
   *  one dynamical system on the graph and needed by the osi
   * \param m the Model
   * \param t time of initialization
   * \param ds the dynamical system
   */
  void initializeDynamicalSystem(Model& m, double t, SP::DynamicalSystem ds);

  /** initialization of the work vectors and matrices (properties) related to
   *  one interaction on the graph and needed by the osi
   * \param inter the interaction
   * \param interProp the properties on the graph
   * \param DSG the dynamical systems graph
   */
  void fillDSLinks(Interaction &inter,
		     InteractionProperties& interProp,
		     DynamicalSystemsGraph & DSG);

  /** get the number of index sets required for the simulation
   * \return unsigned int
   */
  unsigned int numberOfIndexSets() const {return 2;};

  /** initialize iteration matrix W MoreauJeanGOSI matrix at time t
   *  \param time
   *  \param ds a pointer to DynamicalSystem
   */
  void initializeIterationMatrixW(double time, SP::DynamicalSystem ds);

  /** compute W MoreauJeanGOSI matrix at time t
   *  \param time (double)
   *  \param ds a pointer to DynamicalSystem
   *  \param W the matrix to compute
   */
  void computeW(double time, SP::DynamicalSystem ds,  SiconosMatrix& W);

  /** compute WBoundaryConditionsMap[ds] MoreauJeanGOSI matrix at time t
   *  \param ds a pointer to DynamicalSystem
   */
  void computeWBoundaryConditions(SP::DynamicalSystem ds);

  /** initialize iteration matrix WBoundaryConditionsMap[ds] MoreauJeanGOSI
   *  \param ds a pointer to DynamicalSystem
   */
  void initializeIterationMatrixWBoundaryConditions(SP::DynamicalSystem ds);

  /** compute the initial state of the Newton loop.
   */
  void computeInitialNewtonState();

  /** return the maximum of all norms for the "MoreauJeanGOSI-discretized" residus of DS
      \return a double
   */
  double computeResidu();

  /** Perform the integration of the dynamical systems linked to this integrator
   *  without taking into account the nonsmooth input (_r or _p)
   */
  virtual void computeFreeState();

  /** integrates the Interaction linked to this integrator, without taking non-smooth effects into account
   * \param vertex_inter vertex of the interaction graph
   * \param osnsp pointer to OneStepNSProblem
   */
//  virtual void computeFreeOutput(InteractionsGraph::VDescriptor& vertex_inter, OneStepNSProblem* osnsp);

  /** Apply the rule to one Interaction to known if is it should be included
   * in the IndexSet of level i
   * \param inter the Interaction to test
   * \param i level of the IndexSet
   * \return Boolean
   */
  virtual bool addInteractionInIndexSet(SP::Interaction inter, unsigned int i);

  /** Apply the rule to one Interaction to known if is it should be removed
   * in the IndexSet of level i
   * \param inter the Interaction to test
   * \param i level of the IndexSet
   * \return Boolean
   */
  virtual bool removeInteractionInIndexSet(SP::Interaction inter, unsigned int i);

  /** method to prepare the fist Newton iteration
   *   \param time
   */
  void prepareNewtonIteration(double time);

  /** integrate the system, between tinit and tend (->iout=true), with possible stop at tout (->iout=false)
   *  \param tinit the initial time
   *  \param tend the end time
   *  \param tout the real end time
   *  \param notUsed useless flag (for MoreauJeanGOSI, used in LsodarOSI)
   */
  void integrate(double& tinit, double& tend, double& tout, int& notUsed);

  /** update the state of the dynamical systems
      \param ds the dynamical to update
   */
  virtual void updatePosition(DynamicalSystem &ds);

  /** update the state of the dynamical systems
   *  \param level the level of interest for the dynamics: not used at the time
   */
  virtual void updateState(const unsigned int level);

  /** Compute the nonsmooth law contribution
   * \param inter the interaction (for y_k)
   * \param osnsp the non-smooth integrator
   */
  void NSLcontrib(SP::Interaction inter, OneStepNSProblem& osnsp);

  /** Displays the data of the MoreauJeanGOSI's integrator
   */
  void display();

  /** visitors hook
  */
  ACCEPT_STD_VISITORS();

};

#endif // MoreauJeanGOSI_H
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "frame_dropper.h"
#include "internal_defines.h"
#include "trace.h"

namespace webrtc
{

VCMFrameDropper::VCMFrameDropper(WebRtc_Word32 vcmId)
:
_vcmId(vcmId),
_keyFrameSizeAvgKbits(0.9f),
_keyFrameRatio(0.99f),
_dropRatio(0.9f, 0.96f)
{
    Reset();
}

void
VCMFrameDropper::Reset()
{
    _keyFrameRatio.Reset(0.99f);
    _keyFrameRatio.Apply(1.0f, 1.0f/300.0f); // 1 key frame every 10th second in 30 fps
    _keyFrameSizeAvgKbits.Reset(0.9f);
    _keyFrameCount = 0;
    _accumulator = 0.0f;
    _accumulatorMax = 150.0f; // assume 300 kb/s and 0.5 s window
    _targetBitRate = 300.0f;
    _incoming_frame_rate = 30;
    _keyFrameSpreadFrames = 0.5f * _incoming_frame_rate;
    _dropNext = false;
    _dropRatio.Reset(0.9f);
    _dropRatio.Apply(0.0f, 0.0f); // Initialize to 0
    _dropCount = 0;
    _windowSize = 0.5f;
    _wasBelowMax = true;
    _enabled = true;
    _fastMode = false; // start with normal (non-aggressive) mode
    // Cap for the encoder buffer level/accumulator, in secs.
    _cap_buffer_size = 3.0f;
    // Cap on maximum amount of dropped frames between kept frames, in secs.
    _max_time_drops = 4.0f;
}

void
VCMFrameDropper::Enable(bool enable)
{
    _enabled = enable;
}

void
VCMFrameDropper::Fill(WebRtc_UWord32 frameSizeBytes, bool deltaFrame)
{
    if (!_enabled)
    {
        return;
    }
    float frameSizeKbits = 8.0f * static_cast<float>(frameSizeBytes) / 1000.0f;
    if (!deltaFrame && !_fastMode) // fast mode does not treat key-frames any different
    {
        _keyFrameSizeAvgKbits.Apply(1, frameSizeKbits);
        _keyFrameRatio.Apply(1.0, 1.0);
        if (frameSizeKbits > _keyFrameSizeAvgKbits.Value())
        {
            // Remove the average key frame size since we
            // compensate for key frames when adding delta
            // frames.
            frameSizeKbits -= _keyFrameSizeAvgKbits.Value();
        }
        else
        {
            // Shouldn't be negative, so zero is the lower bound.
            frameSizeKbits = 0;
        }
        if (_keyFrameRatio.Value() > 1e-5 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames)
        {
            // We are sending key frames more often than our upper bound for
            // how much we allow the key frame compensation to be spread
            // out in time. Therefor we must use the key frame ratio rather
            // than keyFrameSpreadFrames.
            _keyFrameCount = static_cast<WebRtc_Word32>(1 / _keyFrameRatio.Value() + 0.5);
        }
        else
        {
            // Compensate for the key frame the following frames
            _keyFrameCount = static_cast<WebRtc_Word32>(_keyFrameSpreadFrames + 0.5);
        }
    }
    else
    {
        // Decrease the keyFrameRatio
        _keyFrameRatio.Apply(1.0, 0.0);
    }
    // Change the level of the accumulator (bucket)
    _accumulator += frameSizeKbits;
    CapAccumulator();
}

void
VCMFrameDropper::Leak(WebRtc_UWord32 inputFrameRate)
{
    if (!_enabled)
    {
        return;
    }
    if (inputFrameRate < 1)
    {
        return;
    }
    if (_targetBitRate < 0.0f)
    {
        return;
    }
    _keyFrameSpreadFrames = 0.5f * inputFrameRate;
    // T is the expected bits per frame (target). If all frames were the same size,
    // we would get T bits per frame. Notice that T is also weighted to be able to
    // force a lower frame rate if wanted.
    float T = _targetBitRate / inputFrameRate;
    if (_keyFrameCount > 0)
    {
        // Perform the key frame compensation
        if (_keyFrameRatio.Value() > 0 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames)
        {
            T -= _keyFrameSizeAvgKbits.Value() * _keyFrameRatio.Value();
        }
        else
        {
            T -= _keyFrameSizeAvgKbits.Value() / _keyFrameSpreadFrames;
        }
        _keyFrameCount--;
    }
    _accumulator -= T;
    UpdateRatio();
}

void
VCMFrameDropper::UpdateNack(WebRtc_UWord32 nackBytes)
{
    if (!_enabled)
    {
        return;
    }
    _accumulator += static_cast<float>(nackBytes) * 8.0f / 1000.0f;
}

void
VCMFrameDropper::FillBucket(float inKbits, float outKbits)
{
    _accumulator += (inKbits - outKbits);
}

void
VCMFrameDropper::UpdateRatio()
{
    if (_accumulator > 1.3f * _accumulatorMax)
    {
        // Too far above accumulator max, react faster
        _dropRatio.UpdateBase(0.8f);
    }
    else
    {
        // Go back to normal reaction
        _dropRatio.UpdateBase(0.9f);
    }
    if (_accumulator > _accumulatorMax)
    {
        // We are above accumulator max, and should ideally
        // drop a frame. Increase the dropRatio and drop
        // the frame later.
        if (_wasBelowMax)
        {
            _dropNext = true;
        }
        if (_fastMode)
        {
            // always drop in aggressive mode
            _dropNext = true;
        }

        _dropRatio.Apply(1.0f, 1.0f);
        _dropRatio.UpdateBase(0.9f);
    }
    else
    {
        _dropRatio.Apply(1.0f, 0.0f);
    }
    if (_accumulator < 0.0f)
    {
        _accumulator = 0.0f;
    }
    _wasBelowMax = _accumulator < _accumulatorMax;
    WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId),  "FrameDropper: dropRatio = %f accumulator = %f, accumulatorMax = %f", _dropRatio.Value(), _accumulator, _accumulatorMax);
}

// This function signals when to drop frames to the caller. It makes use of the dropRatio
// to smooth out the drops over time.
bool
VCMFrameDropper::DropFrame()
{
    if (!_enabled)
    {
        return false;
    }
    if (_dropNext)
    {
        _dropNext = false;
        _dropCount = 0;
    }

    if (_dropRatio.Value() >= 0.5f) // Drops per keep
    {
        // limit is the number of frames we should drop between each kept frame
        // to keep our drop ratio. limit is positive in this case.
        float denom = 1.0f - _dropRatio.Value();
        if (denom < 1e-5)
        {
            denom = (float)1e-5;
        }
        WebRtc_Word32 limit = static_cast<WebRtc_Word32>(1.0f / denom - 1.0f + 0.5f);
        // Put a bound on the max amount of dropped frames between each kept
        // frame, in terms of frame rate and window size (secs).
        int max_limit = static_cast<int>(_incoming_frame_rate *
                                         _max_time_drops);
        if (limit > max_limit) {
          limit = max_limit;
        }
        if (_dropCount < 0)
        {
            // Reset the _dropCount since it was negative and should be positive.
            if (_dropRatio.Value() > 0.4f)
            {
                _dropCount = -_dropCount;
            }
            else
            {
                _dropCount = 0;
            }
        }
        if (_dropCount < limit)
        {
            // As long we are below the limit we should drop frames.
            _dropCount++;
            return true;
        }
        else
        {
            // Only when we reset _dropCount a frame should be kept.
            _dropCount = 0;
            return false;
        }
    }
    else if (_dropRatio.Value() > 0.0f && _dropRatio.Value() < 0.5f) // Keeps per drop
    {
        // limit is the number of frames we should keep between each drop
        // in order to keep the drop ratio. limit is negative in this case,
        // and the _dropCount is also negative.
        float denom = _dropRatio.Value();
        if (denom < 1e-5)
        {
            denom = (float)1e-5;
        }
        WebRtc_Word32 limit = -static_cast<WebRtc_Word32>(1.0f / denom - 1.0f + 0.5f);
        if (_dropCount > 0)
        {
            // Reset the _dropCount since we have a positive
            // _dropCount, and it should be negative.
            if (_dropRatio.Value() < 0.6f)
            {
                _dropCount = -_dropCount;
            }
            else
            {
                _dropCount = 0;
            }
        }
        if (_dropCount > limit)
        {
            if (_dropCount == 0)
            {
                // Drop frames when we reset _dropCount.
                _dropCount--;
                return true;
            }
            else
            {
                // Keep frames as long as we haven't reached limit.
                _dropCount--;
                return false;
            }
        }
        else
        {
            _dropCount = 0;
            return false;
        }
    }
    _dropCount = 0;
    return false;

    // A simpler version, unfiltered and quicker
    //bool dropNext = _dropNext;
    //_dropNext = false;
    //return dropNext;
}

void
VCMFrameDropper::SetRates(float bitRate, float incoming_frame_rate)
{
    // Bit rate of -1 means infinite bandwidth.
    _accumulatorMax = bitRate * _windowSize; // bitRate * windowSize (in seconds)
    if (_targetBitRate > 0.0f && bitRate < _targetBitRate && _accumulator > _accumulatorMax)
    {
        // Rescale the accumulator level if the accumulator max decreases
        _accumulator = bitRate / _targetBitRate * _accumulator;
    }
    _targetBitRate = bitRate;
    CapAccumulator();
    _incoming_frame_rate = incoming_frame_rate;
}

float
VCMFrameDropper::ActualFrameRate(WebRtc_UWord32 inputFrameRate) const
{
    if (!_enabled)
    {
        return static_cast<float>(inputFrameRate);
    }
    return inputFrameRate * (1.0f - _dropRatio.Value());
}

// Put a cap on the accumulator, i.e., don't let it grow beyond some level.
// This is a temporary fix for screencasting where very large frames from
// encoder will cause very slow response (too many frame drops).
void VCMFrameDropper::CapAccumulator() {
  float max_accumulator = _targetBitRate * _cap_buffer_size;
  if (_accumulator > max_accumulator) {
    _accumulator = max_accumulator;
  }
}

}
<commit_msg>VCM: Removing frame drop enable from Reset call BUG = 1387<commit_after>/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "frame_dropper.h"
#include "internal_defines.h"
#include "trace.h"

namespace webrtc
{

VCMFrameDropper::VCMFrameDropper(WebRtc_Word32 vcmId)
:
_vcmId(vcmId),
_keyFrameSizeAvgKbits(0.9f),
_keyFrameRatio(0.99f),
_dropRatio(0.9f, 0.96f),
_enabled(true)
{
    Reset();
}

void
VCMFrameDropper::Reset()
{
    _keyFrameRatio.Reset(0.99f);
    _keyFrameRatio.Apply(1.0f, 1.0f/300.0f); // 1 key frame every 10th second in 30 fps
    _keyFrameSizeAvgKbits.Reset(0.9f);
    _keyFrameCount = 0;
    _accumulator = 0.0f;
    _accumulatorMax = 150.0f; // assume 300 kb/s and 0.5 s window
    _targetBitRate = 300.0f;
    _incoming_frame_rate = 30;
    _keyFrameSpreadFrames = 0.5f * _incoming_frame_rate;
    _dropNext = false;
    _dropRatio.Reset(0.9f);
    _dropRatio.Apply(0.0f, 0.0f); // Initialize to 0
    _dropCount = 0;
    _windowSize = 0.5f;
    _wasBelowMax = true;
    _fastMode = false; // start with normal (non-aggressive) mode
    // Cap for the encoder buffer level/accumulator, in secs.
    _cap_buffer_size = 3.0f;
    // Cap on maximum amount of dropped frames between kept frames, in secs.
    _max_time_drops = 4.0f;
}

void
VCMFrameDropper::Enable(bool enable)
{
    _enabled = enable;
}

void
VCMFrameDropper::Fill(WebRtc_UWord32 frameSizeBytes, bool deltaFrame)
{
    if (!_enabled)
    {
        return;
    }
    float frameSizeKbits = 8.0f * static_cast<float>(frameSizeBytes) / 1000.0f;
    if (!deltaFrame && !_fastMode) // fast mode does not treat key-frames any different
    {
        _keyFrameSizeAvgKbits.Apply(1, frameSizeKbits);
        _keyFrameRatio.Apply(1.0, 1.0);
        if (frameSizeKbits > _keyFrameSizeAvgKbits.Value())
        {
            // Remove the average key frame size since we
            // compensate for key frames when adding delta
            // frames.
            frameSizeKbits -= _keyFrameSizeAvgKbits.Value();
        }
        else
        {
            // Shouldn't be negative, so zero is the lower bound.
            frameSizeKbits = 0;
        }
        if (_keyFrameRatio.Value() > 1e-5 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames)
        {
            // We are sending key frames more often than our upper bound for
            // how much we allow the key frame compensation to be spread
            // out in time. Therefor we must use the key frame ratio rather
            // than keyFrameSpreadFrames.
            _keyFrameCount = static_cast<WebRtc_Word32>(1 / _keyFrameRatio.Value() + 0.5);
        }
        else
        {
            // Compensate for the key frame the following frames
            _keyFrameCount = static_cast<WebRtc_Word32>(_keyFrameSpreadFrames + 0.5);
        }
    }
    else
    {
        // Decrease the keyFrameRatio
        _keyFrameRatio.Apply(1.0, 0.0);
    }
    // Change the level of the accumulator (bucket)
    _accumulator += frameSizeKbits;
    CapAccumulator();
}

void
VCMFrameDropper::Leak(WebRtc_UWord32 inputFrameRate)
{
    if (!_enabled)
    {
        return;
    }
    if (inputFrameRate < 1)
    {
        return;
    }
    if (_targetBitRate < 0.0f)
    {
        return;
    }
    _keyFrameSpreadFrames = 0.5f * inputFrameRate;
    // T is the expected bits per frame (target). If all frames were the same size,
    // we would get T bits per frame. Notice that T is also weighted to be able to
    // force a lower frame rate if wanted.
    float T = _targetBitRate / inputFrameRate;
    if (_keyFrameCount > 0)
    {
        // Perform the key frame compensation
        if (_keyFrameRatio.Value() > 0 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames)
        {
            T -= _keyFrameSizeAvgKbits.Value() * _keyFrameRatio.Value();
        }
        else
        {
            T -= _keyFrameSizeAvgKbits.Value() / _keyFrameSpreadFrames;
        }
        _keyFrameCount--;
    }
    _accumulator -= T;
    UpdateRatio();
}

void
VCMFrameDropper::UpdateNack(WebRtc_UWord32 nackBytes)
{
    if (!_enabled)
    {
        return;
    }
    _accumulator += static_cast<float>(nackBytes) * 8.0f / 1000.0f;
}

void
VCMFrameDropper::FillBucket(float inKbits, float outKbits)
{
    _accumulator += (inKbits - outKbits);
}

void
VCMFrameDropper::UpdateRatio()
{
    if (_accumulator > 1.3f * _accumulatorMax)
    {
        // Too far above accumulator max, react faster
        _dropRatio.UpdateBase(0.8f);
    }
    else
    {
        // Go back to normal reaction
        _dropRatio.UpdateBase(0.9f);
    }
    if (_accumulator > _accumulatorMax)
    {
        // We are above accumulator max, and should ideally
        // drop a frame. Increase the dropRatio and drop
        // the frame later.
        if (_wasBelowMax)
        {
            _dropNext = true;
        }
        if (_fastMode)
        {
            // always drop in aggressive mode
            _dropNext = true;
        }

        _dropRatio.Apply(1.0f, 1.0f);
        _dropRatio.UpdateBase(0.9f);
    }
    else
    {
        _dropRatio.Apply(1.0f, 0.0f);
    }
    if (_accumulator < 0.0f)
    {
        _accumulator = 0.0f;
    }
    _wasBelowMax = _accumulator < _accumulatorMax;
    WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId),  "FrameDropper: dropRatio = %f accumulator = %f, accumulatorMax = %f", _dropRatio.Value(), _accumulator, _accumulatorMax);
}

// This function signals when to drop frames to the caller. It makes use of the dropRatio
// to smooth out the drops over time.
bool
VCMFrameDropper::DropFrame()
{
    if (!_enabled)
    {
        return false;
    }
    if (_dropNext)
    {
        _dropNext = false;
        _dropCount = 0;
    }

    if (_dropRatio.Value() >= 0.5f) // Drops per keep
    {
        // limit is the number of frames we should drop between each kept frame
        // to keep our drop ratio. limit is positive in this case.
        float denom = 1.0f - _dropRatio.Value();
        if (denom < 1e-5)
        {
            denom = (float)1e-5;
        }
        WebRtc_Word32 limit = static_cast<WebRtc_Word32>(1.0f / denom - 1.0f + 0.5f);
        // Put a bound on the max amount of dropped frames between each kept
        // frame, in terms of frame rate and window size (secs).
        int max_limit = static_cast<int>(_incoming_frame_rate *
                                         _max_time_drops);
        if (limit > max_limit) {
          limit = max_limit;
        }
        if (_dropCount < 0)
        {
            // Reset the _dropCount since it was negative and should be positive.
            if (_dropRatio.Value() > 0.4f)
            {
                _dropCount = -_dropCount;
            }
            else
            {
                _dropCount = 0;
            }
        }
        if (_dropCount < limit)
        {
            // As long we are below the limit we should drop frames.
            _dropCount++;
            return true;
        }
        else
        {
            // Only when we reset _dropCount a frame should be kept.
            _dropCount = 0;
            return false;
        }
    }
    else if (_dropRatio.Value() > 0.0f && _dropRatio.Value() < 0.5f) // Keeps per drop
    {
        // limit is the number of frames we should keep between each drop
        // in order to keep the drop ratio. limit is negative in this case,
        // and the _dropCount is also negative.
        float denom = _dropRatio.Value();
        if (denom < 1e-5)
        {
            denom = (float)1e-5;
        }
        WebRtc_Word32 limit = -static_cast<WebRtc_Word32>(1.0f / denom - 1.0f + 0.5f);
        if (_dropCount > 0)
        {
            // Reset the _dropCount since we have a positive
            // _dropCount, and it should be negative.
            if (_dropRatio.Value() < 0.6f)
            {
                _dropCount = -_dropCount;
            }
            else
            {
                _dropCount = 0;
            }
        }
        if (_dropCount > limit)
        {
            if (_dropCount == 0)
            {
                // Drop frames when we reset _dropCount.
                _dropCount--;
                return true;
            }
            else
            {
                // Keep frames as long as we haven't reached limit.
                _dropCount--;
                return false;
            }
        }
        else
        {
            _dropCount = 0;
            return false;
        }
    }
    _dropCount = 0;
    return false;

    // A simpler version, unfiltered and quicker
    //bool dropNext = _dropNext;
    //_dropNext = false;
    //return dropNext;
}

void
VCMFrameDropper::SetRates(float bitRate, float incoming_frame_rate)
{
    // Bit rate of -1 means infinite bandwidth.
    _accumulatorMax = bitRate * _windowSize; // bitRate * windowSize (in seconds)
    if (_targetBitRate > 0.0f && bitRate < _targetBitRate && _accumulator > _accumulatorMax)
    {
        // Rescale the accumulator level if the accumulator max decreases
        _accumulator = bitRate / _targetBitRate * _accumulator;
    }
    _targetBitRate = bitRate;
    CapAccumulator();
    _incoming_frame_rate = incoming_frame_rate;
}

float
VCMFrameDropper::ActualFrameRate(WebRtc_UWord32 inputFrameRate) const
{
    if (!_enabled)
    {
        return static_cast<float>(inputFrameRate);
    }
    return inputFrameRate * (1.0f - _dropRatio.Value());
}

// Put a cap on the accumulator, i.e., don't let it grow beyond some level.
// This is a temporary fix for screencasting where very large frames from
// encoder will cause very slow response (too many frame drops).
void VCMFrameDropper::CapAccumulator() {
  float max_accumulator = _targetBitRate * _cap_buffer_size;
  if (_accumulator > max_accumulator) {
    _accumulator = max_accumulator;
  }
}

}
<|endoftext|>


<commit_before>// ======================================================================== //
// Copyright 2009-2015 Intel 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 "bvh4_builder_instancing.h"
#include "bvh4_statistics.h"
#include "../builders/bvh_builder_sah.h"
#include "../geometry/triangle4.h"

namespace embree
{
  namespace isa
  {
    BVH4BuilderInstancing::BVH4BuilderInstancing (BVH4* bvh, Scene* scene, const createTriangleMeshAccelTy createTriangleMeshAccel)
      : bvh(bvh), objects(bvh->objects), scene(scene), createTriangleMeshAccel(createTriangleMeshAccel), refs(scene->device), prims(scene->device),
        nextRef(0), numInstancedPrimitives(0) {}

    BVH4BuilderInstancing::~BVH4BuilderInstancing ()
    {
      for (size_t i=0; i<builders.size(); i++)
	delete builders[i];
    }

    void BVH4BuilderInstancing::build(size_t threadIndex, size_t threadCount)
    {
      /* delete some objects */
      size_t N = scene->size();
      if (N < objects.size()) {
        parallel_for(N, objects.size(), [&] (const range<size_t>& r) {
            for (size_t i=r.begin(); i<r.end(); i++) {
              delete builders[i]; builders[i] = nullptr;
              delete objects[i]; objects[i] = nullptr;
            }
          });
      }

      /* reset memory allocator */
      bvh->alloc.reset();

      /* skip build for empty scene */
      const size_t numPrimitives = scene->getNumPrimitives<TriangleMesh,1>();
      if (numPrimitives == 0) {
        prims.resize(0);
        bvh->set(BVH4::emptyNode,empty,0);
        return;
      }

      double t0 = bvh->preBuild(TOSTRING(isa) "::BVH4BuilderInstancing");

      /* resize object array if scene got larger */
      if (objects.size()  < N) objects.resize(N);
      if (builders.size() < N) builders.resize(N);
      if (refs.size()     < N) refs.resize(N);
      nextRef = 0;

      /* creation of acceleration structures */
      parallel_for(size_t(0), N, [&] (const range<size_t>& r) {
          for (size_t objectID=r.begin(); objectID<r.end(); objectID++)
          {
            TriangleMesh* mesh = scene->getTriangleMeshSafe(objectID);

            /* verify meshes got deleted properly */
            if (mesh == nullptr || mesh->numTimeSteps != 1) {
              assert(objectID < objects.size () && objects[objectID] == nullptr);
              assert(objectID < builders.size() && builders[objectID] == nullptr);
              continue;
            }

            /* delete BVH and builder for meshes that are scheduled for deletion */
            if (mesh->isErasing()) {
              delete builders[objectID]; builders[objectID] = nullptr;
              delete objects [objectID]; objects [objectID] = nullptr;
              continue;
            }

            /* create BVH and builder for new meshes */
            if (objects[objectID] == nullptr)
              createTriangleMeshAccel(mesh,(AccelData*&)objects[objectID],builders[objectID]);
          }
        });

      numInstancedPrimitives = 0;

      /* parallel build of acceleration structures */
      parallel_for(size_t(0), N, [&] (const range<size_t>& r) {
          for (size_t objectID=r.begin(); objectID<r.end(); objectID++)
          {
            /* ignore if no triangle mesh or not enabled */
            TriangleMesh* mesh = scene->getTriangleMeshSafe(objectID);
            if (mesh == nullptr || mesh->numTimeSteps != 1)
              continue;

            BVH4*    object  = objects [objectID]; assert(object);
            Builder* builder = builders[objectID]; assert(builder);

            /* build object if it got modified */
            if (mesh->isModified() && mesh->isUsed())
              builder->build(0,0);

            /* create build primitive */
            if (!object->bounds.empty() && mesh->isEnabled()) {
              refs[nextRef++] = BVH4BuilderInstancing::BuildRef(one,object->bounds,object->root);
              numInstancedPrimitives += mesh->size();
            }
          }
        });

      /* creates all instances */
      parallel_for(size_t(0), N, [&] (const range<size_t>& r) {
          for (size_t objectID=r.begin(); objectID<r.end(); objectID++)
          {
            /* ignore if no triangle mesh or not enabled */
            Geometry* geom = scene->get(objectID);
            if (geom->getType() != (Geometry::TRIANGLE_MESH | Geometry::INSTANCE))
              continue;

            GeometryInstance* instance = (GeometryInstance*) geom;
            if (!instance->isEnabled() || instance->numTimeSteps != 1)
              continue;

            BVH4*    object  = objects [objectID]; assert(object);

            /* create build primitive */
            if (!object->bounds.empty()) {
              refs[nextRef++] = BVH4BuilderInstancing::BuildRef(instance->local2world,object->bounds,object->root);
              numInstancedPrimitives += instance->geom->size();
            }
          }
        });

      /* fast path for single geometry scenes */
      /*if (nextRef == 1) {
        bvh->set(refs[0].node,refs[0].bounds(),numPrimitives);
        return;
        }*/

      /* open all large nodes */
      refs.resize(nextRef);
      open(numPrimitives);

      /* fast path for small geometries */
      /*if (refs.size() == 1) {
        bvh->set(refs[0].node,refs[0].bounds(),numPrimitives);
        return;
        }*/

      /* compute PrimRefs */
      prims.resize(refs.size());
      const PrimInfo pinfo = parallel_reduce(size_t(0), refs.size(), size_t(1024), PrimInfo(empty), [&] (const range<size_t>& r) -> PrimInfo {
          PrimInfo pinfo(empty);
          for (size_t i=r.begin(); i<r.end(); i++)
          {
            const BBox3fa bounds = refs[i].worldBounds();
            pinfo.add(bounds);
            prims[i] = PrimRef(bounds,(size_t)&refs[i]);
          }
          return pinfo;
        }, [] (const PrimInfo& a, const PrimInfo& b) { return PrimInfo::merge(a,b); });

      /* skip if all objects where empty */
      if (pinfo.size() == 0)
        bvh->set(BVH4::emptyNode,empty,0);

      /* otherwise build toplevel hierarchy */
      else
      {
        BVH4::NodeRef root;
        BVHBuilderBinnedSAH::build<BVH4::NodeRef>
          (root,
           [&] { return bvh->alloc.threadLocal2(); },
           [&] (const isa::BVHBuilderBinnedSAH::BuildRecord& current, BVHBuilderBinnedSAH::BuildRecord* children, const size_t N, FastAllocator::ThreadLocal2* alloc) -> int
          {
            BVH4::Node* node = (BVH4::Node*) alloc->alloc0.malloc(sizeof(BVH4::Node)); node->clear();
            for (size_t i=0; i<N; i++) {
              node->set(i,children[i].pinfo.geomBounds);
              children[i].parent = (size_t*)&node->child(i);
            }
            *current.parent = bvh->encodeNode(node);
            return 0;
          },
           [&] (const BVHBuilderBinnedSAH::BuildRecord& current, FastAllocator::ThreadLocal2* alloc) -> int
          {
            assert(current.prims.size() == 1);
            BuildRef* ref = (BuildRef*) prims[current.prims.begin()].ID();
            BVH4::TransformNode* node = (BVH4::TransformNode*) alloc->alloc0.malloc(sizeof(BVH4::TransformNode));
            new (node) BVH4::TransformNode(ref->local2world,ref->localBounds,ref->node); // FIXME: rcp should be precalculated somewhere
            *current.parent = BVH4::encodeNode(node);
            //*current.parent = ref->node;
            //((BVH4::NodeRef*)current.parent)->setBarrier();
            return 1;
          },
           [&] (size_t dn) { bvh->scene->progressMonitor(0); },
           prims.data(),pinfo,BVH4::N,BVH4::maxBuildDepthLeaf,4,1,1,1.0f,1.0f);

        bvh->set(root,pinfo.geomBounds,numPrimitives);
      }

      //bvh->root = collapse(bvh->root);

      bvh->alloc.cleanup();
      bvh->postBuild(t0);
    }

    void BVH4BuilderInstancing::clear()
    {
      for (size_t i=0; i<objects.size(); i++)
        if (objects[i]) objects[i]->clear();

      for (size_t i=0; i<builders.size(); i++)
	if (builders[i]) builders[i]->clear();

      refs.clear();
    }

    void set_primID(BVH4::NodeRef node, unsigned primID)
    {
      if (node.isLeaf())
      {
        size_t num;
        Triangle4* prims = (Triangle4*) node.leaf(num);
        for (size_t i=0; i<num; i++)
          for (size_t j=0; j<4; j++)
            if (prims[i].geomIDs[j] != -1)
              prims[i].primIDs[j] = primID;
      }
      else
      {
        BVH4::Node* n = node.node();
        for (size_t c=0; c<BVH4::N; c++)
          set_primID(n->child(c),primID);
      }
    }

    void BVH4BuilderInstancing::open(size_t numPrimitives)
    {
      if (refs.size() == 0)
	return;

      //size_t N = 0;
      //size_t N = numInstancedPrimitives/2000;
      size_t N = numInstancedPrimitives/200;
      //size_t N = numInstancedPrimitives;

      refs.reserve(N);

      std::make_heap(refs.begin(),refs.end());
      while (refs.size()+3 <= N)
      {
        std::pop_heap (refs.begin(),refs.end());
        BVH4::NodeRef ref = refs.back().node;
        const AffineSpace3fa local2world = refs.back().local2world;
        if (ref.isLeaf()) break;
        refs.pop_back();

        BVH4::Node* node = ref.node();
        for (size_t i=0; i<BVH4::N; i++) {
          if (node->child(i) == BVH4::emptyNode) continue;
          refs.push_back(BuildRef(local2world,node->bounds(i),node->child(i)));
          std::push_heap (refs.begin(),refs.end());
        }
      }

      //for (size_t i=0; i<refs.size(); i++)
      //  set_primID((BVH4::NodeRef) refs[i].node, i);
    }

    BVH4::NodeRef BVH4BuilderInstancing::collapse(BVH4::NodeRef& node)
    {
      if (node.isBarrier()) {
        node.clearBarrier();
        return node;
      }

      assert(node.isNode());
      BVH4::Node* n = node.node();
      BVH4::TransformNode* first = nullptr;
      for (size_t c=0; c<BVH4::N; c++) {
        BVH4::NodeRef child = n->child(c) = collapse(n->child(c));
        if (child.isTransformNode()) first = child.transformNode();
      }

      bool allEqual = true;
      for (size_t c=0; c<BVH4::N; c++)
      {
        BVH4::NodeRef child = n->child(c);
        if (child == BVH4::emptyNode) continue;

        if (!child.isTransformNode()) {
          allEqual = false;
          break;
        }

        if (child.transformNode()->world2local != first->world2local) {
          allEqual = false;
          break;
        }
      }

      if (!allEqual)
        return node;

      BBox3fa bounds = empty;
      for (size_t c=0; c<BVH4::N; c++) {
        if (n->child(c) == BVH4::emptyNode) continue;
        BVH4::TransformNode* child = n->child(c).transformNode();
        const BBox3fa cbounds = child->localBounds;
        n->set(c,cbounds,child->child);
        bounds.extend(cbounds);
      }
      first->localBounds = bounds;
      first->child = node;
      return BVH4::encodeNode(first);
    }

    Builder* BVH4BuilderInstancingSAH (void* bvh, Scene* scene, const createTriangleMeshAccelTy createTriangleMeshAccel) {
      return new BVH4BuilderInstancing((BVH4*)bvh,scene,createTriangleMeshAccel);
    }
  }
}
<commit_msg>collapsing works<commit_after>// ======================================================================== //
// Copyright 2009-2015 Intel 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 "bvh4_builder_instancing.h"
#include "bvh4_statistics.h"
#include "../builders/bvh_builder_sah.h"
#include "../geometry/triangle4.h"

namespace embree
{
  namespace isa
  {
    BVH4BuilderInstancing::BVH4BuilderInstancing (BVH4* bvh, Scene* scene, const createTriangleMeshAccelTy createTriangleMeshAccel)
      : bvh(bvh), objects(bvh->objects), scene(scene), createTriangleMeshAccel(createTriangleMeshAccel), refs(scene->device), prims(scene->device),
        nextRef(0), numInstancedPrimitives(0) {}

    BVH4BuilderInstancing::~BVH4BuilderInstancing ()
    {
      for (size_t i=0; i<builders.size(); i++)
	delete builders[i];
    }

    void BVH4BuilderInstancing::build(size_t threadIndex, size_t threadCount)
    {
      /* delete some objects */
      size_t N = scene->size();
      if (N < objects.size()) {
        parallel_for(N, objects.size(), [&] (const range<size_t>& r) {
            for (size_t i=r.begin(); i<r.end(); i++) {
              delete builders[i]; builders[i] = nullptr;
              delete objects[i]; objects[i] = nullptr;
            }
          });
      }

      /* reset memory allocator */
      bvh->alloc.reset();

      /* skip build for empty scene */
      const size_t numPrimitives = scene->getNumPrimitives<TriangleMesh,1>();
      if (numPrimitives == 0) {
        prims.resize(0);
        bvh->set(BVH4::emptyNode,empty,0);
        return;
      }

      double t0 = bvh->preBuild(TOSTRING(isa) "::BVH4BuilderInstancing");

      /* resize object array if scene got larger */
      if (objects.size()  < N) objects.resize(N);
      if (builders.size() < N) builders.resize(N);
      if (refs.size()     < N) refs.resize(N);
      nextRef = 0;

      /* creation of acceleration structures */
      parallel_for(size_t(0), N, [&] (const range<size_t>& r) {
          for (size_t objectID=r.begin(); objectID<r.end(); objectID++)
          {
            TriangleMesh* mesh = scene->getTriangleMeshSafe(objectID);

            /* verify meshes got deleted properly */
            if (mesh == nullptr || mesh->numTimeSteps != 1) {
              assert(objectID < objects.size () && objects[objectID] == nullptr);
              assert(objectID < builders.size() && builders[objectID] == nullptr);
              continue;
            }

            /* delete BVH and builder for meshes that are scheduled for deletion */
            if (mesh->isErasing()) {
              delete builders[objectID]; builders[objectID] = nullptr;
              delete objects [objectID]; objects [objectID] = nullptr;
              continue;
            }

            /* create BVH and builder for new meshes */
            if (objects[objectID] == nullptr)
              createTriangleMeshAccel(mesh,(AccelData*&)objects[objectID],builders[objectID]);
          }
        });

      numInstancedPrimitives = 0;

      /* parallel build of acceleration structures */
      parallel_for(size_t(0), N, [&] (const range<size_t>& r) {
          for (size_t objectID=r.begin(); objectID<r.end(); objectID++)
          {
            /* ignore if no triangle mesh or not enabled */
            TriangleMesh* mesh = scene->getTriangleMeshSafe(objectID);
            if (mesh == nullptr || mesh->numTimeSteps != 1)
              continue;

            BVH4*    object  = objects [objectID]; assert(object);
            Builder* builder = builders[objectID]; assert(builder);

            /* build object if it got modified */
            if (mesh->isModified() && mesh->isUsed())
              builder->build(0,0);

            /* create build primitive */
            if (!object->bounds.empty() && mesh->isEnabled()) {
              refs[nextRef++] = BVH4BuilderInstancing::BuildRef(one,object->bounds,object->root);
              numInstancedPrimitives += mesh->size();
            }
          }
        });

      /* creates all instances */
      parallel_for(size_t(0), N, [&] (const range<size_t>& r) {
          for (size_t objectID=r.begin(); objectID<r.end(); objectID++)
          {
            /* ignore if no triangle mesh or not enabled */
            Geometry* geom = scene->get(objectID);
            if (geom->getType() != (Geometry::TRIANGLE_MESH | Geometry::INSTANCE))
              continue;

            GeometryInstance* instance = (GeometryInstance*) geom;
            if (!instance->isEnabled() || instance->numTimeSteps != 1)
              continue;

            BVH4*    object  = objects [objectID]; assert(object);

            /* create build primitive */
            if (!object->bounds.empty()) {
              refs[nextRef++] = BVH4BuilderInstancing::BuildRef(instance->local2world,object->bounds,object->root);
              numInstancedPrimitives += instance->geom->size();
            }
          }
        });

      /* fast path for single geometry scenes */
      /*if (nextRef == 1) {
        bvh->set(refs[0].node,refs[0].bounds(),numPrimitives);
        return;
        }*/

      /* open all large nodes */
      refs.resize(nextRef);
      open(numPrimitives);

      /* fast path for small geometries */
      /*if (refs.size() == 1) {
        bvh->set(refs[0].node,refs[0].bounds(),numPrimitives);
        return;
        }*/

      /* compute PrimRefs */
      prims.resize(refs.size());
      const PrimInfo pinfo = parallel_reduce(size_t(0), refs.size(), size_t(1024), PrimInfo(empty), [&] (const range<size_t>& r) -> PrimInfo {
          PrimInfo pinfo(empty);
          for (size_t i=r.begin(); i<r.end(); i++)
          {
            const BBox3fa bounds = refs[i].worldBounds();
            pinfo.add(bounds);
            prims[i] = PrimRef(bounds,(size_t)&refs[i]);
          }
          return pinfo;
        }, [] (const PrimInfo& a, const PrimInfo& b) { return PrimInfo::merge(a,b); });

      /* skip if all objects where empty */
      if (pinfo.size() == 0)
        bvh->set(BVH4::emptyNode,empty,0);

      /* otherwise build toplevel hierarchy */
      else
      {
        BVH4::NodeRef root;
        BVHBuilderBinnedSAH::build<BVH4::NodeRef>
          (root,
           [&] { return bvh->alloc.threadLocal2(); },
           [&] (const isa::BVHBuilderBinnedSAH::BuildRecord& current, BVHBuilderBinnedSAH::BuildRecord* children, const size_t N, FastAllocator::ThreadLocal2* alloc) -> int
          {
            BVH4::Node* node = (BVH4::Node*) alloc->alloc0.malloc(sizeof(BVH4::Node)); node->clear();
            for (size_t i=0; i<N; i++) {
              node->set(i,children[i].pinfo.geomBounds);
              children[i].parent = (size_t*)&node->child(i);
            }
            *current.parent = bvh->encodeNode(node);
            return 0;
          },
           [&] (const BVHBuilderBinnedSAH::BuildRecord& current, FastAllocator::ThreadLocal2* alloc) -> int
          {
            assert(current.prims.size() == 1);
            BuildRef* ref = (BuildRef*) prims[current.prims.begin()].ID();
            BVH4::TransformNode* node = (BVH4::TransformNode*) alloc->alloc0.malloc(sizeof(BVH4::TransformNode));
            new (node) BVH4::TransformNode(ref->local2world,ref->localBounds,ref->node); // FIXME: rcp should be precalculated somewhere
            *current.parent = BVH4::encodeNode(node);
            //*current.parent = ref->node;
            ((BVH4::NodeRef*)current.parent)->setBarrier();
            return 1;
          },
           [&] (size_t dn) { bvh->scene->progressMonitor(0); },
           prims.data(),pinfo,BVH4::N,BVH4::maxBuildDepthLeaf,4,1,1,1.0f,1.0f);

        bvh->set(root,pinfo.geomBounds,numPrimitives);
      }

      bvh->root = collapse(bvh->root);

      bvh->alloc.cleanup();
      bvh->postBuild(t0);
    }

    void BVH4BuilderInstancing::clear()
    {
      for (size_t i=0; i<objects.size(); i++)
        if (objects[i]) objects[i]->clear();

      for (size_t i=0; i<builders.size(); i++)
	if (builders[i]) builders[i]->clear();

      refs.clear();
    }

    void set_primID(BVH4::NodeRef node, unsigned primID)
    {
      if (node.isLeaf())
      {
        size_t num;
        Triangle4* prims = (Triangle4*) node.leaf(num);
        for (size_t i=0; i<num; i++)
          for (size_t j=0; j<4; j++)
            if (prims[i].geomIDs[j] != -1)
              prims[i].primIDs[j] = primID;
      }
      else
      {
        BVH4::Node* n = node.node();
        for (size_t c=0; c<BVH4::N; c++)
          set_primID(n->child(c),primID);
      }
    }

    void BVH4BuilderInstancing::open(size_t numPrimitives)
    {
      if (refs.size() == 0)
	return;

      //size_t N = 0;
      //size_t N = numInstancedPrimitives/2000;
      size_t N = numInstancedPrimitives/200;
      //size_t N = numInstancedPrimitives;

      refs.reserve(N);

      std::make_heap(refs.begin(),refs.end());
      while (refs.size()+3 <= N)
      {
        std::pop_heap (refs.begin(),refs.end());
        BVH4::NodeRef ref = refs.back().node;
        const AffineSpace3fa local2world = refs.back().local2world;
        if (ref.isLeaf()) break;
        refs.pop_back();

        BVH4::Node* node = ref.node();
        for (size_t i=0; i<BVH4::N; i++) {
          if (node->child(i) == BVH4::emptyNode) continue;
          refs.push_back(BuildRef(local2world,node->bounds(i),node->child(i)));
          std::push_heap (refs.begin(),refs.end());
        }
      }

      //for (size_t i=0; i<refs.size(); i++)
      //  set_primID((BVH4::NodeRef) refs[i].node, i);
    }

    BVH4::NodeRef BVH4BuilderInstancing::collapse(BVH4::NodeRef& node)
    {
      if (node.isBarrier()) {
        node.clearBarrier();
        return node;
      }

      assert(node.isNode());
      BVH4::Node* n = node.node();
      BVH4::TransformNode* first = nullptr;
      for (size_t c=0; c<BVH4::N; c++) {
        if (n->child(c) == BVH4::emptyNode) continue;
        BVH4::NodeRef child = n->child(c) = collapse(n->child(c));
        if (child.isTransformNode()) first = child.transformNode();
      }

      bool allEqual = true;
      for (size_t c=0; c<BVH4::N; c++)
      {
        BVH4::NodeRef child = n->child(c);
        if (child == BVH4::emptyNode) continue;

        if (!child.isTransformNode()) {
          allEqual = false;
          break;
        }

        if (child.transformNode()->world2local != first->world2local) {
          allEqual = false;
          break;
        }
      }

      if (!allEqual)
        return node;

      BBox3fa bounds = empty;
      for (size_t c=0; c<BVH4::N; c++) {
        if (n->child(c) == BVH4::emptyNode) continue;
        BVH4::TransformNode* child = n->child(c).transformNode();
        const BBox3fa cbounds = child->localBounds;
        n->set(c,cbounds,child->child);
        bounds.extend(cbounds);
      }
      first->localBounds = bounds;
      first->child = node;
      return BVH4::encodeNode(first);
    }

    Builder* BVH4BuilderInstancingSAH (void* bvh, Scene* scene, const createTriangleMeshAccelTy createTriangleMeshAccel) {
      return new BVH4BuilderInstancing((BVH4*)bvh,scene,createTriangleMeshAccel);
    }
  }
}
<|endoftext|>