<commit_before>/*
 * Copyright (C) 2007-2013 German Aerospace Center (DLR/SC)
 *
 * Created: 2014-01-28 Mark Geiger <Mark.Geiger@dlr.de>
 *
 * 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 "TIGLViewerSelectWingAndFlapStatusDialog.h"
#include "ui_TIGLViewerSelectWingAndFlapStatusDialog.h"
#include "CCPACSConfigurationManager.h"
#include "CCPACSConfiguration.h"
#include "CCPACSTrailingEdgeDevice.h"
#include "CCPACSWingComponentSegment.h"
#include "CCPACSWing.h"
#include "generated/CPACSControlSurfaceStep.h"
#include "tiglmathfunctions.h"
#include "tiglcommonfunctions.h"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QSlider>
#include <QToolTip>
#include <QSpacerItem>
#include <QPushButton>
#include <QDoubleSpinBox>
#include <QSpacerItem>
#include <QTableWidget>
#include <QHeaderView>

namespace
{

class SignalsBlocker
{
public:
    SignalsBlocker(QObject* ptr):
    _ptr(ptr)
    {
        _b = ptr->blockSignals(true);
    }
    ~SignalsBlocker()
    {
        _ptr->blockSignals(_b);
    }

private:
    QObject* _ptr;
    bool _b;
};

double sliderToRelativeDeflect(const QSlider* slider, const QDoubleSpinBox* spinBox)
{

    double minSlider = static_cast<double>(slider->minimum());
    double maxSlider = static_cast<double>(slider->maximum());
    double valSlider = static_cast<double>(slider->value());

    double minDeflect = spinBox->minimum();
    double maxDeflect = spinBox->maximum();

    return (maxDeflect - minDeflect)/(maxSlider-minSlider) * (valSlider - minSlider) + minDeflect;
}

} // namespace

TIGLViewerSelectWingAndFlapStatusDialog::TIGLViewerSelectWingAndFlapStatusDialog(TIGLViewerDocument* document, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::TIGLViewerSelectWingAndFlapStatusDialog)
{
    ui->setupUi(this);
    this->setWindowTitle("Choose ControlSurface Deflections");
    _document = document;

}

TIGLViewerSelectWingAndFlapStatusDialog::~TIGLViewerSelectWingAndFlapStatusDialog()
{
    cleanup();
    delete ui;
}

void TIGLViewerSelectWingAndFlapStatusDialog::slider_value_changed(int /* k */)
{
    QSlider* slider = dynamic_cast<QSlider*>(QObject::sender());
    std::string uid = slider->windowTitle().toStdString();

    DeviceWidgets& elms = _guiMap.at(uid);
    double inputDeflection = sliderToRelativeDeflect(elms.slider, elms.deflectionBox);

    SignalsBlocker block(elms.deflectionBox);
    updateWidgets(uid, inputDeflection);
    _document->updateFlapTransform(uid);
}

void TIGLViewerSelectWingAndFlapStatusDialog::spinBox_value_changed(double inputDeflection)
{
    QDoubleSpinBox* spinBox = dynamic_cast<QDoubleSpinBox*>(QObject::sender());
    std::string uid = spinBox->windowTitle().toStdString();

    DeviceWidgets& elms = _guiMap.at(uid);

    SignalsBlocker block(elms.slider);
    updateWidgets(uid, inputDeflection);
    _document->updateFlapTransform(uid);
}

void TIGLViewerSelectWingAndFlapStatusDialog::cleanup()
{
    _guiMap.clear();
    return;
}

double TIGLViewerSelectWingAndFlapStatusDialog::getTrailingEdgeFlapValue( std::string uid )
{
    try {
        std::map< std::string, tigl::CCPACSTrailingEdgeDevice*>::iterator it;
        it = _deviceMap.find(uid);
        if (it == _deviceMap.end()) {
            throw tigl::CTiglError("getTrailingEdgeFlapValue: UID not found", TIGL_UID_ERROR);
        }
        tigl::CCPACSTrailingEdgeDevice* device = it->second;
        return device->GetDeflection();
    }
    catch(...) {
        return 0;
    }
}

void TIGLViewerSelectWingAndFlapStatusDialog::buildFlapRow(const tigl::CCPACSTrailingEdgeDevice& controlSurfaceDevice, int rowIdx, QTableWidget* gridLayout)
{

    QString uid = controlSurfaceDevice.GetUID().c_str();
    QLabel* labelUID = new QLabel(uid, this);
    QString transparentBG = "background-color: rgba(0, 0, 0, 0.0); padding-left: 6px; padding-right: 6px;";
    labelUID->setStyleSheet(transparentBG);

    gridLayout->setCellWidget(rowIdx, 0, labelUID);

    QSlider* slider = new QSlider(Qt::Horizontal);
    slider->setMaximum(1000);
    slider->setStyleSheet(transparentBG);

    gridLayout->setCellWidget(rowIdx, 1, slider);

    QDoubleSpinBox* spinBox = new QDoubleSpinBox();
    spinBox->setDecimals(3);
    spinBox->setSingleStep(0.005);
    spinBox->setStyleSheet(transparentBG);

    gridLayout->setCellWidget(rowIdx, 2, spinBox);

    QString rot;

    if ( controlSurfaceDevice.GetPath().GetSteps().GetSteps().size() > 0 ) {
        const auto& step = controlSurfaceDevice.GetPath().GetSteps().GetSteps().front();
        if(step) {
            rot.append(QString::number(step->GetHingeLineRotation().value_or(0.)));
        }
    }

    QLabel* labelRotation   = new QLabel(rot, this);
    labelRotation->setStyleSheet(transparentBG);

    gridLayout->setCellWidget(rowIdx, 3, labelRotation);

    double savedValue = controlSurfaceDevice.GetDeflection();

    double minDeflect = controlSurfaceDevice.GetMinDeflection();
    double maxDeflect = controlSurfaceDevice.GetMaxDeflection();

    int newSliderValue = static_cast<int>((slider->maximum() - slider->minimum()) / (maxDeflect-minDeflect) * (savedValue - minDeflect))
                        + slider->minimum();
    slider->setValue(newSliderValue);

    spinBox->setMinimum(minDeflect);
    spinBox->setValue(savedValue);
    spinBox->setMaximum(maxDeflect);

    DeviceWidgets elements;
    elements.slider = slider;
    elements.deflectionBox = spinBox;
    elements.rotAngleLabel = labelRotation;
    _guiMap[uid.toStdString()] = elements;

    slider->setWindowTitle( uid );
    spinBox->setWindowTitle( uid );

    connect(slider, SIGNAL(valueChanged(int)), this, SLOT(slider_value_changed(int)));
    connect(spinBox, SIGNAL(valueChanged(double)), this, SLOT(spinBox_value_changed(double)));
}

void TIGLViewerSelectWingAndFlapStatusDialog::drawGUI()
{
    cleanup();
    std::string wingUID = m_currentWing;

    if (wingUID.empty())
        return;

    tigl::CCPACSConfiguration& config = _document->GetConfiguration();
    tigl::CCPACSWing &wing = config.GetWing(wingUID);

    int noDevices = wing.GetComponentSegmentCount();
    std::vector<tigl::CCPACSTrailingEdgeDevice*> devices;

    for ( int i = 1; i <= wing.GetComponentSegmentCount(); i++ ) {
        tigl::CCPACSWingComponentSegment& componentSegment = static_cast<tigl::CCPACSWingComponentSegment&>(wing.GetComponentSegment(i));
        const auto& controlSurfaces = componentSegment.GetControlSurfaces();
        if (!controlSurfaces || controlSurfaces->ControlSurfaceCount() < 1) {
            noDevices--;
            if (noDevices < 1) {
                continue;
            }
        }

        // @todo: what if no TEDS? fix this
        for (  const auto&  controlSurfaceDevice : controlSurfaces->GetTrailingEdgeDevices()->GetTrailingEdgeDevices()) {
            if (!controlSurfaceDevice) {
                continue;
            }

            if ((!ui->checkTED->isChecked() && controlSurfaceDevice->GetType() == TRAILING_EDGE_DEVICE) ||
                (!ui->checkLED->isChecked() && controlSurfaceDevice->GetType() == LEADING_EDGE_DEVICE) ||
                (!ui->checkSpoiler->isChecked() && controlSurfaceDevice->GetType() == SPOILER)) {

                continue;
            }

            devices.push_back(controlSurfaceDevice.get());
        }

    }

    auto* tableWidget = new QTableWidget(static_cast<int>(devices.size()), 4);

    int rowIdx = 0;
    for (auto* device : devices) {
        buildFlapRow(*device, rowIdx++, tableWidget);

        _deviceMap[device->GetUID()] = device;
        updateWidgets(device->GetUID(), device->GetDeflection() );

    }

    // set style
    tableWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
    tableWidget->setAlternatingRowColors(true);
    tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    tableWidget->setHorizontalHeaderLabels(QStringList({"", "", "Deflection", "Rotation"}));
    tableWidget->verticalHeader()->hide();
    tableWidget->setStyleSheet("QHeaderView::section { border: 0px solid black}");

    ui->scrollArea->setWidget(tableWidget);
}

void TIGLViewerSelectWingAndFlapStatusDialog::on_checkTED_stateChanged(int)
{
   drawGUI();
}

void TIGLViewerSelectWingAndFlapStatusDialog::on_checkLED_stateChanged(int)
{
    drawGUI();
}

void TIGLViewerSelectWingAndFlapStatusDialog::on_checkSpoiler_stateChanged(int)
{
    drawGUI();
}

void TIGLViewerSelectWingAndFlapStatusDialog::updateWidgets(std::string controlSurfaceDeviceUID,
                                                            double inputDeflection)
{
    DeviceWidgets& elms = _guiMap.at(controlSurfaceDeviceUID);

    QString textVal;

    std::map< std::string, tigl::CCPACSTrailingEdgeDevice*>::iterator it;
    it = _deviceMap.find(controlSurfaceDeviceUID);
    if (it == _deviceMap.end()) {
        return;
    }
    tigl::CCPACSTrailingEdgeDevice* device = it->second;

    // Get rotation for current deflection value
    std::vector<double> relDeflections;
    std::vector<double> rotations;
    const tigl::CCPACSControlSurfaceSteps& steps = device->GetPath().GetSteps();
    for ( const auto &step : steps.GetSteps()) {
        if (!step) continue;
        relDeflections.push_back(step->GetRelDeflection());
        rotations.push_back(step->GetHingeLineRotation().value_or(0.));
    }
    double rotation = tigl::Interpolate(relDeflections, rotations, inputDeflection);
    QString textRot = QString::number(rotation);

    double factor = ( inputDeflection - device->GetMinDeflection()  ) / ( device->GetMaxDeflection() - device->GetMinDeflection() );
    textVal.append(QString::number(100 * factor));
    textVal.append("% ");

    int sliderVal = static_cast<int>(Mix(elms.slider->minimum(), elms.slider->maximum(), factor));

    elms.slider->setValue(sliderVal);
    elms.deflectionBox->setValue(inputDeflection);
    elms.rotAngleLabel->setText(textRot);

    device->SetDeflection(inputDeflection);
}
<commit_msg>Another compile fix for qt4<commit_after>/*
 * Copyright (C) 2007-2013 German Aerospace Center (DLR/SC)
 *
 * Created: 2014-01-28 Mark Geiger <Mark.Geiger@dlr.de>
 *
 * 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 "TIGLViewerSelectWingAndFlapStatusDialog.h"
#include "ui_TIGLViewerSelectWingAndFlapStatusDialog.h"
#include "CCPACSConfigurationManager.h"
#include "CCPACSConfiguration.h"
#include "CCPACSTrailingEdgeDevice.h"
#include "CCPACSWingComponentSegment.h"
#include "CCPACSWing.h"
#include "generated/CPACSControlSurfaceStep.h"
#include "tiglmathfunctions.h"
#include "tiglcommonfunctions.h"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QSlider>
#include <QToolTip>
#include <QSpacerItem>
#include <QPushButton>
#include <QDoubleSpinBox>
#include <QSpacerItem>
#include <QTableWidget>
#include <QHeaderView>
#include <QtGlobal>

namespace
{

class SignalsBlocker
{
public:
    SignalsBlocker(QObject* ptr):
    _ptr(ptr)
    {
        _b = ptr->blockSignals(true);
    }
    ~SignalsBlocker()
    {
        _ptr->blockSignals(_b);
    }

private:
    QObject* _ptr;
    bool _b;
};

double sliderToRelativeDeflect(const QSlider* slider, const QDoubleSpinBox* spinBox)
{

    double minSlider = static_cast<double>(slider->minimum());
    double maxSlider = static_cast<double>(slider->maximum());
    double valSlider = static_cast<double>(slider->value());

    double minDeflect = spinBox->minimum();
    double maxDeflect = spinBox->maximum();

    return (maxDeflect - minDeflect)/(maxSlider-minSlider) * (valSlider - minSlider) + minDeflect;
}

} // namespace

TIGLViewerSelectWingAndFlapStatusDialog::TIGLViewerSelectWingAndFlapStatusDialog(TIGLViewerDocument* document, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::TIGLViewerSelectWingAndFlapStatusDialog)
{
    ui->setupUi(this);
    this->setWindowTitle("Choose ControlSurface Deflections");
    _document = document;

}

TIGLViewerSelectWingAndFlapStatusDialog::~TIGLViewerSelectWingAndFlapStatusDialog()
{
    cleanup();
    delete ui;
}

void TIGLViewerSelectWingAndFlapStatusDialog::slider_value_changed(int /* k */)
{
    QSlider* slider = dynamic_cast<QSlider*>(QObject::sender());
    std::string uid = slider->windowTitle().toStdString();

    DeviceWidgets& elms = _guiMap.at(uid);
    double inputDeflection = sliderToRelativeDeflect(elms.slider, elms.deflectionBox);

    SignalsBlocker block(elms.deflectionBox);
    updateWidgets(uid, inputDeflection);
    _document->updateFlapTransform(uid);
}

void TIGLViewerSelectWingAndFlapStatusDialog::spinBox_value_changed(double inputDeflection)
{
    QDoubleSpinBox* spinBox = dynamic_cast<QDoubleSpinBox*>(QObject::sender());
    std::string uid = spinBox->windowTitle().toStdString();

    DeviceWidgets& elms = _guiMap.at(uid);

    SignalsBlocker block(elms.slider);
    updateWidgets(uid, inputDeflection);
    _document->updateFlapTransform(uid);
}

void TIGLViewerSelectWingAndFlapStatusDialog::cleanup()
{
    _guiMap.clear();
    return;
}

double TIGLViewerSelectWingAndFlapStatusDialog::getTrailingEdgeFlapValue( std::string uid )
{
    try {
        std::map< std::string, tigl::CCPACSTrailingEdgeDevice*>::iterator it;
        it = _deviceMap.find(uid);
        if (it == _deviceMap.end()) {
            throw tigl::CTiglError("getTrailingEdgeFlapValue: UID not found", TIGL_UID_ERROR);
        }
        tigl::CCPACSTrailingEdgeDevice* device = it->second;
        return device->GetDeflection();
    }
    catch(...) {
        return 0;
    }
}

void TIGLViewerSelectWingAndFlapStatusDialog::buildFlapRow(const tigl::CCPACSTrailingEdgeDevice& controlSurfaceDevice, int rowIdx, QTableWidget* gridLayout)
{

    QString uid = controlSurfaceDevice.GetUID().c_str();
    QLabel* labelUID = new QLabel(uid, this);
    QString transparentBG = "background-color: rgba(0, 0, 0, 0.0); padding-left: 6px; padding-right: 6px;";
    labelUID->setStyleSheet(transparentBG);

    gridLayout->setCellWidget(rowIdx, 0, labelUID);

    QSlider* slider = new QSlider(Qt::Horizontal);
    slider->setMaximum(1000);
    slider->setStyleSheet(transparentBG);

    gridLayout->setCellWidget(rowIdx, 1, slider);

    QDoubleSpinBox* spinBox = new QDoubleSpinBox();
    spinBox->setDecimals(3);
    spinBox->setSingleStep(0.005);
    spinBox->setStyleSheet(transparentBG);

    gridLayout->setCellWidget(rowIdx, 2, spinBox);

    QString rot;

    if ( controlSurfaceDevice.GetPath().GetSteps().GetSteps().size() > 0 ) {
        const auto& step = controlSurfaceDevice.GetPath().GetSteps().GetSteps().front();
        if(step) {
            rot.append(QString::number(step->GetHingeLineRotation().value_or(0.)));
        }
    }

    QLabel* labelRotation   = new QLabel(rot, this);
    labelRotation->setStyleSheet(transparentBG);

    gridLayout->setCellWidget(rowIdx, 3, labelRotation);

    double savedValue = controlSurfaceDevice.GetDeflection();

    double minDeflect = controlSurfaceDevice.GetMinDeflection();
    double maxDeflect = controlSurfaceDevice.GetMaxDeflection();

    int newSliderValue = static_cast<int>((slider->maximum() - slider->minimum()) / (maxDeflect-minDeflect) * (savedValue - minDeflect))
                        + slider->minimum();
    slider->setValue(newSliderValue);

    spinBox->setMinimum(minDeflect);
    spinBox->setValue(savedValue);
    spinBox->setMaximum(maxDeflect);

    DeviceWidgets elements;
    elements.slider = slider;
    elements.deflectionBox = spinBox;
    elements.rotAngleLabel = labelRotation;
    _guiMap[uid.toStdString()] = elements;

    slider->setWindowTitle( uid );
    spinBox->setWindowTitle( uid );

    connect(slider, SIGNAL(valueChanged(int)), this, SLOT(slider_value_changed(int)));
    connect(spinBox, SIGNAL(valueChanged(double)), this, SLOT(spinBox_value_changed(double)));
}

void TIGLViewerSelectWingAndFlapStatusDialog::drawGUI()
{
    cleanup();
    std::string wingUID = m_currentWing;

    if (wingUID.empty())
        return;

    tigl::CCPACSConfiguration& config = _document->GetConfiguration();
    tigl::CCPACSWing &wing = config.GetWing(wingUID);

    int noDevices = wing.GetComponentSegmentCount();
    std::vector<tigl::CCPACSTrailingEdgeDevice*> devices;

    for ( int i = 1; i <= wing.GetComponentSegmentCount(); i++ ) {
        tigl::CCPACSWingComponentSegment& componentSegment = static_cast<tigl::CCPACSWingComponentSegment&>(wing.GetComponentSegment(i));
        const auto& controlSurfaces = componentSegment.GetControlSurfaces();
        if (!controlSurfaces || controlSurfaces->ControlSurfaceCount() < 1) {
            noDevices--;
            if (noDevices < 1) {
                continue;
            }
        }

        // @todo: what if no TEDS? fix this
        for (  const auto&  controlSurfaceDevice : controlSurfaces->GetTrailingEdgeDevices()->GetTrailingEdgeDevices()) {
            if (!controlSurfaceDevice) {
                continue;
            }

            if ((!ui->checkTED->isChecked() && controlSurfaceDevice->GetType() == TRAILING_EDGE_DEVICE) ||
                (!ui->checkLED->isChecked() && controlSurfaceDevice->GetType() == LEADING_EDGE_DEVICE) ||
                (!ui->checkSpoiler->isChecked() && controlSurfaceDevice->GetType() == SPOILER)) {

                continue;
            }

            devices.push_back(controlSurfaceDevice.get());
        }

    }

    auto* tableWidget = new QTableWidget(static_cast<int>(devices.size()), 4);

    int rowIdx = 0;
    for (auto* device : devices) {
        buildFlapRow(*device, rowIdx++, tableWidget);

        _deviceMap[device->GetUID()] = device;
        updateWidgets(device->GetUID(), device->GetDeflection() );

    }

    // set style
    tableWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
    tableWidget->setAlternatingRowColors(true);
#if QT_VERSION >= 0x050000
    tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
#else
    tableWidget->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
#endif
    tableWidget->setHorizontalHeaderLabels(QStringList({"", "", "Deflection", "Rotation"}));
    tableWidget->verticalHeader()->hide();
    tableWidget->setStyleSheet("QHeaderView::section { border: 0px solid black}");

    ui->scrollArea->setWidget(tableWidget);
}

void TIGLViewerSelectWingAndFlapStatusDialog::on_checkTED_stateChanged(int)
{
   drawGUI();
}

void TIGLViewerSelectWingAndFlapStatusDialog::on_checkLED_stateChanged(int)
{
    drawGUI();
}

void TIGLViewerSelectWingAndFlapStatusDialog::on_checkSpoiler_stateChanged(int)
{
    drawGUI();
}

void TIGLViewerSelectWingAndFlapStatusDialog::updateWidgets(std::string controlSurfaceDeviceUID,
                                                            double inputDeflection)
{
    DeviceWidgets& elms = _guiMap.at(controlSurfaceDeviceUID);

    QString textVal;

    std::map< std::string, tigl::CCPACSTrailingEdgeDevice*>::iterator it;
    it = _deviceMap.find(controlSurfaceDeviceUID);
    if (it == _deviceMap.end()) {
        return;
    }
    tigl::CCPACSTrailingEdgeDevice* device = it->second;

    // Get rotation for current deflection value
    std::vector<double> relDeflections;
    std::vector<double> rotations;
    const tigl::CCPACSControlSurfaceSteps& steps = device->GetPath().GetSteps();
    for ( const auto &step : steps.GetSteps()) {
        if (!step) continue;
        relDeflections.push_back(step->GetRelDeflection());
        rotations.push_back(step->GetHingeLineRotation().value_or(0.));
    }
    double rotation = tigl::Interpolate(relDeflections, rotations, inputDeflection);
    QString textRot = QString::number(rotation);

    double factor = ( inputDeflection - device->GetMinDeflection()  ) / ( device->GetMaxDeflection() - device->GetMinDeflection() );
    textVal.append(QString::number(100 * factor));
    textVal.append("% ");

    int sliderVal = static_cast<int>(Mix(elms.slider->minimum(), elms.slider->maximum(), factor));

    elms.slider->setValue(sliderVal);
    elms.deflectionBox->setValue(inputDeflection);
    elms.rotAngleLabel->setText(textRot);

    device->SetDeflection(inputDeflection);
}
<|endoftext|>


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

// Interface header.
#include "orennayarbrdf.h"

// appleseed.renderer headers.
#include "renderer/modeling/bsdf/bsdf.h"
#include "renderer/modeling/bsdf/bsdfwrapper.h"

// appleseed.foundation headers.
#include "foundation/math/basis.h"
#include "foundation/math/sampling.h"
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/utility/containers/dictionary.h"
#include "foundation/utility/containers/specializedarrays.h"

// Standard headers.
#include <algorithm>
#include <cmath>

// Forward declarations.
namespace foundation    { class AbortSwitch; }
namespace renderer      { class Assembly; }
namespace renderer      { class Project; }

using namespace foundation;
using namespace std;

namespace renderer
{

namespace
{
    //
    // Oren-Nayar BRDF.
    //

    const char* Model = "orennayar_brdf";

    class OrenNayarBRDFImpl
      : public BSDF
    {
      public:
        OrenNayarBRDFImpl(
            const char*         name,
            const ParamArray&   params)
          : BSDF(name, Reflective, Diffuse, params)
        {
            m_inputs.declare("reflectance", InputFormatSpectralReflectance);
            m_inputs.declare("reflectance_multiplier", InputFormatScalar, "1.0");
            m_inputs.declare("roughness" , InputFormatScalar, "0.1");
        }

        virtual void release() OVERRIDE
        {
            delete this;
        }

        virtual const char* get_model() const OVERRIDE
        {
            return Model;
        }

        FORCE_INLINE virtual Mode sample(
            SamplingContext&    sampling_context,
            const void*         data,
            const bool          adjoint,
            const bool          cosine_mult,
            const Vector3d&     geometric_normal,
            const Basis3d&      shading_basis,
            const Vector3d&     outgoing,
            Vector3d&           incoming,
            Spectrum&           value,
            double&             probability) const
        {
            // No reflection below the shading surface.
            const Vector3d& n = shading_basis.get_normal();
            const double cos_on = min(dot(outgoing, n), 1.0);
            if (cos_on < 0.0)
                return Absorption;

            // Compute the incoming direction in local space.
            sampling_context.split_in_place(2, 1);
            const Vector2d s = sampling_context.next_vector2<2>();
            const Vector3d wi = sample_hemisphere_cosine(s);

            // Transform the incoming direction to parent space.
            incoming = shading_basis.transform_to_parent(wi);

            // No reflection below the shading surface.
            const double cos_in = dot(incoming, n);
            if (cos_in < 0.0)
                return Absorption;

            // Compute the BRDF value.
            const InputValues* values = static_cast<const InputValues*>(data);

            if (values->m_roughness != 0)
                oren_nayar_qualitative(cos_on, cos_in, values->m_roughness, values->m_reflectance, outgoing, incoming, n, value);
            else
                value = values->m_reflectance;

            value *= static_cast<float>(values->m_reflectance_multiplier * RcpPi);

            // Compute the probability density of the sampled direction.
            probability = wi.y * RcpPi;
            assert(probability > 0.0);

            // Return the scattering mode.
            return Diffuse;
        }

        FORCE_INLINE virtual double evaluate(
            const void*         data,
            const bool          adjoint,
            const bool          cosine_mult,
            const Vector3d&     geometric_normal,
            const Basis3d&      shading_basis,
            const Vector3d&     outgoing,
            const Vector3d&     incoming,
            const int           modes,
            Spectrum&           value) const
        {
            if (!(modes & Diffuse))
                return 0.0;

            // No reflection below the shading surface.
            const Vector3d& n = shading_basis.get_normal();
            const double cos_in = dot(incoming, n);
            const double cos_on = min(dot(outgoing, n), 1.0);
            if (cos_in < 0.0 || cos_on < 0.0)
                return 0.0;

            // Compute the BRDF value.
            const InputValues* values = static_cast<const InputValues*>(data);

            if (values->m_roughness != 0)
                oren_nayar_qualitative(cos_on, cos_in, values->m_roughness, values->m_reflectance, outgoing, incoming, n, value);
            else
                value = values->m_reflectance;

            value *= static_cast<float>(values->m_reflectance_multiplier * RcpPi );

            // Return the probability density of the sampled direction.
            return cos_in * RcpPi;
        }

        FORCE_INLINE virtual double evaluate_pdf(
            const void*         data,
            const Vector3d&     geometric_normal,
            const Basis3d&      shading_basis,
            const Vector3d&     outgoing,
            const Vector3d&     incoming,
            const int           modes) const
        {
            if (!(modes & Diffuse))
                return 0.0;

            // No reflection below the shading surface.
            const Vector3d& n = shading_basis.get_normal();
            const double cos_in = dot(incoming, n);
            const double cos_on = min(dot(outgoing, n), 1.0);
            if (cos_in < 0.0 || cos_on < 0.0)
                return 0.0;

            return cos_in * RcpPi;
        }

        void oren_nayar_qualitative(
            const double        cos_on,
            const double        cos_in,
            const double        roughness,
            const Spectrum&     reflectance,
            const Vector3d&     outgoing,
            const Vector3d&     incoming,
            const Vector3d&     n,
            Spectrum&           value) const
        {
            const double theta_r = min(HalfPi, acos(cos_on));
            const double theta_i = acos(cos_in);
            const double alpha = max(theta_i, theta_r);
            const double beta = min(theta_i, theta_r);

            const double sigma2 = square(roughness);

            const double C1 = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
            double C2 = 0.45 * sigma2 / (sigma2 + 0.09);

            const Vector3d V_perp_N = normalize(outgoing - n * dot(outgoing, n));

            const double cos_phi_diff = dot(V_perp_N, normalize(incoming - n * cos_in));

            if (cos_phi_diff >= 0.0)
                C2 *= sin(alpha);
            else
            {
                const double temp = 2.0 * beta * RcpPi;
                C2 *= sin(alpha) - square(temp) * temp;
            }

            const double C3 = 0.125 * (sigma2 / (sigma2 + 0.09) * square(4.0 * alpha * beta * RcpPiSq)) * tan((alpha + beta) * 0.5);

            value = reflectance ;
            value *= static_cast<float>(C1 + (cos_phi_diff * C2 * tan(beta)) + (1 - abs(cos_phi_diff)) * C3);
            value += square(reflectance) * static_cast<float>(0.17 * cos_in * (sigma2 / (sigma2 + 0.13)) *
                                                             (1 - cos_phi_diff * square(2 * beta * RcpPi)));
        }

      private:
        typedef OrenNayarBRDFInputValues InputValues;
    };

    typedef BSDFWrapper<OrenNayarBRDFImpl> OrenNayarBRDF;
}

//
// OrenNayarBRDFFactory class implementation.
//

const char* OrenNayarBRDFFactory::get_model() const
{
    return Model;
}

const char* OrenNayarBRDFFactory::get_human_readable_model() const
{
    return "Oren-Nayar BRDF";
}

DictionaryArray OrenNayarBRDFFactory::get_input_metadata() const
{
    DictionaryArray metadata;

    metadata.push_back(
        Dictionary()
            .insert("name", "reflectance")
            .insert("label", "Reflectance")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary()
                    .insert("color", "Colors")
                    .insert("texture_instance", "Textures"))
            .insert("use", "required")
            .insert("default", "0.5"));

    metadata.push_back(
        Dictionary()
            .insert("name", "reflectance_multiplier")
            .insert("label", "Reflectance Multiplier")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Textures"))
            .insert("use", "optional")
            .insert("default", "1.0"));

    metadata.push_back(
        Dictionary()
            .insert("name", "roughness")
            .insert("label", "Roughness")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Textures"))
            .insert("use", "required")
            .insert("default", "0.1"));

    return metadata;
}

auto_release_ptr<BSDF> OrenNayarBRDFFactory::create(
    const char*         name,
    const ParamArray&   params) const
{
    return auto_release_ptr<BSDF>(new OrenNayarBRDF(name, params));
}

}   // namespace renderer
<commit_msg>Fixed potential negative pdf values.<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2014 Luis B. Barrancos, The appleseedhq Organization
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "orennayarbrdf.h"

// appleseed.renderer headers.
#include "renderer/modeling/bsdf/bsdf.h"
#include "renderer/modeling/bsdf/bsdfwrapper.h"

// appleseed.foundation headers.
#include "foundation/math/basis.h"
#include "foundation/math/sampling.h"
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/utility/containers/dictionary.h"
#include "foundation/utility/containers/specializedarrays.h"

// Standard headers.
#include <algorithm>
#include <cmath>

// Forward declarations.
namespace foundation    { class AbortSwitch; }
namespace renderer      { class Assembly; }
namespace renderer      { class Project; }

using namespace foundation;
using namespace std;

namespace renderer
{

namespace
{
    //
    // Oren-Nayar BRDF.
    //

    const char* Model = "orennayar_brdf";

    class OrenNayarBRDFImpl
      : public BSDF
    {
      public:
        OrenNayarBRDFImpl(
            const char*         name,
            const ParamArray&   params)
          : BSDF(name, Reflective, Diffuse, params)
        {
            m_inputs.declare("reflectance", InputFormatSpectralReflectance);
            m_inputs.declare("reflectance_multiplier", InputFormatScalar, "1.0");
            m_inputs.declare("roughness" , InputFormatScalar, "0.1");
        }

        virtual void release() OVERRIDE
        {
            delete this;
        }

        virtual const char* get_model() const OVERRIDE
        {
            return Model;
        }

        FORCE_INLINE virtual Mode sample(
            SamplingContext&    sampling_context,
            const void*         data,
            const bool          adjoint,
            const bool          cosine_mult,
            const Vector3d&     geometric_normal,
            const Basis3d&      shading_basis,
            const Vector3d&     outgoing,
            Vector3d&           incoming,
            Spectrum&           value,
            double&             probability) const
        {
            // No reflection below the shading surface.
            const Vector3d& n = shading_basis.get_normal();
            const double cos_on = dot(outgoing, n);
            if (cos_on < 0.0)
                return Absorption;

            // Compute the incoming direction in local space.
            sampling_context.split_in_place(2, 1);
            const Vector2d s = sampling_context.next_vector2<2>();
            const Vector3d wi = sample_hemisphere_cosine(s);

            // Transform the incoming direction to parent space.
            incoming = shading_basis.transform_to_parent(wi);

            // No reflection below the shading surface.
            const double cos_in = dot(incoming, n);
            if (cos_in < 0.0)
                return Absorption;

            // Compute the BRDF value.
            const InputValues* values = static_cast<const InputValues*>(data);

            if (values->m_roughness != 0)
                oren_nayar_qualitative(cos_on, cos_in, values->m_roughness, values->m_reflectance, outgoing, incoming, n, value);
            else
                value = values->m_reflectance;

            value *= static_cast<float>(values->m_reflectance_multiplier * RcpPi);

            // Compute the probability density of the sampled direction.
            probability = wi.y * RcpPi;
            assert(probability > 0.0);

            // Return the scattering mode.
            return Diffuse;
        }

        FORCE_INLINE virtual double evaluate(
            const void*         data,
            const bool          adjoint,
            const bool          cosine_mult,
            const Vector3d&     geometric_normal,
            const Basis3d&      shading_basis,
            const Vector3d&     outgoing,
            const Vector3d&     incoming,
            const int           modes,
            Spectrum&           value) const
        {
            if (!(modes & Diffuse))
                return 0.0;

            // No reflection below the shading surface.
            const Vector3d& n = shading_basis.get_normal();
            const double cos_in = dot(incoming, n);
            const double cos_on = min(dot(outgoing, n), 1.0);
            if (cos_in < 0.0 || cos_on < 0.0)
                return 0.0;

            // Compute the BRDF value.
            const InputValues* values = static_cast<const InputValues*>(data);

            if (values->m_roughness != 0)
                oren_nayar_qualitative(cos_on, cos_in, values->m_roughness, values->m_reflectance, outgoing, incoming, n, value);
            else
                value = values->m_reflectance;

            value *= static_cast<float>(values->m_reflectance_multiplier * RcpPi );

            // Return the probability density of the sampled direction.
            return cos_in * RcpPi;
        }

        FORCE_INLINE virtual double evaluate_pdf(
            const void*         data,
            const Vector3d&     geometric_normal,
            const Basis3d&      shading_basis,
            const Vector3d&     outgoing,
            const Vector3d&     incoming,
            const int           modes) const
        {
            if (!(modes & Diffuse))
                return 0.0;

            // No reflection below the shading surface.
            const Vector3d& n = shading_basis.get_normal();
            const double cos_in = dot(incoming, n);
            const double cos_on = min(dot(outgoing, n), 1.0);
            if (cos_in < 0.0 || cos_on < 0.0)
                return 0.0;

            return cos_in * RcpPi;
        }

        void oren_nayar_qualitative(
            const double        cos_on,
            const double        cos_in,
            const double        roughness,
            const Spectrum&     reflectance,
            const Vector3d&     outgoing,
            const Vector3d&     incoming,
            const Vector3d&     n,
            Spectrum&           value) const
        {
            const double theta_r = min(HalfPi, acos(cos_on));
            const double theta_i = acos(cos_in);
            const double alpha = max(theta_i, theta_r);
            const double beta = min(theta_i, theta_r);

            const double sigma2 = square(roughness);

            const double C1 = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
            assert( C1 >= 0.0 );

            double C2 = 0.45 * sigma2 / (sigma2 + 0.09);

            const Vector3d V_perp_N = normalize(outgoing - n * dot(outgoing, n));

            const double cos_phi_diff = dot(V_perp_N, normalize(incoming - n * cos_in));

            if (cos_phi_diff >= 0.0)
                C2 *= sin(alpha);
            else
            {
                const double temp = 2.0 * beta * RcpPi;
                C2 *= sin(alpha) - square(temp) * temp;
            }
            assert( C2 >= 0.0);

            const double C3 = 0.125 * (sigma2 / (sigma2 + 0.09) * square(4.0 * alpha * beta * RcpPiSq)) * tan((alpha + beta) * 0.5);
            assert( C3 >= 0.0);

            value = reflectance ;
            value *= static_cast<float>(C1 + (abs(cos_phi_diff) * C2 * tan(beta)) + (1 - abs(cos_phi_diff)) * C3);
            value += square(reflectance) * static_cast<float>(0.17 * cos_in * (sigma2 / (sigma2 + 0.13)) *
                                                             (1 - cos_phi_diff * square(2 * beta * RcpPi)));

            assert(min_value(value) >= 0.0 );
        }

      private:
        typedef OrenNayarBRDFInputValues InputValues;
    };

    typedef BSDFWrapper<OrenNayarBRDFImpl> OrenNayarBRDF;
}

//
// OrenNayarBRDFFactory class implementation.
//

const char* OrenNayarBRDFFactory::get_model() const
{
    return Model;
}

const char* OrenNayarBRDFFactory::get_human_readable_model() const
{
    return "Oren-Nayar BRDF";
}

DictionaryArray OrenNayarBRDFFactory::get_input_metadata() const
{
    DictionaryArray metadata;

    metadata.push_back(
        Dictionary()
            .insert("name", "reflectance")
            .insert("label", "Reflectance")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary()
                    .insert("color", "Colors")
                    .insert("texture_instance", "Textures"))
            .insert("use", "required")
            .insert("default", "0.5"));

    metadata.push_back(
        Dictionary()
            .insert("name", "reflectance_multiplier")
            .insert("label", "Reflectance Multiplier")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Textures"))
            .insert("use", "optional")
            .insert("default", "1.0"));

    metadata.push_back(
        Dictionary()
            .insert("name", "roughness")
            .insert("label", "Roughness")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Textures"))
            .insert("use", "required")
            .insert("default", "0.1"));

    return metadata;
}

auto_release_ptr<BSDF> OrenNayarBRDFFactory::create(
    const char*         name,
    const ParamArray&   params) const
{
    return auto_release_ptr<BSDF>(new OrenNayarBRDF(name, params));
}

}   // namespace renderer
<|endoftext|>


<commit_before>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#include <ctime>
#include <functional>

#include <nix/valid/validator.hpp>
#include <nix/valid/checks.hpp>
#include <nix/valid/conditions.hpp>
#include <nix/valid/validate.hpp>

#include <nix.hpp>

#include "TestValidate.hpp"

using namespace nix;
using namespace valid;
using namespace std;

void TestValidate::setUp() {
    startup_time = time(NULL);
}

void TestValidate::tearDown() {
    return;
}

void TestValidate::test() {
    // dummy class to test empty checks
    class fooC {
    public:
        std::string getFoo () const { return std::string("I'm not empty!"); };
        std::string getBar () const { return std::string(); };
    };

    std::vector<std::string> vect = {"foo", "bar"};
    std::vector<std::string> vect2;
    fooC foobar;

    // NOTE: we can't test nix specific checks since nix & validation
    // structs cannot be included together
    valid::Result myResult = validator({
        could(vect, &std::vector<std::string>::empty, notFalse(), {
            must(vect, &std::vector<std::string>::size, notSmaller(2), "some msg") }),
        must(vect, &std::vector<std::string>::size, notSmaller(2), "some msg"),
        must(vect, &std::vector<std::string>::size, isSmaller(2), "some msg"),
        should(vect, &std::vector<std::string>::size, notGreater(2), "some msg"),
        should(vect, &std::vector<std::string>::size, isGreater(0), "some msg"),
        must(vect, &std::vector<std::string>::size, notEqual<size_t>(0), "some msg"),
        should(vect, &std::vector<std::string>::size, isEqual<size_t>(2), "some msg"),
        must(vect2, &std::vector<std::string>::size, isFalse(), "some msg"),
        must(foobar, &fooC::getFoo, notEmpty(), "some msg"),
        should(foobar, &fooC::getBar, isEmpty(), "some msg") })
    });

    CPPUNIT_ASSERT(myResult.hasWarnings() == false);
    CPPUNIT_ASSERT(myResult.hasErrors() == false);
}
<commit_msg>Fixed tiny bracket error in TestValidate<commit_after>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#include <ctime>
#include <functional>

#include <nix/valid/validator.hpp>
#include <nix/valid/checks.hpp>
#include <nix/valid/conditions.hpp>
#include <nix/valid/validate.hpp>

#include <nix.hpp>

#include "TestValidate.hpp"

using namespace nix;
using namespace valid;
using namespace std;

void TestValidate::setUp() {
    startup_time = time(NULL);
}

void TestValidate::tearDown() {
    return;
}

void TestValidate::test() {
    // dummy class to test empty checks
    class fooC {
    public:
        std::string getFoo () const { return std::string("I'm not empty!"); };
        std::string getBar () const { return std::string(); };
    };

    std::vector<std::string> vect = {"foo", "bar"};
    std::vector<std::string> vect2;
    fooC foobar;

    // NOTE: we can't test nix specific checks since nix & validation
    // structs cannot be included together
    valid::Result myResult = validator({
        could(vect, &std::vector<std::string>::empty, notFalse(), {
            must(vect, &std::vector<std::string>::size, notSmaller(2), "some msg") }),
        must(vect, &std::vector<std::string>::size, notSmaller(2), "some msg"),
        must(vect, &std::vector<std::string>::size, isSmaller(2), "some msg"),
        should(vect, &std::vector<std::string>::size, notGreater(2), "some msg"),
        should(vect, &std::vector<std::string>::size, isGreater(0), "some msg"),
        must(vect, &std::vector<std::string>::size, notEqual<size_t>(0), "some msg"),
        should(vect, &std::vector<std::string>::size, isEqual<size_t>(2), "some msg"),
        must(vect2, &std::vector<std::string>::size, isFalse(), "some msg"),
        must(foobar, &fooC::getFoo, notEmpty(), "some msg"),
        should(foobar, &fooC::getBar, isEmpty(), "some msg")
    });

    CPPUNIT_ASSERT(myResult.hasWarnings() == false);
    CPPUNIT_ASSERT(myResult.hasErrors() == false);
}
<|endoftext|>


<commit_before>#include <mlopen.h>
#include "test.hpp"
#include <vector>
#include <array>
#include <iterator>
#include <memory>
#include <mlopen/tensor_extra.hpp>

struct handle_fixture
{
    mlopenHandle_t handle;
#if MLOPEN_BACKEND_OPENCL
    cl_command_queue q;
#endif

    handle_fixture()
    {
        mlopenCreate(&handle);
#if MLOPEN_BACKEND_OPENCL
        mlopenGetStream(handle, &q);
#endif
    }

    ~handle_fixture()
    {
        mlopenDestroy(handle);
    }
};

struct input_tensor_fixture
{
    mlopenTensorDescriptor_t inputTensor;

    input_tensor_fixture()
    {
        STATUS(mlopenCreateTensorDescriptor(&inputTensor));
        STATUS(mlopenSet4dTensorDescriptor(
                inputTensor,
                mlopenFloat,
                100,
                32,
                8,
                8));

    }

    ~input_tensor_fixture()
    {
        mlopenDestroyTensorDescriptor(inputTensor);
    }

    void run()
    {
        int n, c, h, w;
        int nStride, cStride, hStride, wStride;
        mlopenDataType_t dt;

        STATUS(mlopenGet4dTensorDescriptor(
                inputTensor,
                &dt,
                &n,
                &c,
                &h,
                &w,
                &nStride,
                &cStride,
                &hStride,
                &wStride));

        EXPECT(dt == 1);
        EXPECT(n == 100);
        EXPECT(c == 32);
        EXPECT(h == 8);
        EXPECT(w == 8);
        EXPECT(nStride == c * cStride);
        EXPECT(cStride == h * hStride);
        EXPECT(hStride == w * wStride);
        EXPECT(wStride == 1);
    }
};

struct conv_filter_fixture : virtual handle_fixture
{
    mlopenTensorDescriptor_t convFilter;
    mlopenConvolutionDescriptor_t convDesc;

    static const mlopenConvolutionMode_t mode = mlopenConvolution;

    conv_filter_fixture()
    {
        STATUS(mlopenCreateTensorDescriptor(&convFilter));
        // weights
        STATUS(mlopenSet4dTensorDescriptor(
            convFilter,
            mlopenFloat,
            64,  // outputs
            32,   // inputs
            5,   // kernel size
            5));

        STATUS(mlopenCreateConvolutionDescriptor(&convDesc));
        // convolution with padding 2
        STATUS(mlopenInitConvolutionDescriptor(convDesc,
                mode,
                0,
                0,
                1,
                1,
                1,
                1));

    }
    ~conv_filter_fixture()
    {
        mlopenDestroyTensorDescriptor(convFilter);
        mlopenDestroyConvolutionDescriptor(convDesc);
    }

    void run()
    {
        // TODO: Update API to not require mode by pointer
        mlopenConvolutionMode_t lmode = mode;
        int pad_w, pad_h, u, v, upx, upy;
        STATUS(mlopenGetConvolutionDescriptor(convDesc,
                &lmode,
                &pad_h, &pad_w, &u, &v,
                &upx, &upy));

        EXPECT(mode == 0);
        EXPECT(pad_h == 0);
        EXPECT(pad_w == 0);
        EXPECT(u == 1);
        EXPECT(v == 1);
        EXPECT(upx == 1);
        EXPECT(upy == 1);
    }
};

struct output_tensor_fixture : conv_filter_fixture, input_tensor_fixture
{
    mlopenTensorDescriptor_t outputTensor;
    output_tensor_fixture()
    {
        int x, y, z, a;
        STATUS(mlopenGetConvolutionForwardOutputDim(convDesc, inputTensor, convFilter, &x, &y, &z, &a));

        STATUS(mlopenCreateTensorDescriptor(&outputTensor));

        STATUS(mlopenSet4dTensorDescriptor(
            outputTensor,
            mlopenFloat,
            x,
            y,
            z,
            a));
    }
    ~output_tensor_fixture()
    {
        mlopenDestroyTensorDescriptor(outputTensor);
    }

    void run()
    {
        int x, y, z, a;
        STATUS(mlopenGetConvolutionForwardOutputDim(convDesc, inputTensor, convFilter, &x, &y, &z, &a));

        EXPECT(x == 100);
        EXPECT(y == 64);
        EXPECT(z == 4);
        EXPECT(a == 4);
    }
};

template<bool Profile>
struct conv_forward : output_tensor_fixture
{
    void run()
    {
        STATUS(mlopenEnableProfiling(handle, Profile));
        int alpha = 1, beta = 1;
        STATUS(mlopenTransformTensor(handle,
                &alpha,
                inputTensor,
                NULL,
                &beta,
                convFilter,
                NULL));

        int value = 10;
        // STATUS(mlopenSetTensor(handle, inputTensor, NULL, &value));

        // STATUS(mlopenScaleTensor(handle, inputTensor, NULL, &alpha));

        // Setup OpenCL buffers

		int n, h, c, w;
		STATUS(mlopenGet4dTensorDescriptorLengths(inputTensor, &n, &c, &h, &w));
		size_t sz_in = n*c*h*w;

		STATUS(mlopenGet4dTensorDescriptorLengths(convFilter, &n, &c, &h, &w));
		size_t sz_wei = n*c*h*w;

		STATUS(mlopenGet4dTensorDescriptorLengths(outputTensor, &n, &c, &h, &w));
		size_t sz_out = n*c*h*w;

        std::vector<float> in(sz_in);
        std::vector<float> wei(sz_wei);
        std::vector<float> out(sz_out);

        for(int i = 0; i < sz_in; i++) {
            in[i] = rand() * (1.0 / RAND_MAX);
        }
        for (int i = 0; i < sz_wei; i++) {
            wei[i] = (double)(rand() * (1.0 / RAND_MAX) - 0.5) * 0.001;
        }

#if MLOPEN_BACKEND_OPENCL

        cl_context ctx;
        clGetCommandQueueInfo(q, CL_QUEUE_CONTEXT, sizeof(cl_context), &ctx, NULL);

        cl_int status = CL_SUCCESS;
		cl_mem in_dev = clCreateBuffer(ctx, CL_MEM_READ_ONLY, 4*sz_in,NULL, &status);
		cl_mem wei_dev = clCreateBuffer(ctx, CL_MEM_READ_ONLY, 4*sz_wei,NULL, NULL);
		cl_mem out_dev = clCreateBuffer(ctx, CL_MEM_READ_WRITE, 4*sz_out,NULL, NULL);

		status = clEnqueueWriteBuffer(q, in_dev, CL_TRUE, 0, 4*sz_in, in.data(), 0, NULL, NULL);
		status |= clEnqueueWriteBuffer(q, wei_dev, CL_TRUE, 0, 4*sz_wei, wei.data(), 0, NULL, NULL);
		status |= clEnqueueWriteBuffer(q, out_dev, CL_TRUE, 0, 4*sz_out, out.data(), 0, NULL, NULL);
		EXPECT(status == CL_SUCCESS);

#elif MLOPEN_BACKEND_HIP

        void * in_dev;
        void * wei_dev;
        void * out_dev;
        EXPECT(hipMalloc(&in_dev, 4*sz_in) == hipSuccess);
        EXPECT(hipMalloc(&wei_dev, 4*sz_wei) == hipSuccess);
        EXPECT(hipMalloc(&out_dev, 4*sz_out) == hipSuccess);

        EXPECT(hipMemcpy(in_dev, in.data(), 4*sz_in, hipMemcpyDeviceToHost) == hipSuccess);
        EXPECT(hipMemcpy(wei_dev, wei.data(), 4*sz_wei, hipMemcpyDeviceToHost) == hipSuccess);
        EXPECT(hipMemcpy(out_dev, out.data(), 4*sz_out, hipMemcpyDeviceToHost) == hipSuccess);

#endif

        int ret_algo_count;
        mlopenConvAlgoPerf_t perf;

        STATUS(mlopenFindConvolutionForwardAlgorithm(handle,
            inputTensor,
            in_dev,
            convFilter,
            wei_dev,
            convDesc,
            outputTensor,
            out_dev,
            1,
            &ret_algo_count,
            &perf,
            mlopenConvolutionFastest,
            NULL,
            10,
			0)); // MD: Not performing exhaustiveSearch by default for now

        STATUS(mlopenConvolutionForward(handle,
            &alpha,
            inputTensor,
            in_dev,
            convFilter,
            wei_dev,
            convDesc,
            mlopenConvolutionFwdAlgoDirect,
            &beta,
            outputTensor,
			out_dev,
            NULL,
            0));

        float time;
        STATUS(mlopenGetKernelTime(handle, &time));
        if (Profile)
        {
            CHECK(time > 0.0);
        }
        else
        {
            CHECK(time == 0.0);
        }
    }
};

int main() {
    run_test<input_tensor_fixture>();
    run_test<conv_filter_fixture>();
    run_test<output_tensor_fixture>();
    run_test<conv_forward<true>>();
    run_test<conv_forward<false>>();
}

<commit_msg>Fix copy direction<commit_after>#include <mlopen.h>
#include "test.hpp"
#include <vector>
#include <array>
#include <iterator>
#include <memory>
#include <mlopen/tensor_extra.hpp>

struct handle_fixture
{
    mlopenHandle_t handle;
#if MLOPEN_BACKEND_OPENCL
    cl_command_queue q;
#endif

    handle_fixture()
    {
        mlopenCreate(&handle);
#if MLOPEN_BACKEND_OPENCL
        mlopenGetStream(handle, &q);
#endif
    }

    ~handle_fixture()
    {
        mlopenDestroy(handle);
    }
};

struct input_tensor_fixture
{
    mlopenTensorDescriptor_t inputTensor;

    input_tensor_fixture()
    {
        STATUS(mlopenCreateTensorDescriptor(&inputTensor));
        STATUS(mlopenSet4dTensorDescriptor(
                inputTensor,
                mlopenFloat,
                100,
                32,
                8,
                8));

    }

    ~input_tensor_fixture()
    {
        mlopenDestroyTensorDescriptor(inputTensor);
    }

    void run()
    {
        int n, c, h, w;
        int nStride, cStride, hStride, wStride;
        mlopenDataType_t dt;

        STATUS(mlopenGet4dTensorDescriptor(
                inputTensor,
                &dt,
                &n,
                &c,
                &h,
                &w,
                &nStride,
                &cStride,
                &hStride,
                &wStride));

        EXPECT(dt == 1);
        EXPECT(n == 100);
        EXPECT(c == 32);
        EXPECT(h == 8);
        EXPECT(w == 8);
        EXPECT(nStride == c * cStride);
        EXPECT(cStride == h * hStride);
        EXPECT(hStride == w * wStride);
        EXPECT(wStride == 1);
    }
};

struct conv_filter_fixture : virtual handle_fixture
{
    mlopenTensorDescriptor_t convFilter;
    mlopenConvolutionDescriptor_t convDesc;

    static const mlopenConvolutionMode_t mode = mlopenConvolution;

    conv_filter_fixture()
    {
        STATUS(mlopenCreateTensorDescriptor(&convFilter));
        // weights
        STATUS(mlopenSet4dTensorDescriptor(
            convFilter,
            mlopenFloat,
            64,  // outputs
            32,   // inputs
            5,   // kernel size
            5));

        STATUS(mlopenCreateConvolutionDescriptor(&convDesc));
        // convolution with padding 2
        STATUS(mlopenInitConvolutionDescriptor(convDesc,
                mode,
                0,
                0,
                1,
                1,
                1,
                1));

    }
    ~conv_filter_fixture()
    {
        mlopenDestroyTensorDescriptor(convFilter);
        mlopenDestroyConvolutionDescriptor(convDesc);
    }

    void run()
    {
        // TODO: Update API to not require mode by pointer
        mlopenConvolutionMode_t lmode = mode;
        int pad_w, pad_h, u, v, upx, upy;
        STATUS(mlopenGetConvolutionDescriptor(convDesc,
                &lmode,
                &pad_h, &pad_w, &u, &v,
                &upx, &upy));

        EXPECT(mode == 0);
        EXPECT(pad_h == 0);
        EXPECT(pad_w == 0);
        EXPECT(u == 1);
        EXPECT(v == 1);
        EXPECT(upx == 1);
        EXPECT(upy == 1);
    }
};

struct output_tensor_fixture : conv_filter_fixture, input_tensor_fixture
{
    mlopenTensorDescriptor_t outputTensor;
    output_tensor_fixture()
    {
        int x, y, z, a;
        STATUS(mlopenGetConvolutionForwardOutputDim(convDesc, inputTensor, convFilter, &x, &y, &z, &a));

        STATUS(mlopenCreateTensorDescriptor(&outputTensor));

        STATUS(mlopenSet4dTensorDescriptor(
            outputTensor,
            mlopenFloat,
            x,
            y,
            z,
            a));
    }
    ~output_tensor_fixture()
    {
        mlopenDestroyTensorDescriptor(outputTensor);
    }

    void run()
    {
        int x, y, z, a;
        STATUS(mlopenGetConvolutionForwardOutputDim(convDesc, inputTensor, convFilter, &x, &y, &z, &a));

        EXPECT(x == 100);
        EXPECT(y == 64);
        EXPECT(z == 4);
        EXPECT(a == 4);
    }
};

template<bool Profile>
struct conv_forward : output_tensor_fixture
{
    void run()
    {
        STATUS(mlopenEnableProfiling(handle, Profile));
        int alpha = 1, beta = 1;
        STATUS(mlopenTransformTensor(handle,
                &alpha,
                inputTensor,
                NULL,
                &beta,
                convFilter,
                NULL));

        int value = 10;
        // STATUS(mlopenSetTensor(handle, inputTensor, NULL, &value));

        // STATUS(mlopenScaleTensor(handle, inputTensor, NULL, &alpha));

        // Setup OpenCL buffers

		int n, h, c, w;
		STATUS(mlopenGet4dTensorDescriptorLengths(inputTensor, &n, &c, &h, &w));
		size_t sz_in = n*c*h*w;

		STATUS(mlopenGet4dTensorDescriptorLengths(convFilter, &n, &c, &h, &w));
		size_t sz_wei = n*c*h*w;

		STATUS(mlopenGet4dTensorDescriptorLengths(outputTensor, &n, &c, &h, &w));
		size_t sz_out = n*c*h*w;

        std::vector<float> in(sz_in);
        std::vector<float> wei(sz_wei);
        std::vector<float> out(sz_out);

        for(int i = 0; i < sz_in; i++) {
            in[i] = rand() * (1.0 / RAND_MAX);
        }
        for (int i = 0; i < sz_wei; i++) {
            wei[i] = (double)(rand() * (1.0 / RAND_MAX) - 0.5) * 0.001;
        }

#if MLOPEN_BACKEND_OPENCL

        cl_context ctx;
        clGetCommandQueueInfo(q, CL_QUEUE_CONTEXT, sizeof(cl_context), &ctx, NULL);

        cl_int status = CL_SUCCESS;
		cl_mem in_dev = clCreateBuffer(ctx, CL_MEM_READ_ONLY, 4*sz_in,NULL, &status);
		cl_mem wei_dev = clCreateBuffer(ctx, CL_MEM_READ_ONLY, 4*sz_wei,NULL, NULL);
		cl_mem out_dev = clCreateBuffer(ctx, CL_MEM_READ_WRITE, 4*sz_out,NULL, NULL);

		status = clEnqueueWriteBuffer(q, in_dev, CL_TRUE, 0, 4*sz_in, in.data(), 0, NULL, NULL);
		status |= clEnqueueWriteBuffer(q, wei_dev, CL_TRUE, 0, 4*sz_wei, wei.data(), 0, NULL, NULL);
		status |= clEnqueueWriteBuffer(q, out_dev, CL_TRUE, 0, 4*sz_out, out.data(), 0, NULL, NULL);
		EXPECT(status == CL_SUCCESS);

#elif MLOPEN_BACKEND_HIP

        void * in_dev;
        void * wei_dev;
        void * out_dev;
        EXPECT(hipMalloc(&in_dev, 4*sz_in) == hipSuccess);
        EXPECT(hipMalloc(&wei_dev, 4*sz_wei) == hipSuccess);
        EXPECT(hipMalloc(&out_dev, 4*sz_out) == hipSuccess);

        EXPECT(hipMemcpy(in_dev, in.data(), 4*sz_in, hipMemcpyHostToDevice) == hipSuccess);
        EXPECT(hipMemcpy(wei_dev, wei.data(), 4*sz_wei, hipMemcpyHostToDevice) == hipSuccess);
        EXPECT(hipMemcpy(out_dev, out.data(), 4*sz_out, hipMemcpyHostToDevice) == hipSuccess);

#endif

        int ret_algo_count;
        mlopenConvAlgoPerf_t perf;

        STATUS(mlopenFindConvolutionForwardAlgorithm(handle,
            inputTensor,
            in_dev,
            convFilter,
            wei_dev,
            convDesc,
            outputTensor,
            out_dev,
            1,
            &ret_algo_count,
            &perf,
            mlopenConvolutionFastest,
            NULL,
            10,
			0)); // MD: Not performing exhaustiveSearch by default for now

        STATUS(mlopenConvolutionForward(handle,
            &alpha,
            inputTensor,
            in_dev,
            convFilter,
            wei_dev,
            convDesc,
            mlopenConvolutionFwdAlgoDirect,
            &beta,
            outputTensor,
			out_dev,
            NULL,
            0));

        float time;
        STATUS(mlopenGetKernelTime(handle, &time));
        if (Profile)
        {
            CHECK(time > 0.0);
        }
        else
        {
            CHECK(time == 0.0);
        }

        // Potential memory leak free memory at end of function
#if MLOPEN_BACKEND_OPENCL

#elif MLOPEN_BACKEND_HIP
        hipFree(in_dev);
        hipFree(wei_dev);
        hipFree(out_dev);
#endif
    }
};

int main() {
    run_test<input_tensor_fixture>();
    run_test<conv_filter_fixture>();
    run_test<output_tensor_fixture>();
    run_test<conv_forward<true>>();
    run_test<conv_forward<false>>();
}

<|endoftext|>


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

// Interface header.
#include "curveobject.h"

// appleseed.renderer headers.
#include "renderer/global/globallogger.h"
#include "renderer/utility/paramarray.h"

// appleseed.foundation headers.
#include "foundation/math/sampling/mappings.h"
#include "foundation/math/aabb.h"
#include "foundation/math/qmc.h"
#include "foundation/math/rng.h"
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/platform/defaulttimers.h"
#include "foundation/utility/searchpaths.h"
#include "foundation/utility/stopwatch.h"
#include "foundation/utility/string.h"

// Standard headers.
#include <cassert>
#include <cmath>
#include <fstream>
#include <string>
#include <vector>

using namespace foundation;
using namespace std;

namespace renderer
{

//
// CurveObject class implementation.
//

struct CurveObject::Impl
{
    RegionKit               m_region_kit;
    Lazy<RegionKit>         m_lazy_region_kit;
    vector<BezierCurve3d>   m_curves;
    vector<string>          m_material_slots;

    Impl()
      : m_lazy_region_kit(&m_region_kit)
    {
    }

    GAABB3 compute_bounds() const
    {
        AABB3d bbox;
        bbox.invalidate();

        const size_t curve_count = m_curves.size();

        for (size_t i = 0; i < curve_count; ++i)
            bbox.insert(m_curves[i].compute_bbox());

        return bbox;
    }

    template <typename RNG>
    static Vector2d rand_vector2d(RNG& rng)
    {
        Vector2d v;
        v[0] = rand_double2(rng);
        v[1] = rand_double2(rng);
        return v;
    }

    void create_hair_ball(const ParamArray& params)
    {
        const size_t ControlPointCount = 4;

        const size_t curve_count = params.get_optional<size_t>("curves", 100);
        const double curve_width = params.get_optional<double>("width", 0.002);

        Vector3d points[ControlPointCount];
        MersenneTwister rng;

        m_curves.reserve(curve_count);

        for (size_t c = 0; c < curve_count; ++c)
        {
            for (size_t p = 0; p < ControlPointCount; ++p)
            {
                // http://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability
                const double r = pow(1.0 - rand_double2(rng), 1.0 / 3);
                const Vector3d d = sample_sphere_uniform(rand_vector2d(rng));
                points[p] = r * d;
            }

            const BezierCurve3d curve(&points[0], curve_width);
            m_curves.push_back(curve);
        }
    }

    void create_furry_ball(const ParamArray& params)
    {
        const size_t ControlPointCount = 4;

        const size_t curve_count = params.get_optional<size_t>("curves", 100);
        const double curve_length = params.get_optional<double>("length", 0.1);
        const double base_width = params.get_optional<double>("base_width", 0.001);
        const double tip_width = params.get_optional<double>("tip_width", 0.0001);
        const double length_fuzziness = params.get_optional<double>("length_fuzziness", 0.3);
        const double curliness = params.get_optional<double>("curliness", 0.5);

        Vector3d points[ControlPointCount];
        double widths[ControlPointCount];

        MersenneTwister rng;

        m_curves.reserve(curve_count);

        for (size_t c = 0; c < curve_count; ++c)
        {
            static const size_t Bases[] = { 2 };
            const Vector2d s = hammersley_sequence<double, 2>(Bases, c, curve_count);
            const Vector3d d = sample_sphere_uniform(s);
            points[0] = d;
            widths[0] = base_width;

            const double f = rand_double1(rng, -length_fuzziness, +length_fuzziness);
            const double length = curve_length * (1.0 + f);

            for (size_t p = 1; p < ControlPointCount; ++p)
            {
                const double r = static_cast<double>(p) / (ControlPointCount - 1);
                const Vector3d f = curliness * sample_sphere_uniform(rand_vector2d(rng));
                points[p] = points[0] + length * (r * d + f);
                widths[p] = lerp(base_width, tip_width, r);
            }

            const BezierCurve3d curve(&points[0], &widths[0]);
            m_curves.push_back(curve);
        }
    }

    void load_curve_file(const char* filepath)
    {
        const double CurveWidth = 0.009;

        ifstream input;
        input.open(filepath);

        if (input.good())
        {
            Stopwatch<DefaultWallclockTimer> stopwatch;
            stopwatch.start();

            // Read the number of curves.
            size_t curve_count;
            input >> curve_count;

            // Read the number of control points per curve.
            size_t control_point_count;
            input >> control_point_count;

            vector<Vector3d> points(control_point_count);

            m_curves.reserve(curve_count);

            for (size_t c = 0; c < curve_count; ++c)
            {
                for (size_t p = 0; p < control_point_count; ++p)
                {
                    Vector3d point;
                    input >> point.x >> point.y >> point.z;
                    points[p] = point;
                }

                const BezierCurve3d curve(&points[0], CurveWidth);
                m_curves.push_back(curve);
            }

            stopwatch.measure();

            RENDERER_LOG_INFO(
                "loaded curve file %s (%s curves) in %s.",
                filepath,
                pretty_uint(curve_count).c_str(),
                pretty_time(stopwatch.get_seconds()).c_str());

            input.close();
        }
        else
        {
            RENDERER_LOG_ERROR("failed to load curve file %s.", filepath);
        }
    }
};

CurveObject::CurveObject(
    const SearchPaths&  search_paths,
    const char*         name,
    const ParamArray&   params)
  : Object(name, params)
  , impl(new Impl())
{
    const string filepath = params.get<string>("filepath");

    if (filepath == "builtin:hairball")
        impl->create_hair_ball(params);
    else if (filepath == "builtin:furryball")
        impl->create_furry_ball(params);
    else
    {
        impl->load_curve_file(
            search_paths.qualify(filepath).c_str());
    }
}

CurveObject::~CurveObject()
{
    delete impl;
}

void CurveObject::release()
{
    delete this;
}

const char* CurveObject::get_model() const
{
    return CurveObjectFactory::get_model();
}

GAABB3 CurveObject::compute_local_bbox() const
{
    return impl->compute_bounds();
}

Lazy<RegionKit>& CurveObject::get_region_kit()
{
    return impl->m_lazy_region_kit;
}

size_t CurveObject::get_material_slot_count() const
{
    return impl->m_material_slots.size();
}

const char* CurveObject::get_material_slot(const size_t index) const
{
    return impl->m_material_slots[index].c_str();
}

size_t CurveObject::get_curve_count() const
{
    return impl->m_curves.size();
}

const BezierCurve3d& CurveObject::get_curve(const size_t index) const
{
    assert(index < impl->m_curves.size());
    return impl->m_curves[index];
}

//
// CurveObjectFactory class implementation.
//

const char* CurveObjectFactory::get_model()
{
    return "curve_object";
}

auto_release_ptr<CurveObject> CurveObjectFactory::create(
    const SearchPaths&  search_paths,
    const char*         name,
    const ParamArray&   params)
{
    return
        auto_release_ptr<CurveObject>(
            new CurveObject(search_paths, name, params));
}

}   // namespace renderer
<commit_msg>support per-control point width in temporary curve file format.<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2014 Srinath Ravichandran, The appleseedhq Organization
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "curveobject.h"

// appleseed.renderer headers.
#include "renderer/global/globallogger.h"
#include "renderer/utility/paramarray.h"

// appleseed.foundation headers.
#include "foundation/math/sampling/mappings.h"
#include "foundation/math/aabb.h"
#include "foundation/math/qmc.h"
#include "foundation/math/rng.h"
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/platform/defaulttimers.h"
#include "foundation/utility/searchpaths.h"
#include "foundation/utility/stopwatch.h"
#include "foundation/utility/string.h"

// Standard headers.
#include <cassert>
#include <cmath>
#include <fstream>
#include <string>
#include <vector>

using namespace foundation;
using namespace std;

namespace renderer
{

//
// CurveObject class implementation.
//

struct CurveObject::Impl
{
    RegionKit               m_region_kit;
    Lazy<RegionKit>         m_lazy_region_kit;
    vector<BezierCurve3d>   m_curves;
    vector<string>          m_material_slots;

    Impl()
      : m_lazy_region_kit(&m_region_kit)
    {
    }

    GAABB3 compute_bounds() const
    {
        AABB3d bbox;
        bbox.invalidate();

        const size_t curve_count = m_curves.size();

        for (size_t i = 0; i < curve_count; ++i)
            bbox.insert(m_curves[i].compute_bbox());

        return bbox;
    }

    template <typename RNG>
    static Vector2d rand_vector2d(RNG& rng)
    {
        Vector2d v;
        v[0] = rand_double2(rng);
        v[1] = rand_double2(rng);
        return v;
    }

    void create_hair_ball(const ParamArray& params)
    {
        const size_t ControlPointCount = 4;

        const size_t curve_count = params.get_optional<size_t>("curves", 100);
        const double curve_width = params.get_optional<double>("width", 0.002);

        Vector3d points[ControlPointCount];
        MersenneTwister rng;

        m_curves.reserve(curve_count);

        for (size_t c = 0; c < curve_count; ++c)
        {
            for (size_t p = 0; p < ControlPointCount; ++p)
            {
                // http://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability
                const double r = pow(1.0 - rand_double2(rng), 1.0 / 3);
                const Vector3d d = sample_sphere_uniform(rand_vector2d(rng));
                points[p] = r * d;
            }

            const BezierCurve3d curve(&points[0], curve_width);
            m_curves.push_back(curve);
        }
    }

    void create_furry_ball(const ParamArray& params)
    {
        const size_t ControlPointCount = 4;

        const size_t curve_count = params.get_optional<size_t>("curves", 100);
        const double curve_length = params.get_optional<double>("length", 0.1);
        const double base_width = params.get_optional<double>("base_width", 0.001);
        const double tip_width = params.get_optional<double>("tip_width", 0.0001);
        const double length_fuzziness = params.get_optional<double>("length_fuzziness", 0.3);
        const double curliness = params.get_optional<double>("curliness", 0.5);

        Vector3d points[ControlPointCount];
        double widths[ControlPointCount];

        MersenneTwister rng;

        m_curves.reserve(curve_count);

        for (size_t c = 0; c < curve_count; ++c)
        {
            static const size_t Bases[] = { 2 };
            const Vector2d s = hammersley_sequence<double, 2>(Bases, c, curve_count);
            const Vector3d d = sample_sphere_uniform(s);
            points[0] = d;
            widths[0] = base_width;

            const double f = rand_double1(rng, -length_fuzziness, +length_fuzziness);
            const double length = curve_length * (1.0 + f);

            for (size_t p = 1; p < ControlPointCount; ++p)
            {
                const double r = static_cast<double>(p) / (ControlPointCount - 1);
                const Vector3d f = curliness * sample_sphere_uniform(rand_vector2d(rng));
                points[p] = points[0] + length * (r * d + f);
                widths[p] = lerp(base_width, tip_width, r);
            }

            const BezierCurve3d curve(&points[0], &widths[0]);
            m_curves.push_back(curve);
        }
    }

    void load_curve_file(const char* filepath)
    {
        ifstream input;
        input.open(filepath);

        if (input.good())
        {
            Stopwatch<DefaultWallclockTimer> stopwatch;
            stopwatch.start();

            // Read the number of curves.
            size_t curve_count;
            input >> curve_count;

            // Read the number of control points per curve.
            size_t control_point_count;
            input >> control_point_count;

            if (control_point_count != 4)
            {
                RENDERER_LOG_ERROR(
                    "while loading curve file %s: only curves with 4 control points are currently supported.",
                    filepath);
                return;
            }

            vector<Vector3d> points(control_point_count);
            vector<double> widths(control_point_count);

            m_curves.reserve(curve_count);

            for (size_t c = 0; c < curve_count; ++c)
            {
                for (size_t p = 0; p < control_point_count; ++p)
                {
                    input >> points[p].x >> points[p].y >> points[p].z;
                    input >> widths[p];
                }

                const BezierCurve3d curve(&points[0], &widths[0]);
                m_curves.push_back(curve);
            }

            input.close();

            stopwatch.measure();

            RENDERER_LOG_INFO(
                "loaded curve file %s (%s curves) in %s.",
                filepath,
                pretty_uint(curve_count).c_str(),
                pretty_time(stopwatch.get_seconds()).c_str());
        }
        else
        {
            RENDERER_LOG_ERROR("failed to load curve file %s.", filepath);
        }
    }
};

CurveObject::CurveObject(
    const SearchPaths&  search_paths,
    const char*         name,
    const ParamArray&   params)
  : Object(name, params)
  , impl(new Impl())
{
    const string filepath = params.get<string>("filepath");

    if (filepath == "builtin:hairball")
        impl->create_hair_ball(params);
    else if (filepath == "builtin:furryball")
        impl->create_furry_ball(params);
    else
    {
        impl->load_curve_file(
            search_paths.qualify(filepath).c_str());
    }
}

CurveObject::~CurveObject()
{
    delete impl;
}

void CurveObject::release()
{
    delete this;
}

const char* CurveObject::get_model() const
{
    return CurveObjectFactory::get_model();
}

GAABB3 CurveObject::compute_local_bbox() const
{
    return impl->compute_bounds();
}

Lazy<RegionKit>& CurveObject::get_region_kit()
{
    return impl->m_lazy_region_kit;
}

size_t CurveObject::get_material_slot_count() const
{
    return impl->m_material_slots.size();
}

const char* CurveObject::get_material_slot(const size_t index) const
{
    return impl->m_material_slots[index].c_str();
}

size_t CurveObject::get_curve_count() const
{
    return impl->m_curves.size();
}

const BezierCurve3d& CurveObject::get_curve(const size_t index) const
{
    assert(index < impl->m_curves.size());
    return impl->m_curves[index];
}

//
// CurveObjectFactory class implementation.
//

const char* CurveObjectFactory::get_model()
{
    return "curve_object";
}

auto_release_ptr<CurveObject> CurveObjectFactory::create(
    const SearchPaths&  search_paths,
    const char*         name,
    const ParamArray&   params)
{
    return
        auto_release_ptr<CurveObject>(
            new CurveObject(search_paths, name, params));
}

}   // namespace renderer
<|endoftext|>


<commit_before>// Copyright (c) 2021 by Apex.AI Inc. All rights reserved.
//
// 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.
//
// SPDX-License-Identifier: Apache-2.0

#include "iceoryx_posh/popo/client.hpp"
#include "iceoryx_posh/popo/listener.hpp"
#include "iceoryx_posh/popo/server.hpp"
#include "iceoryx_posh/popo/subscriber.hpp"
#include "iceoryx_posh/popo/untyped_client.hpp"
#include "iceoryx_posh/popo/untyped_server.hpp"
#include "iceoryx_posh/popo/untyped_subscriber.hpp"
#include "iceoryx_posh/testing/mocks/posh_runtime_mock.hpp"

#include "test.hpp"
namespace
{
using namespace ::testing;
using namespace ::iox::popo;
using namespace ::iox::cxx;
using namespace ::iox::capro;
using namespace ::iox::mepoo;

constexpr const char RUNTIME_NAME[] = "torben_dallas";
constexpr const char SERVICE[] = "respect_to_the_man_in_the_icecream_van";
constexpr const char INSTANCE[] = "Lakierski materialski";
constexpr const char EVENT[] = "boom boom boomerang";

/// Those tests verify that the destructor of the attachables is correct. When we
/// use inheritance it is possible that the trigger is a member of the base class
/// and destroyed in the base class constructor as well.
/// But the listener and waitset require the child class for cleanup. If the child
/// class destructor does not call reset on all the triggers the base class will
/// and the listener and waitset will call a method on the original class which is
/// already deleted. This causes undefined behavior which the sanitizer will catch.
///
/// The following tests should be run for all classes which can be attached to a
/// listener or a waitset to ensure that trigger.reset() is called in the child destructor
/// when we work with inheritance.
///
/// When no inheritance is used we shouldn't encounter any problems.
///
/// It suffices to call all tests on the listener since the listener and the waitset
/// are using the same trigger concept
class ListenerWaitsetAttachments_test : public Test
{
  public:
    void SetUp()
    {
        EXPECT_CALL(*this->runtimeMock, getMiddlewareConditionVariable())
            .WillOnce(Return(&this->conditionVariableData));
        listener.emplace();
    }

    template <typename T>
    static void genericTriggerCallback(T* const)
    {
    }

    std::unique_ptr<PoshRuntimeMock> runtimeMock = PoshRuntimeMock::create(RUNTIME_NAME);
    ConditionVariableData conditionVariableData{RUNTIME_NAME};
    optional<Listener> listener;

    MemoryManager memoryManager;
};

TEST_F(ListenerWaitsetAttachments_test, SubscriberAttachesToListener)
{
    ::testing::Test::RecordProperty("TEST_ID", "f1d163e0-4479-47b4-b4e8-01b0ee6a71b0");
    SubscriberPortData subscriberData({SERVICE, INSTANCE, EVENT},
                                      RUNTIME_NAME,
                                      VariantQueueTypes::SoFi_MultiProducerSingleConsumer,
                                      SubscriberOptions());
    EXPECT_CALL(*this->runtimeMock, getMiddlewareSubscriber(_, _, _)).WillOnce(Return(&subscriberData));

    optional<Subscriber<int>> subscriber;
    subscriber.emplace(ServiceDescription(SERVICE, INSTANCE, EVENT));

    ASSERT_FALSE(listener
                     ->attachEvent(*subscriber,
                                   SubscriberEvent::DATA_RECEIVED,
                                   createNotificationCallback(
                                       ListenerWaitsetAttachments_test::genericTriggerCallback<Subscriber<int>>))
                     .has_error());

    EXPECT_THAT(listener->size(), Eq(1));
    subscriber.reset();
    EXPECT_THAT(listener->size(), Eq(0));
}

TEST_F(ListenerWaitsetAttachments_test, UntypedSubscriberAttachesToListener)
{
    ::testing::Test::RecordProperty("TEST_ID", "a78a7016-46b6-4223-b7b1-e30344bb208f");
    SubscriberPortData subscriberData({SERVICE, INSTANCE, EVENT},
                                      RUNTIME_NAME,
                                      VariantQueueTypes::SoFi_MultiProducerSingleConsumer,
                                      SubscriberOptions());
    EXPECT_CALL(*this->runtimeMock, getMiddlewareSubscriber(_, _, _)).WillOnce(Return(&subscriberData));

    optional<UntypedSubscriber> subscriber;
    subscriber.emplace(ServiceDescription(SERVICE, INSTANCE, EVENT));

    ASSERT_FALSE(listener
                     ->attachEvent(*subscriber,
                                   SubscriberEvent::DATA_RECEIVED,
                                   createNotificationCallback(
                                       ListenerWaitsetAttachments_test::genericTriggerCallback<UntypedSubscriber>))
                     .has_error());

    EXPECT_THAT(listener->size(), Eq(1));
    subscriber.reset();
    EXPECT_THAT(listener->size(), Eq(0));
}

TEST_F(ListenerWaitsetAttachments_test, ClientAttachesToListener)
{
    ::testing::Test::RecordProperty("TEST_ID", "e21d98b9-9d24-4c85-90b9-0e7acd24a242");
    ClientPortData clientData({SERVICE, INSTANCE, EVENT}, RUNTIME_NAME, ClientOptions(), &memoryManager);
    EXPECT_CALL(*this->runtimeMock, getMiddlewareClient(_, _, _)).WillOnce(Return(&clientData));

    optional<Client<int, int>> client;
    client.emplace(ServiceDescription(SERVICE, INSTANCE, EVENT));

    ASSERT_FALSE(listener
                     ->attachEvent(*client,
                                   ClientEvent::RESPONSE_RECEIVED,
                                   createNotificationCallback(
                                       ListenerWaitsetAttachments_test::genericTriggerCallback<Client<int, int>>))
                     .has_error());

    EXPECT_THAT(listener->size(), Eq(1));
    client.reset();
    EXPECT_THAT(listener->size(), Eq(0));
}

TEST_F(ListenerWaitsetAttachments_test, UntypedClientAttachesToListener)
{
    ::testing::Test::RecordProperty("TEST_ID", "07934dd2-93aa-4aab-a216-eb86e842088b");
    ClientPortData clientData({SERVICE, INSTANCE, EVENT}, RUNTIME_NAME, ClientOptions(), &memoryManager);
    EXPECT_CALL(*this->runtimeMock, getMiddlewareClient(_, _, _)).WillOnce(Return(&clientData));

    optional<UntypedClient> client;
    client.emplace(ServiceDescription(SERVICE, INSTANCE, EVENT));

    ASSERT_FALSE(listener
                     ->attachEvent(*client,
                                   ClientEvent::RESPONSE_RECEIVED,
                                   createNotificationCallback(
                                       ListenerWaitsetAttachments_test::genericTriggerCallback<UntypedClient>))
                     .has_error());

    EXPECT_THAT(listener->size(), Eq(1));
    client.reset();
    EXPECT_THAT(listener->size(), Eq(0));
}

TEST_F(ListenerWaitsetAttachments_test, ServerAttachesToListener)
{
    ::testing::Test::RecordProperty("TEST_ID", "306d7ef9-1fb1-4ce8-8b58-e5a7cb5fff69");
    ServerPortData serverData({SERVICE, INSTANCE, EVENT}, RUNTIME_NAME, ServerOptions(), &memoryManager);
    EXPECT_CALL(*this->runtimeMock, getMiddlewareServer(_, _, _)).WillOnce(Return(&serverData));

    optional<Server<int, int>> server;
    server.emplace(ServiceDescription(SERVICE, INSTANCE, EVENT));

    ASSERT_FALSE(listener
                     ->attachEvent(*server,
                                   ServerEvent::REQUEST_RECEIVED,
                                   createNotificationCallback(
                                       ListenerWaitsetAttachments_test::genericTriggerCallback<Server<int, int>>))
                     .has_error());

    EXPECT_THAT(listener->size(), Eq(1));
    server.reset();
    EXPECT_THAT(listener->size(), Eq(0));
}

TEST_F(ListenerWaitsetAttachments_test, UntypedServerAttachesToListener)
{
    ::testing::Test::RecordProperty("TEST_ID", "3d074e8f-eab6-475d-a884-de8b4d2596ea");
    ServerPortData serverData({SERVICE, INSTANCE, EVENT}, RUNTIME_NAME, ServerOptions(), &memoryManager);
    EXPECT_CALL(*this->runtimeMock, getMiddlewareServer(_, _, _)).WillOnce(Return(&serverData));

    optional<UntypedServer> server;
    server.emplace(ServiceDescription(SERVICE, INSTANCE, EVENT));

    ASSERT_FALSE(listener
                     ->attachEvent(*server,
                                   ServerEvent::REQUEST_RECEIVED,
                                   createNotificationCallback(
                                       ListenerWaitsetAttachments_test::genericTriggerCallback<UntypedServer>))
                     .has_error());

    EXPECT_THAT(listener->size(), Eq(1));
    server.reset();
    EXPECT_THAT(listener->size(), Eq(0));
}
} // namespace
<commit_msg>iox-#1173 Copyright year adjusted<commit_after>// Copyright (c) 2022 by Apex.AI Inc. All rights reserved.
//
// 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.
//
// SPDX-License-Identifier: Apache-2.0

#include "iceoryx_posh/popo/client.hpp"
#include "iceoryx_posh/popo/listener.hpp"
#include "iceoryx_posh/popo/server.hpp"
#include "iceoryx_posh/popo/subscriber.hpp"
#include "iceoryx_posh/popo/untyped_client.hpp"
#include "iceoryx_posh/popo/untyped_server.hpp"
#include "iceoryx_posh/popo/untyped_subscriber.hpp"
#include "iceoryx_posh/testing/mocks/posh_runtime_mock.hpp"

#include "test.hpp"
namespace
{
using namespace ::testing;
using namespace ::iox::popo;
using namespace ::iox::cxx;
using namespace ::iox::capro;
using namespace ::iox::mepoo;

constexpr const char RUNTIME_NAME[] = "torben_dallas";
constexpr const char SERVICE[] = "respect_to_the_man_in_the_icecream_van";
constexpr const char INSTANCE[] = "Lakierski materialski";
constexpr const char EVENT[] = "boom boom boomerang";

/// Those tests verify that the destructor of the attachables is correct. When we
/// use inheritance it is possible that the trigger is a member of the base class
/// and destroyed in the base class constructor as well.
/// But the listener and waitset require the child class for cleanup. If the child
/// class destructor does not call reset on all the triggers the base class will
/// and the listener and waitset will call a method on the original class which is
/// already deleted. This causes undefined behavior which the sanitizer will catch.
///
/// The following tests should be run for all classes which can be attached to a
/// listener or a waitset to ensure that trigger.reset() is called in the child destructor
/// when we work with inheritance.
///
/// When no inheritance is used we shouldn't encounter any problems.
///
/// It suffices to call all tests on the listener since the listener and the waitset
/// are using the same trigger concept
class ListenerWaitsetAttachments_test : public Test
{
  public:
    void SetUp()
    {
        EXPECT_CALL(*this->runtimeMock, getMiddlewareConditionVariable())
            .WillOnce(Return(&this->conditionVariableData));
        listener.emplace();
    }

    template <typename T>
    static void genericTriggerCallback(T* const)
    {
    }

    std::unique_ptr<PoshRuntimeMock> runtimeMock = PoshRuntimeMock::create(RUNTIME_NAME);
    ConditionVariableData conditionVariableData{RUNTIME_NAME};
    optional<Listener> listener;

    MemoryManager memoryManager;
};

TEST_F(ListenerWaitsetAttachments_test, SubscriberAttachesToListener)
{
    ::testing::Test::RecordProperty("TEST_ID", "f1d163e0-4479-47b4-b4e8-01b0ee6a71b0");
    SubscriberPortData subscriberData({SERVICE, INSTANCE, EVENT},
                                      RUNTIME_NAME,
                                      VariantQueueTypes::SoFi_MultiProducerSingleConsumer,
                                      SubscriberOptions());
    EXPECT_CALL(*this->runtimeMock, getMiddlewareSubscriber(_, _, _)).WillOnce(Return(&subscriberData));

    optional<Subscriber<int>> subscriber;
    subscriber.emplace(ServiceDescription(SERVICE, INSTANCE, EVENT));

    ASSERT_FALSE(listener
                     ->attachEvent(*subscriber,
                                   SubscriberEvent::DATA_RECEIVED,
                                   createNotificationCallback(
                                       ListenerWaitsetAttachments_test::genericTriggerCallback<Subscriber<int>>))
                     .has_error());

    EXPECT_THAT(listener->size(), Eq(1));
    subscriber.reset();
    EXPECT_THAT(listener->size(), Eq(0));
}

TEST_F(ListenerWaitsetAttachments_test, UntypedSubscriberAttachesToListener)
{
    ::testing::Test::RecordProperty("TEST_ID", "a78a7016-46b6-4223-b7b1-e30344bb208f");
    SubscriberPortData subscriberData({SERVICE, INSTANCE, EVENT},
                                      RUNTIME_NAME,
                                      VariantQueueTypes::SoFi_MultiProducerSingleConsumer,
                                      SubscriberOptions());
    EXPECT_CALL(*this->runtimeMock, getMiddlewareSubscriber(_, _, _)).WillOnce(Return(&subscriberData));

    optional<UntypedSubscriber> subscriber;
    subscriber.emplace(ServiceDescription(SERVICE, INSTANCE, EVENT));

    ASSERT_FALSE(listener
                     ->attachEvent(*subscriber,
                                   SubscriberEvent::DATA_RECEIVED,
                                   createNotificationCallback(
                                       ListenerWaitsetAttachments_test::genericTriggerCallback<UntypedSubscriber>))
                     .has_error());

    EXPECT_THAT(listener->size(), Eq(1));
    subscriber.reset();
    EXPECT_THAT(listener->size(), Eq(0));
}

TEST_F(ListenerWaitsetAttachments_test, ClientAttachesToListener)
{
    ::testing::Test::RecordProperty("TEST_ID", "e21d98b9-9d24-4c85-90b9-0e7acd24a242");
    ClientPortData clientData({SERVICE, INSTANCE, EVENT}, RUNTIME_NAME, ClientOptions(), &memoryManager);
    EXPECT_CALL(*this->runtimeMock, getMiddlewareClient(_, _, _)).WillOnce(Return(&clientData));

    optional<Client<int, int>> client;
    client.emplace(ServiceDescription(SERVICE, INSTANCE, EVENT));

    ASSERT_FALSE(listener
                     ->attachEvent(*client,
                                   ClientEvent::RESPONSE_RECEIVED,
                                   createNotificationCallback(
                                       ListenerWaitsetAttachments_test::genericTriggerCallback<Client<int, int>>))
                     .has_error());

    EXPECT_THAT(listener->size(), Eq(1));
    client.reset();
    EXPECT_THAT(listener->size(), Eq(0));
}

TEST_F(ListenerWaitsetAttachments_test, UntypedClientAttachesToListener)
{
    ::testing::Test::RecordProperty("TEST_ID", "07934dd2-93aa-4aab-a216-eb86e842088b");
    ClientPortData clientData({SERVICE, INSTANCE, EVENT}, RUNTIME_NAME, ClientOptions(), &memoryManager);
    EXPECT_CALL(*this->runtimeMock, getMiddlewareClient(_, _, _)).WillOnce(Return(&clientData));

    optional<UntypedClient> client;
    client.emplace(ServiceDescription(SERVICE, INSTANCE, EVENT));

    ASSERT_FALSE(listener
                     ->attachEvent(*client,
                                   ClientEvent::RESPONSE_RECEIVED,
                                   createNotificationCallback(
                                       ListenerWaitsetAttachments_test::genericTriggerCallback<UntypedClient>))
                     .has_error());

    EXPECT_THAT(listener->size(), Eq(1));
    client.reset();
    EXPECT_THAT(listener->size(), Eq(0));
}

TEST_F(ListenerWaitsetAttachments_test, ServerAttachesToListener)
{
    ::testing::Test::RecordProperty("TEST_ID", "306d7ef9-1fb1-4ce8-8b58-e5a7cb5fff69");
    ServerPortData serverData({SERVICE, INSTANCE, EVENT}, RUNTIME_NAME, ServerOptions(), &memoryManager);
    EXPECT_CALL(*this->runtimeMock, getMiddlewareServer(_, _, _)).WillOnce(Return(&serverData));

    optional<Server<int, int>> server;
    server.emplace(ServiceDescription(SERVICE, INSTANCE, EVENT));

    ASSERT_FALSE(listener
                     ->attachEvent(*server,
                                   ServerEvent::REQUEST_RECEIVED,
                                   createNotificationCallback(
                                       ListenerWaitsetAttachments_test::genericTriggerCallback<Server<int, int>>))
                     .has_error());

    EXPECT_THAT(listener->size(), Eq(1));
    server.reset();
    EXPECT_THAT(listener->size(), Eq(0));
}

TEST_F(ListenerWaitsetAttachments_test, UntypedServerAttachesToListener)
{
    ::testing::Test::RecordProperty("TEST_ID", "3d074e8f-eab6-475d-a884-de8b4d2596ea");
    ServerPortData serverData({SERVICE, INSTANCE, EVENT}, RUNTIME_NAME, ServerOptions(), &memoryManager);
    EXPECT_CALL(*this->runtimeMock, getMiddlewareServer(_, _, _)).WillOnce(Return(&serverData));

    optional<UntypedServer> server;
    server.emplace(ServiceDescription(SERVICE, INSTANCE, EVENT));

    ASSERT_FALSE(listener
                     ->attachEvent(*server,
                                   ServerEvent::REQUEST_RECEIVED,
                                   createNotificationCallback(
                                       ListenerWaitsetAttachments_test::genericTriggerCallback<UntypedServer>))
                     .has_error());

    EXPECT_THAT(listener->size(), Eq(1));
    server.reset();
    EXPECT_THAT(listener->size(), Eq(0));
}
} // namespace
<|endoftext|>