<commit_before>/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2017 BMW Car IT GmbH
 * %%
 * 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.
 * #L%
 */

#include "libjoynrclustercontroller/mqtt/MosquittoConnection.h"

#include "joynr/ClusterControllerSettings.h"
#include "joynr/MessagingSettings.h"
#include "joynr/exceptions/JoynrException.h"

namespace joynr
{

INIT_LOGGER(MosquittoConnection);

MosquittoConnection::MosquittoConnection(const MessagingSettings& messagingSettings,
                                         const ClusterControllerSettings& ccSettings,
                                         const std::string& clientId)
        : mosquittopp(clientId.c_str(), false),
          messagingSettings(messagingSettings),
          host(messagingSettings.getBrokerUrl().getBrokerChannelsBaseUrl().getHost()),
          port(messagingSettings.getBrokerUrl().getBrokerChannelsBaseUrl().getPort()),
          channelId(),
          subscribeChannelMid(),
          topic(),
          additionalTopics(),
          additionalTopicsMutex(),
          isConnected(false),
          isInitialConnection(true),
          isRunning(false),
          isChannelIdRegistered(false),
          subscribedToChannelTopic(false),
          readyToSend(false),
          onMessageReceived(),
          onReadyToSendChangedMutex(),
          onReadyToSendChanged(),
          thread()
{
    JOYNR_LOG_DEBUG(logger, "Try to connect to tcp://{}:{}", host, port);

    mosqpp::lib_init();

    if (ccSettings.isMqttTlsEnabled()) {
        int rc = tls_set(ccSettings.getMqttCertificateAuthorityPemFilename().c_str(),
                         NULL,
                         ccSettings.getMqttCertificatePemFilename().c_str(),
                         ccSettings.getMqttPrivateKeyPemFilename().c_str());

        if (rc != MOSQ_ERR_SUCCESS) {
            JOYNR_LOG_ERROR(
                    logger, "Could not initialize TLS connection - {}", mosqpp::strerror(rc));
        }
    } else {
        JOYNR_LOG_DEBUG(logger, "MQTT connection not encrypted");
    };

    connect(host.c_str(), port, messagingSettings.getMqttKeepAliveTime().count());
}

MosquittoConnection::~MosquittoConnection()
{
    stop();
}

void MosquittoConnection::on_disconnect(int rc)
{
    isConnected = false;
    setReadyToSend(false);

    if (rc == 0) {
        JOYNR_LOG_DEBUG(logger, "Disconnected from tcp://{}:{}", host, port);
    } else {
        JOYNR_LOG_ERROR(logger,
                        "Unexpectedly disconnected from tcp://{}:{}, error: {}",
                        host,
                        port,
                        mosqpp::strerror(rc));
    }
}

void MosquittoConnection::on_log(int level, const char* str)
{
    if (level == MOSQ_LOG_ERR) {
        JOYNR_LOG_ERROR(logger, "Mosquitto Log: {}", str);
    } else if (level == MOSQ_LOG_WARNING) {
        JOYNR_LOG_WARN(logger, "Mosquitto Log: {}", str);
    } else if (level == MOSQ_LOG_INFO) {
        JOYNR_LOG_INFO(logger, "Mosquitto Log: {}", str);
    } else {
        // MOSQ_LOG_NOTICE || MOSQ_LOG_DEBUG || any other log level
        JOYNR_LOG_DEBUG(logger, "Mosquitto Log: {}", str);
    }
}

void MosquittoConnection::on_error()
{
    JOYNR_LOG_WARN(logger, "Mosquitto Error");
}

std::uint16_t MosquittoConnection::getMqttQos() const
{
    return mqttQos;
}

std::string MosquittoConnection::getMqttPrio() const
{
    static const std::string value("low");
    return value;
}

bool MosquittoConnection::isMqttRetain() const
{
    return mqttRetain;
}

void MosquittoConnection::start()
{
    isRunning = true;
    thread = std::thread(&MosquittoConnection::runLoop, this);
}

void MosquittoConnection::stop()
{
    isRunning = false;
    if (thread.joinable()) {
        thread.join();
    }
}

void MosquittoConnection::runLoop()
{
    const int mqttConnectionTimeoutMs = messagingSettings.getMqttConnectionTimeout().count();

    while (isRunning) {
        int rc = loop(mqttConnectionTimeoutMs);

        if (rc != MOSQ_ERR_SUCCESS) {
            if (rc == MOSQ_ERR_CONN_LOST) {
                JOYNR_LOG_DEBUG(logger,
                                "error: connection to broker lost ({}), trying to reconnect...",
                                mosqpp::strerror(rc));
            } else if (rc == MOSQ_ERR_NO_CONN) {
                JOYNR_LOG_DEBUG(logger,
                                "error: not connected to a broker ({}), trying to reconnect...",
                                mosqpp::strerror(rc));
            } else {
                // MOSQ_ERR_INVAL || MOSQ_ERR_NOMEM || MOSQ_ERR_PROTOCOL || MOSQ_ERR_ERRNO
                JOYNR_LOG_ERROR(logger,
                                "connection to broker lost, unexpected error: {} ({}), trying to "
                                "reconnect...",
                                std::to_string(rc),
                                mosqpp::strerror(rc));
            }
            std::this_thread::sleep_for(messagingSettings.getMqttReconnectSleepTime());
            reconnect();
        }
    }

    JOYNR_LOG_TRACE(logger, "Try to disconnect Mosquitto Connection");
    int rc = disconnect();
    if (rc == MOSQ_ERR_SUCCESS) {
        JOYNR_LOG_DEBUG(logger, "Mosquitto Connection disconnected");
    } else {
        JOYNR_LOG_ERROR(logger,
                        "Mosquitto disconnect failed: error: {} ({})",
                        std::to_string(rc),
                        mosqpp::strerror(rc));
    }

    mosqpp::lib_cleanup();
}

void MosquittoConnection::on_connect(int rc)
{
    if (rc > 0) {
        if (rc == 1) {
            JOYNR_LOG_ERROR(logger,
                            "Mosquitto Connection Error {} ({})",
                            rc,
                            "connection refused (unacceptable protocol version)");
        } else if (rc == 2) {
            JOYNR_LOG_ERROR(logger,
                            "Mosquitto Connection Error {} ({})",
                            rc,
                            "connection refused (identifier rejected)");
        } else if (rc == 3) {
            JOYNR_LOG_DEBUG(logger,
                            "Mosquitto Connection Error {} ({})",
                            rc,
                            "connection refused (broker unavailable)");
        } else {
            JOYNR_LOG_ERROR(logger,
                            "Mosquitto Connection Error {} ({})",
                            rc,
                            "unknown error code (reserved for future use)");
        }
    } else {
        JOYNR_LOG_DEBUG(logger, "Mosquitto Connection established");
        isConnected = true;

        // The MQTT broker will reestablish subscriptions based on the client ID.
        if (isInitialConnection) {
            isInitialConnection = false;
            createSubscriptions();
        } else {
            setReadyToSend(true);
        }
    }
}

void MosquittoConnection::createSubscriptions()
{
    while (!isChannelIdRegistered && isRunning) {
        std::this_thread::sleep_for(std::chrono::milliseconds(25));
    }
    try {
        subscribeToTopicInternal(topic, true);
        std::lock_guard<std::recursive_mutex> lock(additionalTopicsMutex);
        for (const std::string& additionalTopic : additionalTopics) {
            subscribeToTopicInternal(additionalTopic);
        }
    } catch (const exceptions::JoynrRuntimeException& error) {
        JOYNR_LOG_ERROR(logger, "Error subscribing to Mqtt topic, error: ", error.getMessage());
    }
}

void MosquittoConnection::subscribeToTopicInternal(const std::string& topic,
                                                   const bool isChannelTopic)
{
    int* mid = nullptr;
    if (isChannelTopic) {
        mid = &subscribeChannelMid;
    }
    int rc = subscribe(mid, topic.c_str(), getMqttQos());
    switch (rc) {
    case (MOSQ_ERR_SUCCESS):
        JOYNR_LOG_DEBUG(logger, "Subscribed to {}", topic);
        break;
    case (MOSQ_ERR_NO_CONN):
        JOYNR_LOG_DEBUG(logger,
                        "Subscription to {} failed: error: {} (not connected to a broker). "
                        "Subscription will be restored on connect.",
                        topic,
                        std::to_string(rc));
        break;
    default:
        // MOSQ_ERR_INVAL, MOSQ_ERR_NOMEM
        std::string errorMsg = "Subscription to " + topic + " failed: error: " +
                               std::to_string(rc) + " (" + mosqpp::strerror(rc) + ")";
        throw exceptions::JoynrRuntimeException(errorMsg);
    }
}

void MosquittoConnection::subscribeToTopic(const std::string& topic)
{
    while (!isChannelIdRegistered && isRunning) {
        std::this_thread::sleep_for(std::chrono::milliseconds(25));
    }
    {
        std::lock_guard<std::recursive_mutex> lock(additionalTopicsMutex);
        if (additionalTopics.find(topic) != additionalTopics.end()) {
            JOYNR_LOG_DEBUG(logger, "already subscribed to topic {}", topic);
            return;
        }
        subscribeToTopicInternal(topic);
        additionalTopics.insert(topic);
    }
}

void MosquittoConnection::unsubscribeFromTopic(const std::string& topic)
{
    if (isChannelIdRegistered) {
        std::lock_guard<std::recursive_mutex> lock(additionalTopicsMutex);
        if (additionalTopics.find(topic) == additionalTopics.end()) {
            JOYNR_LOG_DEBUG(logger, "Unsubscribe called for non existing topic {}", topic);
            return;
        }
        additionalTopics.erase(topic);
        if (isConnected && isRunning) {
            int rc = unsubscribe(nullptr, topic.c_str());
            if (rc == MOSQ_ERR_SUCCESS) {
                JOYNR_LOG_DEBUG(logger, "Unsubscribed from {}", topic);
            } else {
                // MOSQ_ERR_INVAL || MOSQ_ERR_NOMEM || MOSQ_ERR_NO_CONN
                JOYNR_LOG_ERROR(logger,
                                "Unsubscribe from {} failed: error: {} ({})",
                                topic,
                                std::to_string(rc),
                                mosqpp::strerror(rc));
            }
        }
    }
}

void MosquittoConnection::publishMessage(
        const std::string& topic,
        const int qosLevel,
        const std::function<void(const exceptions::JoynrRuntimeException&)>& onFailure,
        uint32_t payloadlen = 0,
        const void* payload = nullptr)
{
    JOYNR_LOG_DEBUG(logger, "Publish to {}", topic);

    int mid;
    int rc = publish(&mid, topic.c_str(), payloadlen, payload, qosLevel, isMqttRetain());
    if (!(rc == MOSQ_ERR_SUCCESS)) {
        if (rc == MOSQ_ERR_INVAL || rc == MOSQ_ERR_PAYLOAD_SIZE) {
            onFailure(exceptions::JoynrMessageNotSentException(
                    "message could not be sent: mid (mqtt message id): " + std::to_string(mid) +
                    ", error: " + std::to_string(rc) + " (" + mosqpp::strerror(rc) + ")"));
        }
        // MOSQ_ERR_NOMEM || MOSQ_ERR_NO_CONN || MOSQ_ERR_PROTOCOL ||| unexpected errors
        onFailure(exceptions::JoynrDelayMessageException(
                "error sending message: mid (mqtt message id): " + std::to_string(mid) +
                ", error: " + std::to_string(rc) + " (" + mosqpp::strerror(rc) + ")"));
    }
    JOYNR_LOG_TRACE(logger, "published message with mqtt message id {}", std::to_string(mid));
}

void MosquittoConnection::registerChannelId(const std::string& channelId)
{
    this->channelId = channelId;
    topic = channelId + "/" + getMqttPrio() + "/" + "#";
    isChannelIdRegistered = true;
}

void MosquittoConnection::registerReceiveCallback(
        std::function<void(smrf::ByteVector&&)> onMessageReceived)
{
    this->onMessageReceived = onMessageReceived;
}

void MosquittoConnection::registerReadyToSendChangedCallback(
        std::function<void(bool)> onReadyToSendChanged)
{
    std::lock_guard<std::mutex> lock(onReadyToSendChangedMutex);
    this->onReadyToSendChanged = std::move(onReadyToSendChanged);
}

bool MosquittoConnection::isSubscribedToChannelTopic() const
{
    return subscribedToChannelTopic;
}

bool MosquittoConnection::isReadyToSend() const
{
    return readyToSend;
}

void MosquittoConnection::on_subscribe(int mid, int qos_count, const int* granted_qos)
{
    JOYNR_LOG_DEBUG(logger, "Subscribed (mid: {} with granted QOS {}", mid, granted_qos[0]);

    for (int i = 1; i < qos_count; i++) {
        JOYNR_LOG_DEBUG(logger, "QOS: {} granted {}", i, granted_qos[i]);
    }

    if (mid == subscribeChannelMid) {
        subscribedToChannelTopic = true;
        setReadyToSend(isConnected);
    }
}

void MosquittoConnection::on_message(const mosquitto_message* message)
{
    if (!onMessageReceived) {
        JOYNR_LOG_ERROR(
                logger, "Discarding received message, since onMessageReceived callback is empty.");
        return;
    }

    std::uint8_t* data = static_cast<std::uint8_t*>(message->payload);
    smrf::ByteVector rawMessage(data, data + message->payloadlen);
    onMessageReceived(std::move(rawMessage));
}

void MosquittoConnection::on_publish(int mid)
{
    JOYNR_LOG_TRACE(logger, "published message with mid {}", std::to_string(mid));
}

void MosquittoConnection::setReadyToSend(bool readyToSend)
{
    if (this->readyToSend != readyToSend) {
        this->readyToSend = readyToSend;

        std::lock_guard<std::mutex> lock(onReadyToSendChangedMutex);
        if (onReadyToSendChanged) {
            onReadyToSendChanged(readyToSend);
        }
    }
}

} // namespace joynr
<commit_msg>[C++] Reestablish mqtt subscriptions upon connect event<commit_after>/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2017 BMW Car IT GmbH
 * %%
 * 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.
 * #L%
 */

#include "libjoynrclustercontroller/mqtt/MosquittoConnection.h"

#include "joynr/ClusterControllerSettings.h"
#include "joynr/MessagingSettings.h"
#include "joynr/exceptions/JoynrException.h"

namespace joynr
{

INIT_LOGGER(MosquittoConnection);

MosquittoConnection::MosquittoConnection(const MessagingSettings& messagingSettings,
                                         const ClusterControllerSettings& ccSettings,
                                         const std::string& clientId)
        : mosquittopp(clientId.c_str(), false),
          messagingSettings(messagingSettings),
          host(messagingSettings.getBrokerUrl().getBrokerChannelsBaseUrl().getHost()),
          port(messagingSettings.getBrokerUrl().getBrokerChannelsBaseUrl().getPort()),
          channelId(),
          subscribeChannelMid(),
          topic(),
          additionalTopics(),
          additionalTopicsMutex(),
          isConnected(false),
          isInitialConnection(true),
          isRunning(false),
          isChannelIdRegistered(false),
          subscribedToChannelTopic(false),
          readyToSend(false),
          onMessageReceived(),
          onReadyToSendChangedMutex(),
          onReadyToSendChanged(),
          thread()
{
    JOYNR_LOG_DEBUG(logger, "Try to connect to tcp://{}:{}", host, port);

    mosqpp::lib_init();

    if (ccSettings.isMqttTlsEnabled()) {
        int rc = tls_set(ccSettings.getMqttCertificateAuthorityPemFilename().c_str(),
                         NULL,
                         ccSettings.getMqttCertificatePemFilename().c_str(),
                         ccSettings.getMqttPrivateKeyPemFilename().c_str());

        if (rc != MOSQ_ERR_SUCCESS) {
            JOYNR_LOG_ERROR(
                    logger, "Could not initialize TLS connection - {}", mosqpp::strerror(rc));
        }
    } else {
        JOYNR_LOG_DEBUG(logger, "MQTT connection not encrypted");
    };

    connect(host.c_str(), port, messagingSettings.getMqttKeepAliveTime().count());
}

MosquittoConnection::~MosquittoConnection()
{
    stop();
}

void MosquittoConnection::on_disconnect(int rc)
{
    isConnected = false;
    setReadyToSend(false);

    if (rc == 0) {
        JOYNR_LOG_DEBUG(logger, "Disconnected from tcp://{}:{}", host, port);
    } else {
        JOYNR_LOG_ERROR(logger,
                        "Unexpectedly disconnected from tcp://{}:{}, error: {}",
                        host,
                        port,
                        mosqpp::strerror(rc));
    }
}

void MosquittoConnection::on_log(int level, const char* str)
{
    if (level == MOSQ_LOG_ERR) {
        JOYNR_LOG_ERROR(logger, "Mosquitto Log: {}", str);
    } else if (level == MOSQ_LOG_WARNING) {
        JOYNR_LOG_WARN(logger, "Mosquitto Log: {}", str);
    } else if (level == MOSQ_LOG_INFO) {
        JOYNR_LOG_INFO(logger, "Mosquitto Log: {}", str);
    } else {
        // MOSQ_LOG_NOTICE || MOSQ_LOG_DEBUG || any other log level
        JOYNR_LOG_DEBUG(logger, "Mosquitto Log: {}", str);
    }
}

void MosquittoConnection::on_error()
{
    JOYNR_LOG_WARN(logger, "Mosquitto Error");
}

std::uint16_t MosquittoConnection::getMqttQos() const
{
    return mqttQos;
}

std::string MosquittoConnection::getMqttPrio() const
{
    static const std::string value("low");
    return value;
}

bool MosquittoConnection::isMqttRetain() const
{
    return mqttRetain;
}

void MosquittoConnection::start()
{
    isRunning = true;
    thread = std::thread(&MosquittoConnection::runLoop, this);
}

void MosquittoConnection::stop()
{
    isRunning = false;
    if (thread.joinable()) {
        thread.join();
    }
}

void MosquittoConnection::runLoop()
{
    const int mqttConnectionTimeoutMs = messagingSettings.getMqttConnectionTimeout().count();

    while (isRunning) {
        int rc = loop(mqttConnectionTimeoutMs);

        if (rc != MOSQ_ERR_SUCCESS) {
            if (rc == MOSQ_ERR_CONN_LOST) {
                JOYNR_LOG_DEBUG(logger,
                                "error: connection to broker lost ({}), trying to reconnect...",
                                mosqpp::strerror(rc));
            } else if (rc == MOSQ_ERR_NO_CONN) {
                JOYNR_LOG_DEBUG(logger,
                                "error: not connected to a broker ({}), trying to reconnect...",
                                mosqpp::strerror(rc));
            } else {
                // MOSQ_ERR_INVAL || MOSQ_ERR_NOMEM || MOSQ_ERR_PROTOCOL || MOSQ_ERR_ERRNO
                JOYNR_LOG_ERROR(logger,
                                "connection to broker lost, unexpected error: {} ({}), trying to "
                                "reconnect...",
                                std::to_string(rc),
                                mosqpp::strerror(rc));
            }
            std::this_thread::sleep_for(messagingSettings.getMqttReconnectSleepTime());
            reconnect();
        }
    }

    JOYNR_LOG_TRACE(logger, "Try to disconnect Mosquitto Connection");
    int rc = disconnect();
    if (rc == MOSQ_ERR_SUCCESS) {
        JOYNR_LOG_DEBUG(logger, "Mosquitto Connection disconnected");
    } else {
        JOYNR_LOG_ERROR(logger,
                        "Mosquitto disconnect failed: error: {} ({})",
                        std::to_string(rc),
                        mosqpp::strerror(rc));
    }

    mosqpp::lib_cleanup();
}

void MosquittoConnection::on_connect(int rc)
{
    if (rc > 0) {
        if (rc == 1) {
            JOYNR_LOG_ERROR(logger,
                            "Mosquitto Connection Error {} ({})",
                            rc,
                            "connection refused (unacceptable protocol version)");
        } else if (rc == 2) {
            JOYNR_LOG_ERROR(logger,
                            "Mosquitto Connection Error {} ({})",
                            rc,
                            "connection refused (identifier rejected)");
        } else if (rc == 3) {
            JOYNR_LOG_DEBUG(logger,
                            "Mosquitto Connection Error {} ({})",
                            rc,
                            "connection refused (broker unavailable)");
        } else {
            JOYNR_LOG_ERROR(logger,
                            "Mosquitto Connection Error {} ({})",
                            rc,
                            "unknown error code (reserved for future use)");
        }
    } else {
        JOYNR_LOG_DEBUG(logger, "Mosquitto Connection established");
        isConnected = true;

        createSubscriptions();

        if (isInitialConnection) {
            isInitialConnection = false;
        }
    }
}

void MosquittoConnection::createSubscriptions()
{
    while (!isChannelIdRegistered && isRunning) {
        std::this_thread::sleep_for(std::chrono::milliseconds(25));
    }
    try {
        subscribeToTopicInternal(topic, true);
        std::lock_guard<std::recursive_mutex> lock(additionalTopicsMutex);
        for (const std::string& additionalTopic : additionalTopics) {
            subscribeToTopicInternal(additionalTopic);
        }
    } catch (const exceptions::JoynrRuntimeException& error) {
        JOYNR_LOG_ERROR(logger, "Error subscribing to Mqtt topic, error: ", error.getMessage());
    }
}

void MosquittoConnection::subscribeToTopicInternal(const std::string& topic,
                                                   const bool isChannelTopic)
{
    int* mid = nullptr;
    if (isChannelTopic) {
        mid = &subscribeChannelMid;
    }
    int rc = subscribe(mid, topic.c_str(), getMqttQos());
    switch (rc) {
    case (MOSQ_ERR_SUCCESS):
        JOYNR_LOG_DEBUG(logger, "Subscribed to {}", topic);
        break;
    case (MOSQ_ERR_NO_CONN):
        JOYNR_LOG_DEBUG(logger,
                        "Subscription to {} failed: error: {} (not connected to a broker). "
                        "Subscription will be restored on connect.",
                        topic,
                        std::to_string(rc));
        break;
    default:
        // MOSQ_ERR_INVAL, MOSQ_ERR_NOMEM
        std::string errorMsg = "Subscription to " + topic + " failed: error: " +
                               std::to_string(rc) + " (" + mosqpp::strerror(rc) + ")";
        throw exceptions::JoynrRuntimeException(errorMsg);
    }
}

void MosquittoConnection::subscribeToTopic(const std::string& topic)
{
    while (!isChannelIdRegistered && isRunning) {
        std::this_thread::sleep_for(std::chrono::milliseconds(25));
    }
    {
        std::lock_guard<std::recursive_mutex> lock(additionalTopicsMutex);
        if (additionalTopics.find(topic) != additionalTopics.end()) {
            JOYNR_LOG_DEBUG(logger, "already subscribed to topic {}", topic);
            return;
        }
        subscribeToTopicInternal(topic);
        additionalTopics.insert(topic);
    }
}

void MosquittoConnection::unsubscribeFromTopic(const std::string& topic)
{
    if (isChannelIdRegistered) {
        std::lock_guard<std::recursive_mutex> lock(additionalTopicsMutex);
        if (additionalTopics.find(topic) == additionalTopics.end()) {
            JOYNR_LOG_DEBUG(logger, "Unsubscribe called for non existing topic {}", topic);
            return;
        }
        additionalTopics.erase(topic);
        if (isConnected && isRunning) {
            int rc = unsubscribe(nullptr, topic.c_str());
            if (rc == MOSQ_ERR_SUCCESS) {
                JOYNR_LOG_DEBUG(logger, "Unsubscribed from {}", topic);
            } else {
                // MOSQ_ERR_INVAL || MOSQ_ERR_NOMEM || MOSQ_ERR_NO_CONN
                JOYNR_LOG_ERROR(logger,
                                "Unsubscribe from {} failed: error: {} ({})",
                                topic,
                                std::to_string(rc),
                                mosqpp::strerror(rc));
            }
        }
    }
}

void MosquittoConnection::publishMessage(
        const std::string& topic,
        const int qosLevel,
        const std::function<void(const exceptions::JoynrRuntimeException&)>& onFailure,
        uint32_t payloadlen = 0,
        const void* payload = nullptr)
{
    JOYNR_LOG_DEBUG(logger, "Publish to {}", topic);

    int mid;
    int rc = publish(&mid, topic.c_str(), payloadlen, payload, qosLevel, isMqttRetain());
    if (!(rc == MOSQ_ERR_SUCCESS)) {
        if (rc == MOSQ_ERR_INVAL || rc == MOSQ_ERR_PAYLOAD_SIZE) {
            onFailure(exceptions::JoynrMessageNotSentException(
                    "message could not be sent: mid (mqtt message id): " + std::to_string(mid) +
                    ", error: " + std::to_string(rc) + " (" + mosqpp::strerror(rc) + ")"));
        }
        // MOSQ_ERR_NOMEM || MOSQ_ERR_NO_CONN || MOSQ_ERR_PROTOCOL ||| unexpected errors
        onFailure(exceptions::JoynrDelayMessageException(
                "error sending message: mid (mqtt message id): " + std::to_string(mid) +
                ", error: " + std::to_string(rc) + " (" + mosqpp::strerror(rc) + ")"));
    }
    JOYNR_LOG_TRACE(logger, "published message with mqtt message id {}", std::to_string(mid));
}

void MosquittoConnection::registerChannelId(const std::string& channelId)
{
    this->channelId = channelId;
    topic = channelId + "/" + getMqttPrio() + "/" + "#";
    isChannelIdRegistered = true;
}

void MosquittoConnection::registerReceiveCallback(
        std::function<void(smrf::ByteVector&&)> onMessageReceived)
{
    this->onMessageReceived = onMessageReceived;
}

void MosquittoConnection::registerReadyToSendChangedCallback(
        std::function<void(bool)> onReadyToSendChanged)
{
    std::lock_guard<std::mutex> lock(onReadyToSendChangedMutex);
    this->onReadyToSendChanged = std::move(onReadyToSendChanged);
}

bool MosquittoConnection::isSubscribedToChannelTopic() const
{
    return subscribedToChannelTopic;
}

bool MosquittoConnection::isReadyToSend() const
{
    return readyToSend;
}

void MosquittoConnection::on_subscribe(int mid, int qos_count, const int* granted_qos)
{
    JOYNR_LOG_DEBUG(logger, "Subscribed (mid: {} with granted QOS {}", mid, granted_qos[0]);

    for (int i = 1; i < qos_count; i++) {
        JOYNR_LOG_DEBUG(logger, "QOS: {} granted {}", i, granted_qos[i]);
    }

    if (mid == subscribeChannelMid) {
        subscribedToChannelTopic = true;
        setReadyToSend(isConnected);
    }
}

void MosquittoConnection::on_message(const mosquitto_message* message)
{
    if (!onMessageReceived) {
        JOYNR_LOG_ERROR(
                logger, "Discarding received message, since onMessageReceived callback is empty.");
        return;
    }

    std::uint8_t* data = static_cast<std::uint8_t*>(message->payload);
    smrf::ByteVector rawMessage(data, data + message->payloadlen);
    onMessageReceived(std::move(rawMessage));
}

void MosquittoConnection::on_publish(int mid)
{
    JOYNR_LOG_TRACE(logger, "published message with mid {}", std::to_string(mid));
}

void MosquittoConnection::setReadyToSend(bool readyToSend)
{
    if (this->readyToSend != readyToSend) {
        this->readyToSend = readyToSend;

        std::lock_guard<std::mutex> lock(onReadyToSendChangedMutex);
        if (onReadyToSendChanged) {
            onReadyToSendChanged(readyToSend);
        }
    }
}

} // namespace joynr
<|endoftext|>


<commit_before>// ========================================================================== //
// This file is part of Sara, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2018 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

//! @file

#pragma once

#include <DO/Sara/Core/ArrayIterators/CoordinatesIterator.hpp>
#include <DO/Sara/Core/MultiArray/Padding.hpp>

namespace DO { namespace Sara {

  template <typename ArrayView>
  class InfiniteArrayIterator
  {
  public:
    using self_type = InfiniteArrayIterator;
    using vector_type = typename ArrayView::vector_type;
    using value_type = typename ArrayView::value_type;

  public:
    inline InfiniteArrayIterator(const ArrayView& f,    //
                                 const vector_type& a,  //
                                 const vector_type& b)
      : _f{f}
      , _x{a, b}
    {
    }

    //! Dereferencing operator.
    inline value_type operator*() const
    {
      return _f(*_x);
    }

    //! Prefix increment operator.
    inline self_type& operator++()
    {
      ++_x;
      return *this;
    }

    //! Prefix decrement operator.
    inline self_type& operator--()
    {
      --_x;
      return *this;
    }

    //! Postfix increment operator.
    inline self_type operator++(int)
    {
      self_type old{*this};
      operator++();
      return old;
    }

    //! Postfix increment operator.
    inline self_type operator--(int)
    {
      self_type old{*this};
      operator--();
      return old;
    }

    //! Arithmetic operator (slow).
    inline self_type operator+=(const vector_type& offset)
    {
      _x += offset;
      return *this;
    }

    //! Arithmetic operator (slow).
    inline self_type operator-=(const vector_type& offset)
    {
      _x +=(-offset);
      return *this;
    }

    inline bool end() const
    {
      return _x.end();
    }

    inline auto position() const -> const vector_type&
    {
      return *_x;
    }

  private:
    const ArrayView& _f;
    CoordinatesIterator<ArrayView> _x;
  };

  template <typename ArrayView>
  class InfiniteSteppedArrayIterator
  {
  public:
    using self_type = InfiniteSteppedArrayIterator;
    using vector_type = typename ArrayView::vector_type;
    using value_type = typename ArrayView::value_type;

  public:
    inline InfiniteSteppedArrayIterator(const ArrayView& f,        //
                                        const vector_type& a,      //
                                        const vector_type& b,      //
                                        const vector_type& steps)  //
      : _f{f}
      , _x{a, b, steps}
    {
    }

    //! Dereferencing operator.
    inline value_type operator*() const
    {
      return _f(*_x);
    }

    //! Prefix increment operator.
    inline self_type& operator++()
    {
      ++_x;
      return *this;
    }

    //! Prefix decrement operator.
    inline self_type& operator--()
    {
      --_x;
      return *this;
    }

    //! Postfix increment operator.
    inline self_type operator++(int)
    {
      self_type old{*this};
      operator++();
      return old;
    }

    //! Postfix increment operator.
    inline self_type operator--(int)
    {
      self_type old{*this};
      operator--();
      return old;
    }

    //! Arithmetic operator (slow).
    inline self_type operator+=(const vector_type& offset)
    {
      _x += offset;
      return *this;
    }

    //! Arithmetic operator (slow).
    inline self_type operator-=(const vector_type& offset)
    {
      _x +=(-offset);
      return *this;
    }

    inline bool end() const
    {
      return _x.end();
    }

    inline auto position() const -> const vector_type&
    {
      return *_x;
    }

    inline auto stepped_subarray_sizes() const -> vector_type
    {
      return _x.stepped_subarray_sizes();
    }

  private:
    const ArrayView& _f;
    SteppedCoordinatesIterator<ArrayView> _x;
  };

  template <typename ArrayView, typename Padding>
  class InfiniteMultiArrayView
  {
  public:
    using vector_type = typename ArrayView::vector_type;
    using value_type = typename ArrayView::value_type;

    enum { StorageOrder = ArrayView::StorageOrder };
    enum { Dimension = ArrayView::Dimension };

    inline InfiniteMultiArrayView(const ArrayView& f, const Padding& pad)
      : _f(f)
      , _pad{pad}
    {
    }

    inline auto operator()(const vector_type& x) const -> value_type
    {
      return _pad.at(_f, x);
    }

    inline auto begin_subarray(const vector_type& a, const vector_type& b) const
        -> InfiniteArrayIterator<InfiniteMultiArrayView>
    {
      return {*this, a, b};
    }

    inline auto begin_stepped_subarray(const vector_type& a,  //
                                       const vector_type& b,  //
                                       const vector_type& steps) const
        -> InfiniteSteppedArrayIterator<InfiniteMultiArrayView>
    {
      return {*this, a, b, steps};
    }

  private:
    const ArrayView& _f;
    Padding _pad;
  };

  template <typename ArrayView, typename Padding>
  inline auto make_infinite(const ArrayView& f, const Padding& pad)
      -> InfiniteMultiArrayView<ArrayView, Padding>
  {
    return {f, pad};
  }

} /* namespace Sara */
} /* namespace DO */
<commit_msg>MAINT: fix compile errors for Travis CI.<commit_after>// ========================================================================== //
// This file is part of Sara, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2018 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

//! @file

#pragma once

#include <DO/Sara/Core/ArrayIterators/CoordinatesIterator.hpp>
#include <DO/Sara/Core/MultiArray/Padding.hpp>

namespace DO { namespace Sara {

  template <typename ArrayView>
  class InfiniteArrayIterator
  {
  public:
    using self_type = InfiniteArrayIterator;
    using vector_type = typename ArrayView::vector_type;
    using value_type = typename ArrayView::value_type;

  public:
    inline InfiniteArrayIterator(const ArrayView& f,    //
                                 const vector_type& a,  //
                                 const vector_type& b)
      : _f{f}
      , _x{a, b}
    {
    }

    //! Dereferencing operator.
    inline value_type operator*() const
    {
      return _f(*_x);
    }

    //! Prefix increment operator.
    inline self_type& operator++()
    {
      ++_x;
      return *this;
    }

    //! Prefix decrement operator.
    inline self_type& operator--()
    {
      --_x;
      return *this;
    }

    //! Postfix increment operator.
    inline self_type operator++(int)
    {
      self_type old{*this};
      operator++();
      return old;
    }

    //! Postfix increment operator.
    inline self_type operator--(int)
    {
      self_type old{*this};
      operator--();
      return old;
    }

    //! Arithmetic operator (slow).
    inline self_type operator+=(const vector_type& offset)
    {
      _x += offset;
      return *this;
    }

    //! Arithmetic operator (slow).
    inline self_type operator-=(const vector_type& offset)
    {
      _x +=(-offset);
      return *this;
    }

    inline bool end() const
    {
      return _x.end();
    }

    inline auto position() const -> const vector_type&
    {
      return *_x;
    }

  private:
    const ArrayView& _f;
    CoordinatesIterator<ArrayView> _x;
  };

  template <typename ArrayView>
  class InfiniteSteppedArrayIterator
  {
  public:
    using self_type = InfiniteSteppedArrayIterator;
    using vector_type = typename ArrayView::vector_type;
    using value_type = typename ArrayView::value_type;

  public:
    inline InfiniteSteppedArrayIterator(const ArrayView& f,        //
                                        const vector_type& a,      //
                                        const vector_type& b,      //
                                        const vector_type& steps)  //
      : _f{f}
      , _x{a, b, steps}
    {
    }

    //! Dereferencing operator.
    inline value_type operator*() const
    {
      return _f(*_x);
    }

    //! Prefix increment operator.
    inline self_type& operator++()
    {
      ++_x;
      return *this;
    }

    //! Prefix decrement operator.
    inline self_type& operator--()
    {
      --_x;
      return *this;
    }

    //! Postfix increment operator.
    inline self_type operator++(int)
    {
      self_type old{*this};
      operator++();
      return old;
    }

    //! Postfix increment operator.
    inline self_type operator--(int)
    {
      self_type old{*this};
      operator--();
      return old;
    }

    //! Arithmetic operator (slow).
    inline self_type operator+=(const vector_type& offset)
    {
      _x += offset;
      return *this;
    }

    //! Arithmetic operator (slow).
    inline self_type operator-=(const vector_type& offset)
    {
      _x +=(-offset);
      return *this;
    }

    inline bool end() const
    {
      return _x.end();
    }

    inline auto position() const -> const vector_type&
    {
      return *_x;
    }

    inline auto stepped_subarray_sizes() const -> vector_type
    {
      return _x.stepped_subarray_sizes();
    }

  private:
    const ArrayView& _f;
    SteppedCoordinatesIterator<ArrayView> _x;
  };

  template <typename ArrayView, typename Padding>
  class InfiniteMultiArrayView
  {
  public:
    using vector_type = typename ArrayView::vector_type;
    using value_type = typename ArrayView::value_type;

    enum { StorageOrder = ArrayView::StorageOrder };
    enum { Dimension = ArrayView::Dimension };

    inline InfiniteMultiArrayView(const ArrayView& f, const Padding& pad)
      : _f(f)
      , _pad(pad)
    {
    }

    inline auto operator()(const vector_type& x) const -> value_type
    {
      return _pad.at(_f, x);
    }

    inline auto begin_subarray(const vector_type& a, const vector_type& b) const
        -> InfiniteArrayIterator<InfiniteMultiArrayView>
    {
      return {*this, a, b};
    }

    inline auto begin_stepped_subarray(const vector_type& a,  //
                                       const vector_type& b,  //
                                       const vector_type& steps) const
        -> InfiniteSteppedArrayIterator<InfiniteMultiArrayView>
    {
      return {*this, a, b, steps};
    }

  private:
    const ArrayView& _f;
    Padding _pad;
  };

  template <typename ArrayView, typename Padding>
  inline auto make_infinite(const ArrayView& f, const Padding& pad)
      -> InfiniteMultiArrayView<ArrayView, Padding>
  {
    return {f, pad};
  }

} /* namespace Sara */
} /* namespace DO */
<|endoftext|>


<commit_before>// Copyright 2022 Google LLC
//
// 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 "ink_stroke_modeler/internal/stylus_state_modeler.h"

#include <limits>

#include "ink_stroke_modeler/internal/internal_types.h"
#include "ink_stroke_modeler/internal/utils.h"
#include "ink_stroke_modeler/params.h"
#include "ink_stroke_modeler/types.h"

namespace ink {
namespace stroke_model {

void StylusStateModeler::Update(Vec2 position, const StylusState &state) {
  if (state.pressure < 0) received_unknown_pressure_ = true;
  if (state.tilt < 0) received_unknown_tilt_ = true;
  if (state.orientation < 0) received_unknown_orientation_ = true;

  if (received_unknown_pressure_ && received_unknown_tilt_ &&
      received_unknown_orientation_) {
    // We've stopped tracking all fields, so there's no need to keep updating.
    positions_and_states_.clear();
    return;
  }

  positions_and_states_.push_back({position, state});

  if (params_.max_input_samples < 0 ||
      positions_and_states_.size() > (uint)params_.max_input_samples) {
    positions_and_states_.pop_front();
  }
}

void StylusStateModeler::Reset(const StylusStateModelerParams &params) {
  params_ = params;
  positions_and_states_.clear();
  received_unknown_pressure_ = false;
  received_unknown_tilt_ = false;
  received_unknown_orientation_ = false;
}

StylusState StylusStateModeler::Query(Vec2 position) const {
  if (positions_and_states_.empty())
    return {.pressure = -1, .tilt = -1, .orientation = -1};

  if (positions_and_states_.size() == 1) {
    const auto &state = positions_and_states_.front().state;
    return {
        .pressure = received_unknown_pressure_ ? -1 : state.pressure,
        .tilt = received_unknown_tilt_ ? -1 : state.tilt,
        .orientation = received_unknown_orientation_ ? -1 : state.orientation};
  }

  int closest_segment = -1;
  float min_distance = std::numeric_limits<float>::infinity();
  float interp_value = 0;
  for (decltype(positions_and_states_.size()) i = 0;
       i < positions_and_states_.size() - 1; ++i) {
    const Vec2 segment_start = positions_and_states_[i].position;
    const Vec2 segment_end = positions_and_states_[i + 1].position;
    float param = NearestPointOnSegment(segment_start, segment_end, position);
    float distance =
        Distance(position, Interp(segment_start, segment_end, param));
    if (distance <= min_distance) {
      closest_segment = i;
      min_distance = distance;
      interp_value = param;
    }
  }

  auto from_state = positions_and_states_[closest_segment].state;
  auto to_state = positions_and_states_[closest_segment + 1].state;
  return StylusState{
      .pressure =
          received_unknown_pressure_
              ? -1
              : Interp(from_state.pressure, to_state.pressure, interp_value),
      .tilt = received_unknown_tilt_
                  ? -1
                  : Interp(from_state.tilt, to_state.tilt, interp_value),
      .orientation = received_unknown_orientation_
                         ? -1
                         : InterpAngle(from_state.orientation,
                                       to_state.orientation, interp_value)};
}

}  // namespace stroke_model
}  // namespace ink
<commit_msg>Avoid segfault in StylusStateModeler::Query<commit_after>// Copyright 2022 Google LLC
//
// 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 "ink_stroke_modeler/internal/stylus_state_modeler.h"

#include <limits>

#include "ink_stroke_modeler/internal/internal_types.h"
#include "ink_stroke_modeler/internal/utils.h"
#include "ink_stroke_modeler/params.h"
#include "ink_stroke_modeler/types.h"

namespace ink {
namespace stroke_model {

void StylusStateModeler::Update(Vec2 position, const StylusState &state) {
  if (state.pressure < 0) received_unknown_pressure_ = true;
  if (state.tilt < 0) received_unknown_tilt_ = true;
  if (state.orientation < 0) received_unknown_orientation_ = true;

  if (received_unknown_pressure_ && received_unknown_tilt_ &&
      received_unknown_orientation_) {
    // We've stopped tracking all fields, so there's no need to keep updating.
    positions_and_states_.clear();
    return;
  }

  positions_and_states_.push_back({position, state});

  if (params_.max_input_samples < 0 ||
      positions_and_states_.size() > (uint)params_.max_input_samples) {
    positions_and_states_.pop_front();
  }
}

void StylusStateModeler::Reset(const StylusStateModelerParams &params) {
  params_ = params;
  positions_and_states_.clear();
  received_unknown_pressure_ = false;
  received_unknown_tilt_ = false;
  received_unknown_orientation_ = false;
}

StylusState StylusStateModeler::Query(Vec2 position) const {
  if (positions_and_states_.empty())
    return {.pressure = -1, .tilt = -1, .orientation = -1};

  int closest_segment_index = -1;
  float min_distance = std::numeric_limits<float>::infinity();
  float interp_value = 0;
  for (decltype(positions_and_states_.size()) i = 0;
       i < positions_and_states_.size() - 1; ++i) {
    const Vec2 segment_start = positions_and_states_[i].position;
    const Vec2 segment_end = positions_and_states_[i + 1].position;
    float param = NearestPointOnSegment(segment_start, segment_end, position);
    float distance =
        Distance(position, Interp(segment_start, segment_end, param));
    if (distance <= min_distance) {
      closest_segment_index = i;
      min_distance = distance;
      interp_value = param;
    }
  }

  if (closest_segment_index < 0) {
    const auto &state = positions_and_states_.front().state;
    return {
        .pressure = received_unknown_pressure_ ? -1 : state.pressure,
        .tilt = received_unknown_tilt_ ? -1 : state.tilt,
        .orientation = received_unknown_orientation_ ? -1 : state.orientation};
  }

  auto from_state = positions_and_states_[closest_segment_index].state;
  auto to_state = positions_and_states_[closest_segment_index + 1].state;
  return StylusState{
      .pressure =
          received_unknown_pressure_
              ? -1
              : Interp(from_state.pressure, to_state.pressure, interp_value),
      .tilt = received_unknown_tilt_
                  ? -1
                  : Interp(from_state.tilt, to_state.tilt, interp_value),
      .orientation = received_unknown_orientation_
                         ? -1
                         : InterpAngle(from_state.orientation,
                                       to_state.orientation, interp_value)};
}

}  // namespace stroke_model
}  // namespace ink
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2011, Image Engine Design Inc. All rights reserved.
//  Copyright (c) 2012, John Haddon. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "IECorePython/IECoreBinding.h"
#include "IECorePython/ScopedGILLock.h"

#include "maya/MFnDagNode.h"
#include "maya/MBoundingBox.h"
#include "maya/MPlugArray.h"
#include "maya/MItDependencyGraph.h"
#include "maya/MIteratorType.h"

#include "extension/Extension.h"
#include "translators/shape/ShapeTranslator.h"

#include "IECore/CompoundParameter.h"

#include "IECoreMaya/ProceduralHolder.h"
#include "IECoreMaya/PythonCmd.h"

class ProceduralHolderTranslator : public CShapeTranslator
{

	public :

		virtual AtNode *CreateArnoldNodes()
		{
			return AddArnoldNode( "procedural" );
		}

		virtual void Export( AtNode *node )
		{
			// do basic node export

			ExportMatrix( node, 0 );

			AtNode *shader = arnoldShader();
			if( shader )
			{
				AiNodeSetPtr( node, "shader", shader );
			}

			AiNodeSetInt( node, "visibility", ComputeVisibility() );

			MPlug plug = FindMayaObjectPlug( "receiveShadows" );
			if( !plug.isNull() )
			{
				AiNodeSetBool( node, "receive_shadows", plug.asBool() );
			}

			plug = FindMayaObjectPlug( "aiSelfShadows" );
			if( !plug.isNull() )
			{
				AiNodeSetBool( node, "self_shadows", plug.asBool() );
			}

			plug = FindMayaObjectPlug( "aiOpaque" );
			if( !plug.isNull() )
			{
				AiNodeSetBool( node, "opaque", plug.asBool() );
			}

			// export any shading groups or displacement shaders which look like they
			// may be connected to procedural parameters. this ensures that maya shaders
			// the procedural will expect to find at rendertime will be exported to the
			// ass file (they otherwise might not be if they're not assigned to any objects).

			exportShadingInputs();

			// now set the procedural-specific parameters

			MFnDagNode fnDagNode( m_dagPath );
			MBoundingBox bound = fnDagNode.boundingBox();

			AiNodeSetPnt( node, "min", bound.min().x, bound.min().y, bound.min().z );
			AiNodeSetPnt( node, "max", bound.max().x, bound.max().y, bound.max().z );

			const char *dsoPath = getenv( "IECOREARNOLD_PROCEDURAL_PATH" );
			AiNodeSetStr( node, "dso", dsoPath ? dsoPath : "ieProcedural.so" );

			AiNodeDeclare( node, "className", "constant STRING" );
			AiNodeDeclare( node, "classVersion", "constant INT" );
			AiNodeDeclare( node, "parameterValues", "constant ARRAY STRING" );

			// cast should be ok as we're registered to only work on procedural holders
			IECoreMaya::ProceduralHolder *pHolder = static_cast<IECoreMaya::ProceduralHolder *>( fnDagNode.userNode() );

			std::string className;
			int classVersion;
			IECore::ParameterisedProceduralPtr procedural = pHolder->getProcedural( &className, &classVersion );

			AiNodeSetStr( node, "className", className.c_str() );
			AiNodeSetInt( node, "classVersion", classVersion );

			IECorePython::ScopedGILLock gilLock;
			try
			{
				boost::python::object parser = IECoreMaya::PythonCmd::globalContext()["IECore"].attr( "ParameterParser" )();
				boost::python::object serialised = parser.attr( "serialise" )( procedural->parameters() );

				size_t numStrings = IECorePython::len( serialised );
				AtArray *stringArray = AiArrayAllocate( numStrings, 1, AI_TYPE_STRING );
				for( size_t i=0; i<numStrings; i++ )
				{
					std::string s = boost::python::extract<std::string>( serialised[i] );
					// hack to workaround ass parsing errors
					/// \todo Remove when we get the Arnold version that fixes this
					for( size_t c = 0; c<s.size(); c++ )
					{
						if( s[c] == '#' )
						{
							s[c] = '@';
						}
					}
					AiArraySetStr( stringArray, i, s.c_str() );
				}

				AiNodeSetArray( node, "parameterValues", stringArray );
			}
			catch( boost::python::error_already_set )
			{
				PyErr_Print();
			}

		}

		virtual bool RequiresMotionData()
		{
			return IsMotionBlurEnabled( MTOA_MBLUR_OBJECT ) && IsLocalMotionBlurEnabled();
		}

		virtual void ExportMotion( AtNode *node, AtUInt step )
		{
   			if( !IsMotionBlurEnabled() )
			{
				return;
			}

			ExportMatrix( node, step );
		}

		static void nodeInitialiser( CAbTranslator context )
		{
			CExtensionAttrHelper helper( context.maya, "procedural" );
			MakeArnoldVisibilityFlags( helper );

			helper.MakeInput( "self_shadows" );
			helper.MakeInput( "opaque" );

		}

		static void *creator()
		{
			return new ProceduralHolderTranslator();
		}

	protected :

		/// Returns the arnold shader to assign to the procedural.
		AtNode *arnoldShader()
		{
			bool overrideShaders = false;
			MPlug plug = FindMayaObjectPlug( "overrideProceduralShaders" );
			if( !plug.isNull() )
			{
				// if we've been told explicitly not to override the shaders
				// in the procedurals, then early out.
				overrideShaders = plug.asBool();
				if( !overrideShaders )
				{
					return 0;
				}
			}

			unsigned instNumber = m_dagPath.isInstanced() ? m_dagPath.instanceNumber() : 0;
			MPlug shadingGroupPlug = GetNodeShadingGroup(m_dagPath.node(), instNumber);

			if( !overrideShaders )
			{
				// if we weren't explicitly told to override the shaders, then
				// decide whether to or not based on whether a non-default
				// shader has been applied to the shape by the user.
				MObject shadingGroupNode = shadingGroupPlug.node();
				MFnDependencyNode fnShadingGroupNode( shadingGroupNode );
				if( fnShadingGroupNode.name() != "initialShadingGroup" )
				{
					overrideShaders = true;
				}
			}

			if( overrideShaders )
			{
				return ExportNode( shadingGroupPlug );
			}
			else
			{
				return 0;
			}
		}

		void exportShadingInputs()
		{
			MObject proceduralNode = m_dagPath.node();
			MPlug nullPlug;

			MIteratorType filter;
			MIntArray filterTypes;
			filterTypes.append( MFn::kShadingEngine );
			filterTypes.append( MFn::kDisplacementShader );
			filter.setFilterList( filterTypes );

			MItDependencyGraph itDG( proceduralNode, nullPlug, filter, MItDependencyGraph::kUpstream );
			while( !itDG.isDone() )
			{
				MObject node = itDG.currentItem();
				MFnDependencyNode fnNode( node );
				MPlug plug;
				if( fnNode.typeName() == "displacementShader" )
				{
					plug = fnNode.findPlug( "displacement" );
				}
				else
				{
					plug = fnNode.findPlug( "dsm" );
				}
				ExportNode( plug );
				itDG.next();
			}
		}

};

extern "C"
{

DLLEXPORT void initializeExtension( CExtension &extension )
{
	extension.Requires( "ieCore" );
	extension.RegisterTranslator(
		"ieProceduralHolder",
		"",
		ProceduralHolderTranslator::creator,
		ProceduralHolderTranslator::nodeInitialiser
	);
}

DLLEXPORT void deinitializeExtension( CExtension &extension )
{
}

} // extern "C"

<commit_msg>made MtoA acknoleged instances and generate ginstance nodes in ass output<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2011, Image Engine Design Inc. All rights reserved.
//  Copyright (c) 2012, John Haddon. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "IECorePython/IECoreBinding.h"
#include "IECorePython/ScopedGILLock.h"

#include "maya/MFnDagNode.h"
#include "maya/MBoundingBox.h"
#include "maya/MPlugArray.h"
#include "maya/MItDependencyGraph.h"
#include "maya/MIteratorType.h"

#include "extension/Extension.h"
#include "translators/shape/ShapeTranslator.h"

#include "IECore/CompoundParameter.h"

#include "IECoreMaya/ProceduralHolder.h"
#include "IECoreMaya/PythonCmd.h"

class ProceduralHolderTranslator : public CShapeTranslator
{

	public :

		virtual AtNode *CreateArnoldNodes()
		{
                  m_isMasterDag =  IsMasterInstance(m_masterDag);

                  if (m_isMasterDag)
                  {

			return AddArnoldNode( "procedural" );
                  }
                  else
                  {
                        return AddArnoldNode( "ginstance" );
                  }
		}

		virtual void Export( AtNode *node )
                {
                  const char* nodeType = AiNodeEntryGetName(AiNodeGetNodeEntry(node));
                  if (strcmp(nodeType, "ginstance") == 0)
                  {
                     ExportInstance(node, m_masterDag);
                  }
                  else
                  {
                     ExportProcedural(node);
                  }
                }

                virtual AtNode* ExportInstance(AtNode *instance, const MDagPath& masterInstance)
                {
                   AtNode* masterNode = AiNodeLookUpByName(masterInstance.partialPathName().asChar());

                   int instanceNum = m_dagPath.instanceNumber();

                   if ( instanceNum > 0 )
                     {
                       AiNodeSetStr(instance, "name", m_dagPath.partialPathName().asChar());

                       ExportMatrix(instance, 0);

                       AiNodeSetPtr(instance, "node", masterNode);
                       AiNodeSetBool(instance, "inherit_xform", false);
                       int visibility = AiNodeGetInt(masterNode, "visibility");
                       AiNodeSetInt(instance, "visibility", visibility);

                       AiNodeSetPtr( instance, "shader", arnoldShader() );

                       // Export light linking per instance
                       ExportLightLinking(instance);
                     }
                   return instance;
                }

		virtual void ExportProcedural( AtNode *node )
		{
			// do basic node export

			ExportMatrix( node, 0 );

			AtNode *shader = arnoldShader();
			if( shader )
			{
				AiNodeSetPtr( node, "shader", shader );
			}

			AiNodeSetInt( node, "visibility", ComputeVisibility() );

			MPlug plug = FindMayaObjectPlug( "receiveShadows" );
			if( !plug.isNull() )
			{
				AiNodeSetBool( node, "receive_shadows", plug.asBool() );
			}

			plug = FindMayaObjectPlug( "aiSelfShadows" );
			if( !plug.isNull() )
			{
				AiNodeSetBool( node, "self_shadows", plug.asBool() );
			}

			plug = FindMayaObjectPlug( "aiOpaque" );
			if( !plug.isNull() )
			{
				AiNodeSetBool( node, "opaque", plug.asBool() );
			}

			// export any shading groups or displacement shaders which look like they
			// may be connected to procedural parameters. this ensures that maya shaders
			// the procedural will expect to find at rendertime will be exported to the
			// ass file (they otherwise might not be if they're not assigned to any objects).

			exportShadingInputs();

			// now set the procedural-specific parameters

			MFnDagNode fnDagNode( m_dagPath );
			MBoundingBox bound = fnDagNode.boundingBox();

			AiNodeSetPnt( node, "min", bound.min().x, bound.min().y, bound.min().z );
			AiNodeSetPnt( node, "max", bound.max().x, bound.max().y, bound.max().z );

			const char *dsoPath = getenv( "IECOREARNOLD_PROCEDURAL_PATH" );
			AiNodeSetStr( node, "dso", dsoPath ? dsoPath : "ieProcedural.so" );

			AiNodeDeclare( node, "className", "constant STRING" );
			AiNodeDeclare( node, "classVersion", "constant INT" );
			AiNodeDeclare( node, "parameterValues", "constant ARRAY STRING" );

			// cast should be ok as we're registered to only work on procedural holders
			IECoreMaya::ProceduralHolder *pHolder = static_cast<IECoreMaya::ProceduralHolder *>( fnDagNode.userNode() );

			std::string className;
			int classVersion;
			IECore::ParameterisedProceduralPtr procedural = pHolder->getProcedural( &className, &classVersion );

			AiNodeSetStr( node, "className", className.c_str() );
			AiNodeSetInt( node, "classVersion", classVersion );

			IECorePython::ScopedGILLock gilLock;
			try
			{
				boost::python::object parser = IECoreMaya::PythonCmd::globalContext()["IECore"].attr( "ParameterParser" )();
				boost::python::object serialised = parser.attr( "serialise" )( procedural->parameters() );

				size_t numStrings = IECorePython::len( serialised );
				AtArray *stringArray = AiArrayAllocate( numStrings, 1, AI_TYPE_STRING );
				for( size_t i=0; i<numStrings; i++ )
				{
					std::string s = boost::python::extract<std::string>( serialised[i] );
					// hack to workaround ass parsing errors
					/// \todo Remove when we get the Arnold version that fixes this
					for( size_t c = 0; c<s.size(); c++ )
					{
						if( s[c] == '#' )
						{
							s[c] = '@';
						}
					}
					AiArraySetStr( stringArray, i, s.c_str() );
				}

				AiNodeSetArray( node, "parameterValues", stringArray );
			}
			catch( boost::python::error_already_set )
			{
				PyErr_Print();
			}

		}

		virtual bool RequiresMotionData()
		{
			return IsMotionBlurEnabled( MTOA_MBLUR_OBJECT ) && IsLocalMotionBlurEnabled();
		}

		virtual void ExportMotion( AtNode *node, AtUInt step )
		{
   			if( !IsMotionBlurEnabled() )
			{
				return;
			}

			ExportMatrix( node, step );
		}

		static void nodeInitialiser( CAbTranslator context )
		{
			CExtensionAttrHelper helper( context.maya, "procedural" );
			MakeArnoldVisibilityFlags( helper );

			helper.MakeInput( "self_shadows" );
			helper.MakeInput( "opaque" );

		}

		static void *creator()
		{
			return new ProceduralHolderTranslator();
		}

	protected :

		/// Returns the arnold shader to assign to the procedural.
		AtNode *arnoldShader()
		{
			bool overrideShaders = false;
			MPlug plug = FindMayaObjectPlug( "overrideProceduralShaders" );
			if( !plug.isNull() )
			{
				// if we've been told explicitly not to override the shaders
				// in the procedurals, then early out.
				overrideShaders = plug.asBool();
				if( !overrideShaders )
				{
					return 0;
				}
			}

			unsigned instNumber = m_dagPath.isInstanced() ? m_dagPath.instanceNumber() : 0;
			MPlug shadingGroupPlug = GetNodeShadingGroup(m_dagPath.node(), instNumber);

			if( !overrideShaders )
			{
				// if we weren't explicitly told to override the shaders, then
				// decide whether to or not based on whether a non-default
				// shader has been applied to the shape by the user.
				MObject shadingGroupNode = shadingGroupPlug.node();
				MFnDependencyNode fnShadingGroupNode( shadingGroupNode );
				if( fnShadingGroupNode.name() != "initialShadingGroup" )
				{
					overrideShaders = true;
				}
			}

			if( overrideShaders )
			{
				return ExportNode( shadingGroupPlug );
			}
			else
			{
				return 0;
			}
		}

		void exportShadingInputs()
		{
			MObject proceduralNode = m_dagPath.node();
			MPlug nullPlug;

			MIteratorType filter;
			MIntArray filterTypes;
			filterTypes.append( MFn::kShadingEngine );
			filterTypes.append( MFn::kDisplacementShader );
			filter.setFilterList( filterTypes );

			MItDependencyGraph itDG( proceduralNode, nullPlug, filter, MItDependencyGraph::kUpstream );
			while( !itDG.isDone() )
			{
				MObject node = itDG.currentItem();
				MFnDependencyNode fnNode( node );
				MPlug plug;
				if( fnNode.typeName() == "displacementShader" )
				{
					plug = fnNode.findPlug( "displacement" );
				}
				else
				{
					plug = fnNode.findPlug( "dsm" );
				}
				ExportNode( plug );
				itDG.next();
			}
		}

        protected :
                bool m_isMasterDag;
                MDagPath m_masterDag;

};

extern "C"
{

DLLEXPORT void initializeExtension( CExtension &extension )
{
	extension.Requires( "ieCore" );
	extension.RegisterTranslator(
		"ieProceduralHolder",
		"",
		ProceduralHolderTranslator::creator,
		ProceduralHolderTranslator::nodeInitialiser
	);
}

DLLEXPORT void deinitializeExtension( CExtension &extension )
{
}

} // extern "C"

<|endoftext|>


<commit_before>#pragma once
//=====================================================================//
/*!	@file
	@brief	libmad を使った MP3 デコード・クラス @n
			※このクラスを使うには、libmad ライブラリーが必要
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=====================================================================//
#include <mad.h>
#include "common/file_io.hpp"
#include "sound/audio_out.hpp"
#include "sound/id3_mgr.hpp"

extern "C" {
	void set_sample_rate(uint32_t freq);
};

namespace audio {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief	MP3 形式デコード・クラス
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	class mp3_in {
	public:
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	制御列挙型
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		enum class CTRL {
			NONE,		///< 何もしない
			STOP,		///< 停止
			PAUSE,		///< 一時停止
			NEXT,		///< 次の曲
			REPLAY,		///< 曲の先頭に戻って再生
		};

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	制御タスク型
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		typedef CTRL (*CTRL_TASK)();

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	情報タスク型
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		typedef void (*TAG_TASK)(const tag_t&);

	private:
		static const uint32_t INPUT_BUFFER_SIZE = 2048;

		mad_stream	mad_stream_;
		mad_frame	mad_frame_;
		mad_synth	mad_synth_;
		mad_timer_t	mad_timer_;

		uint8_t		input_buffer_[INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD];

		// サブバンド領域フィルター特性用。
		mad_fixed_t		subband_filter_[32];
		bool			subband_filter_enable_;
		bool			id3v1_;

		uint32_t		time_;

		CTRL_TASK		ctrl_task_;

		TAG_TASK		tag_task_;

		int fill_read_buffer_(utils::file_io& fin, mad_stream& strm)
 		{
			/* The input bucket must be filled if it becomes empty or if
			 * it's the first execution of the loop.
			 */
			if(strm.buffer == NULL || strm.error == MAD_ERROR_BUFLEN) {
				size_t size;
				size_t remaining;
				unsigned char* ptr;

				/* {2} libmad may not consume all bytes of the input
				 * buffer. If the last frame in the buffer is not wholly
				 * contained by it, then that frame's start is pointed by
				 * the next_frame member of the Stream structure. This
				 * common situation occurs when mad_frame_decode() fails,
				 * sets the stream error code to MAD_ERROR_BUFLEN, and
				 * sets the next_frame pointer to a non NULL value. (See
				 * also the comment marked {4} bellow.)
				 *
				 * When this occurs, the remaining unused bytes must be
				 * put back at the beginning of the buffer and taken in
				 * account before refilling the buffer. This means that
				 * the input buffer must be large enough to hold a whole
				 * frame at the highest observable bit-rate (currently 448
				 * kb/s). XXX=XXX Is 2016 bytes the size of the largest
				 * frame? (448000*(1152/32000))/8
				 */
				if(strm.next_frame != NULL) {
					remaining = strm.bufend - strm.next_frame;
					memmove(&input_buffer_[0], strm.next_frame, remaining);
					ptr  = &input_buffer_[remaining];
					size = INPUT_BUFFER_SIZE - remaining;
				} else {
					size = INPUT_BUFFER_SIZE;
					ptr  = &input_buffer_[0];
					remaining = 0;
				}

				/* Fill-in the buffer. If an error occurs print a message
				 * and leave the decoding loop. If the end of stream is
				 * reached we also leave the loop but the return status is
				 * left untouched.
				 */
				// ReadSize = BstdRead(ReadStart, 1, ReadSize, BstdFile);
				size_t req = size;
				size_t rs = fin.read(ptr, 1, req);
				if(id3v1_) {
					if(fin.tell() >= (fin.get_file_size() - 128)) return -1;
				} else {
					if(fin.eof()) return -1;
				}
				size = rs;
				if(rs < req) {
					memset(&ptr[rs], 0, MAD_BUFFER_GUARD);
					size += MAD_BUFFER_GUARD;
				}

				/* Pipe the new buffer content to libmad's stream decoder
				 * facility.
				 */
				mad_stream_buffer(&strm, &input_buffer_[0], size + remaining);
				strm.error = MAD_ERROR_NONE;
				return 0;
			} else {
				return 1;
			}
		}

		/****************************************************************************
		 * Applies a frequency-domain filter to audio data in the subband-domain.	*
		 ****************************************************************************/
		void apply_filter_(mad_frame& frame)
		{
			/* There is two application loops, each optimized for the number
			 * of audio channels to process. The first alternative is for
			 * two-channel frames, the second is for mono-audio.
			 */
			int num = MAD_NSBSAMPLES(&frame.header);
			if(frame.header.mode != MAD_MODE_SINGLE_CHANNEL) {
				for(int ch = 0; ch < 2; ++ch) {
					for(int s = 0; s < num; ++s) {
						for(int sb = 0; sb < 32; ++sb) {
							frame.sbsample[ch][s][sb] =
								mad_f_mul(frame.sbsample[ch][s][sb], subband_filter_[sb]);
						}
					}
				}
			} else {
				for(int s = 0; s < num; ++s) {
					for(int sb = 0; sb < 32; ++sb) {
						frame.sbsample[0][s][sb] =
							mad_f_mul(frame.sbsample[0][s][sb], subband_filter_[sb]);
					}
				}
			}
		}

		static const short SHRT_MAX_ = 32767;

		/****************************************************************************
		 * Converts a sample from mad's fixed point number format to a signed		*
		 * short (16 bits).															*
		 ****************************************************************************/
		static short MadFixedToSshort(mad_fixed_t v)
		{
			/* A fixed point number is formed of the following bit pattern:
			 *
			 * SWWWFFFFFFFFFFFFFFFFFFFFFFFFFFFF
			 * MSB                          LSB
			 * S ==> Sign (0 is positive, 1 is negative)
			 * W ==> Whole part bits
			 * F ==> Fractional part bits
			 *
			 * This pattern contains MAD_F_FRACBITS fractional bits, one
			 * should alway use this macro when working on the bits of a fixed
			 * point number. It is not guaranteed to be constant over the
			 * different platforms supported by libmad.
			 *
			 * The signed short value is formed, after clipping, by the least
			 * significant whole part bit, followed by the 15 most significant
			 * fractional part bits. Warning: this is a quick and dirty way to
			 * compute the 16-bit number, madplay includes much better
			 * algorithms.
			 */

			// Clipping..
			if(v >= MAD_F_ONE) {
				return SHRT_MAX_;
			}
			if(v <= -MAD_F_ONE) {
				return -SHRT_MAX_;
			}

			return (signed short)(v >> (MAD_F_FRACBITS - 15));
		}

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
		*/
		//-----------------------------------------------------------------//
		mp3_in() : subband_filter_enable_(false), id3v1_(false),
				   time_(0), ctrl_task_(nullptr), tag_task_(nullptr) { }

		//-----------------------------------------------------------------//
		/*!
			@brief	制御タスクの設定
			@param[in]	task	制御タスク
		*/
		//-----------------------------------------------------------------//
		void set_ctrl_task(CTRL_TASK task)
		{
			ctrl_task_ = task;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	タグ・タスクの設定
			@param[in]	task	タグ・タスク
		*/
		//-----------------------------------------------------------------//
		void set_tag_task(TAG_TASK task)
		{
			tag_task_ = task;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	デコード
			@param[in]	fin		file_io コンテキスト（参照）
			@param[in]	out		オーディオ出力（参照）
			@return 正常終了なら「true」
		*/
		//-----------------------------------------------------------------//
		template <class AUDIO_OUT>
		bool decode(utils::file_io& fin, AUDIO_OUT& out)
		{
			id3_mgr id3;
			id3.analize(fin);
			if(tag_task_ != nullptr) {
				const auto& tag = id3.get_tag();
				(*tag_task_)(tag);
//				utils::format("Album:  '%s'\n") % tag.album_;
//				utils::format("Title:  '%s'\n") % tag.title_;
//				utils::format("Artist: '%s'\n") % tag.artist_;
//				utils::format("Year:    %s\n") % tag.year_;
//				utils::format("Track:   %s\n") % tag.track_;
			}

			mad_stream_init(&mad_stream_);
			mad_frame_init(&mad_frame_);
			mad_synth_init(&mad_synth_);
			mad_timer_reset(&mad_timer_);

			uint32_t forg = fin.tell();
			bool info = false;
			uint32_t pos = 0;
			uint32_t frame_count = 0;
			bool status = true;
			bool pause = false;
			while(fill_read_buffer_(fin, mad_stream_) >= 0) {

				CTRL ctrl = CTRL::NONE;
				if(ctrl_task_ != nullptr) {
					ctrl = (*ctrl_task_)();
				}
				if(ctrl == CTRL::NEXT) {
					out.mute();
					break;
				} else if(ctrl == CTRL::REPLAY) {
					out.mute();
					fin.seek(utils::file_io::SEEK::SET, forg);
					info = false;
					pos = 0;
					time_ = 0;
					frame_count = 0;
					status = true;
					pause = false;
					continue;
				} else if(ctrl == CTRL::PAUSE) {
					out.mute();
					pause = !pause;
				} else if(ctrl == CTRL::STOP) {
					out.mute();
					status = false;
					break;
				}
				if(pause) {
					utils::delay::milli_second(5);
					continue;
				}

				if(mad_frame_decode(&mad_frame_, &mad_stream_)) {
					if(MAD_RECOVERABLE(mad_stream_.error)) {
						continue;
					} else {
						if(mad_stream_.error == MAD_ERROR_BUFLEN) {
							continue;
						} else {
							status = false;
							break;
						}
					}
				}

				if(!info) {
					set_sample_rate(mad_frame_.header.samplerate);
					utils::format("Sample Rate: %d\n") % mad_frame_.header.samplerate;
					info = true;
				}

				frame_count++;
				mad_timer_add(&mad_timer_, mad_frame_.header.duration);

				if(subband_filter_enable_) {
					apply_filter_(mad_frame_);
				}

				mad_synth_frame(&mad_synth_, &mad_frame_);

				for(uint32_t i = 0; i < mad_synth_.pcm.length; ++i) {
					while((out.at_fifo().size() - out.at_fifo().length()) < 8) {
					}
					audio::wave_t t;
					if(MAD_NCHANNELS(&mad_frame_.header) == 1) {
						t.l_ch = t.r_ch = MadFixedToSshort(mad_synth_.pcm.samples[0][i]);
					} else {
						t.l_ch = MadFixedToSshort(mad_synth_.pcm.samples[0][i]);
						t.r_ch = MadFixedToSshort(mad_synth_.pcm.samples[1][i]);
					}
					out.at_fifo().put(t);
					++pos;
				}

				{
					uint32_t s = pos / mad_frame_.header.samplerate;
					if(s != time_) {
						uint16_t sec = s % 60;
						uint16_t min = (s / 60) % 60;
						uint16_t hor = (s / 3600) % 24;
						utils::format("\r%02d:%02d:%02d") % hor % min % sec;
						time_ = s;
					}
				}
			}

			mad_synth_finish(&mad_synth_);
			mad_frame_finish(&mad_frame_);
			mad_stream_finish(&mad_stream_);

			return status;
		}
	};
}

<commit_msg>update: update task manage<commit_after>#pragma once
//=====================================================================//
/*!	@file
	@brief	libmad を使った MP3 デコード・クラス @n
			※このクラスを使うには、libmad ライブラリーが必要
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=====================================================================//
#include <mad.h>
#include "common/file_io.hpp"
#include "sound/audio_out.hpp"
#include "sound/id3_mgr.hpp"

extern "C" {
	void set_sample_rate(uint32_t freq);
};

namespace audio {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief	MP3 形式デコード・クラス
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	class mp3_in {
	public:
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	制御列挙型
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		enum class CTRL {
			NONE,		///< 何もしない
			STOP,		///< 停止
			PAUSE,		///< 一時停止
			NEXT,		///< 次の曲
			REPLAY,		///< 曲の先頭に戻って再生
		};

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	制御タスク型
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		typedef CTRL (*CTRL_TASK)();

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	情報タスク型
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		typedef void (*TAG_TASK)(const tag_t&);

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	更新タスク型
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		typedef void (*UPDATE_TASK)(uint32_t);

	private:
		static const uint32_t INPUT_BUFFER_SIZE = 2048;

		mad_stream	mad_stream_;
		mad_frame	mad_frame_;
		mad_synth	mad_synth_;
		mad_timer_t	mad_timer_;

		uint8_t		input_buffer_[INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD];

		// サブバンド領域フィルター特性用。
		mad_fixed_t		subband_filter_[32];
		bool			subband_filter_enable_;
		bool			id3v1_;

		uint32_t		time_;

		CTRL_TASK		ctrl_task_;
		TAG_TASK		tag_task_;
		UPDATE_TASK		update_task_;

		int fill_read_buffer_(utils::file_io& fin, mad_stream& strm)
 		{
			/* The input bucket must be filled if it becomes empty or if
			 * it's the first execution of the loop.
			 */
			if(strm.buffer == NULL || strm.error == MAD_ERROR_BUFLEN) {
				size_t size;
				size_t remaining;
				unsigned char* ptr;

				/* {2} libmad may not consume all bytes of the input
				 * buffer. If the last frame in the buffer is not wholly
				 * contained by it, then that frame's start is pointed by
				 * the next_frame member of the Stream structure. This
				 * common situation occurs when mad_frame_decode() fails,
				 * sets the stream error code to MAD_ERROR_BUFLEN, and
				 * sets the next_frame pointer to a non NULL value. (See
				 * also the comment marked {4} bellow.)
				 *
				 * When this occurs, the remaining unused bytes must be
				 * put back at the beginning of the buffer and taken in
				 * account before refilling the buffer. This means that
				 * the input buffer must be large enough to hold a whole
				 * frame at the highest observable bit-rate (currently 448
				 * kb/s). XXX=XXX Is 2016 bytes the size of the largest
				 * frame? (448000*(1152/32000))/8
				 */
				if(strm.next_frame != NULL) {
					remaining = strm.bufend - strm.next_frame;
					memmove(&input_buffer_[0], strm.next_frame, remaining);
					ptr  = &input_buffer_[remaining];
					size = INPUT_BUFFER_SIZE - remaining;
				} else {
					size = INPUT_BUFFER_SIZE;
					ptr  = &input_buffer_[0];
					remaining = 0;
				}

				/* Fill-in the buffer. If an error occurs print a message
				 * and leave the decoding loop. If the end of stream is
				 * reached we also leave the loop but the return status is
				 * left untouched.
				 */
				// ReadSize = BstdRead(ReadStart, 1, ReadSize, BstdFile);
				size_t req = size;
				size_t rs = fin.read(ptr, 1, req);
				if(id3v1_) {
					if(fin.tell() >= (fin.get_file_size() - 128)) return -1;
				} else {
					if(fin.eof()) return -1;
				}
				size = rs;
				if(rs < req) {
					memset(&ptr[rs], 0, MAD_BUFFER_GUARD);
					size += MAD_BUFFER_GUARD;
				}

				/* Pipe the new buffer content to libmad's stream decoder
				 * facility.
				 */
				mad_stream_buffer(&strm, &input_buffer_[0], size + remaining);
				strm.error = MAD_ERROR_NONE;
				return 0;
			} else {
				return 1;
			}
		}

		/****************************************************************************
		 * Applies a frequency-domain filter to audio data in the subband-domain.	*
		 ****************************************************************************/
		void apply_filter_(mad_frame& frame)
		{
			/* There is two application loops, each optimized for the number
			 * of audio channels to process. The first alternative is for
			 * two-channel frames, the second is for mono-audio.
			 */
			int num = MAD_NSBSAMPLES(&frame.header);
			if(frame.header.mode != MAD_MODE_SINGLE_CHANNEL) {
				for(int ch = 0; ch < 2; ++ch) {
					for(int s = 0; s < num; ++s) {
						for(int sb = 0; sb < 32; ++sb) {
							frame.sbsample[ch][s][sb] =
								mad_f_mul(frame.sbsample[ch][s][sb], subband_filter_[sb]);
						}
					}
				}
			} else {
				for(int s = 0; s < num; ++s) {
					for(int sb = 0; sb < 32; ++sb) {
						frame.sbsample[0][s][sb] =
							mad_f_mul(frame.sbsample[0][s][sb], subband_filter_[sb]);
					}
				}
			}
		}

		static const short SHRT_MAX_ = 32767;

		/****************************************************************************
		 * Converts a sample from mad's fixed point number format to a signed		*
		 * short (16 bits).															*
		 ****************************************************************************/
		static short MadFixedToSshort(mad_fixed_t v)
		{
			/* A fixed point number is formed of the following bit pattern:
			 *
			 * SWWWFFFFFFFFFFFFFFFFFFFFFFFFFFFF
			 * MSB                          LSB
			 * S ==> Sign (0 is positive, 1 is negative)
			 * W ==> Whole part bits
			 * F ==> Fractional part bits
			 *
			 * This pattern contains MAD_F_FRACBITS fractional bits, one
			 * should alway use this macro when working on the bits of a fixed
			 * point number. It is not guaranteed to be constant over the
			 * different platforms supported by libmad.
			 *
			 * The signed short value is formed, after clipping, by the least
			 * significant whole part bit, followed by the 15 most significant
			 * fractional part bits. Warning: this is a quick and dirty way to
			 * compute the 16-bit number, madplay includes much better
			 * algorithms.
			 */

			// Clipping..
			if(v >= MAD_F_ONE) {
				return SHRT_MAX_;
			}
			if(v <= -MAD_F_ONE) {
				return -SHRT_MAX_;
			}

			return (signed short)(v >> (MAD_F_FRACBITS - 15));
		}

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
		*/
		//-----------------------------------------------------------------//
		mp3_in() : subband_filter_enable_(false), id3v1_(false), time_(0),
				   ctrl_task_(nullptr), tag_task_(nullptr), update_task_(nullptr) { }

		//-----------------------------------------------------------------//
		/*!
			@brief	制御タスクの設定
			@param[in]	task	制御タスク
		*/
		//-----------------------------------------------------------------//
		void set_ctrl_task(CTRL_TASK task)
		{
			ctrl_task_ = task;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	タグ・タスクの設定
			@param[in]	task	タグ・タスク
		*/
		//-----------------------------------------------------------------//
		void set_tag_task(TAG_TASK task)
		{
			tag_task_ = task;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	アップデート・タスクの設定
			@param[in]	task	アップデート・タスク
		*/
		//-----------------------------------------------------------------//
		void set_update_task(UPDATE_TASK task)
		{
			update_task_ = task;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	デコード
			@param[in]	fin		file_io コンテキスト（参照）
			@param[in]	out		オーディオ出力（参照）
			@return 正常終了なら「true」
		*/
		//-----------------------------------------------------------------//
		template <class AUDIO_OUT>
		bool decode(utils::file_io& fin, AUDIO_OUT& out)
		{
			id3_mgr id3;
			id3.analize(fin);
			if(tag_task_ != nullptr) {
				const auto& tag = id3.get_tag();
				(*tag_task_)(tag);
//				utils::format("Album:  '%s'\n") % tag.album_;
//				utils::format("Title:  '%s'\n") % tag.title_;
//				utils::format("Artist: '%s'\n") % tag.artist_;
//				utils::format("Year:    %s\n") % tag.year_;
//				utils::format("Track:   %s\n") % tag.track_;
			}

			mad_stream_init(&mad_stream_);
			mad_frame_init(&mad_frame_);
			mad_synth_init(&mad_synth_);
			mad_timer_reset(&mad_timer_);

			uint32_t forg = fin.tell();
			bool info = false;
			uint32_t pos = 0;
			uint32_t frame_count = 0;
			bool status = true;
			bool pause = false;
			while(fill_read_buffer_(fin, mad_stream_) >= 0) {

				CTRL ctrl = CTRL::NONE;
				if(ctrl_task_ != nullptr) {
					ctrl = (*ctrl_task_)();
				}
				if(ctrl == CTRL::NEXT) {
					out.mute();
					break;
				} else if(ctrl == CTRL::REPLAY) {
					out.mute();
					fin.seek(utils::file_io::SEEK::SET, forg);
					info = false;
					pos = 0;
					time_ = 0;
					frame_count = 0;
					status = true;
					pause = false;
					continue;
				} else if(ctrl == CTRL::PAUSE) {
					out.mute();
					pause = !pause;
				} else if(ctrl == CTRL::STOP) {
					out.mute();
					status = false;
					break;
				}
				if(pause) {
					utils::delay::milli_second(5);
					continue;
				}

				if(mad_frame_decode(&mad_frame_, &mad_stream_)) {
					if(MAD_RECOVERABLE(mad_stream_.error)) {
						continue;
					} else {
						if(mad_stream_.error == MAD_ERROR_BUFLEN) {
							continue;
						} else {
							status = false;
							break;
						}
					}
				}

				if(!info) {
					set_sample_rate(mad_frame_.header.samplerate);
					utils::format("Sample Rate: %d\n") % mad_frame_.header.samplerate;
					info = true;
				}

				frame_count++;
				mad_timer_add(&mad_timer_, mad_frame_.header.duration);

				if(subband_filter_enable_) {
					apply_filter_(mad_frame_);
				}

				mad_synth_frame(&mad_synth_, &mad_frame_);

				for(uint32_t i = 0; i < mad_synth_.pcm.length; ++i) {
					while((out.at_fifo().size() - out.at_fifo().length()) < 8) {
					}
					audio::wave_t t;
					if(MAD_NCHANNELS(&mad_frame_.header) == 1) {
						t.l_ch = t.r_ch = MadFixedToSshort(mad_synth_.pcm.samples[0][i]);
					} else {
						t.l_ch = MadFixedToSshort(mad_synth_.pcm.samples[0][i]);
						t.r_ch = MadFixedToSshort(mad_synth_.pcm.samples[1][i]);
					}
					out.at_fifo().put(t);
					++pos;
				}

				{
					uint32_t s = pos / mad_frame_.header.samplerate;
					if(s != time_) {
						if(update_task_ != nullptr) {
							(*update_task_)(s);
						}
						time_ = s;
					}
				}
			}

			mad_synth_finish(&mad_synth_);
			mad_frame_finish(&mad_frame_);
			mad_stream_finish(&mad_stream_);

			return status;
		}
	};
}

<|endoftext|>


<commit_before>//needed for testing
#define CATCH_CONFIG_RUNNER
#include <catch.hpp>

//other stuff needed
#include <glm/vec3.hpp>
#include <cmath>
#include <iostream>

//include classes for testing
#include "shape.hpp"
#include "sphere.hpp"
#include "box.hpp"
#include "material.hpp"
#include "color.hpp"

// ----------------------------------
// SHAPE TESTS
// ----------------------------------

//karos änderung

TEST_CASE("getter shape","[shape]")
{
	Sphere s {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f},1.0f};
	REQUIRE(s.get_name() == "name");

	Material c{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f};
	REQUIRE(s.get_material() == c); //added operator == in color.hpp
}

TEST_CASE("operator<< and print shape","[shape]")
{
	Sphere es {"empty_sphere"};
	std::cout << es;

	Sphere s {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f},1.0f};
	std::cout << s;
}

// ----------------------------------
// SPHERE TESTS
// ----------------------------------

TEST_CASE("constructors of sphere","[sphere]")
{
	Sphere s1 {"name"};
	Sphere s2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};

	REQUIRE(s1.get_center() == s2.get_center());
	REQUIRE(s1.get_radius() == s2.get_radius());
}

TEST_CASE("get_center and get_radius","[sphere]")
{
	Sphere s {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};

	REQUIRE(s.get_radius() == 1.0f);
	REQUIRE(s.get_center() == glm::vec3{0.0f});
}

TEST_CASE("area","[sphere]")
{
	Sphere s {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};
	REQUIRE(12.566f == Approx(s.area()).epsilon(0.001));
}

TEST_CASE("volume","[sphere]")
{
	Sphere s {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};
	REQUIRE(4.189 == Approx(s.volume()).epsilon(0.001));
}

TEST_CASE("print sphere","[sphere]")
{
	Sphere es {"empty_sphere"};
	std::cout << es;

	Sphere s {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f},1.0f};
	std::cout << s;
}

// ----------------------------------
// BOX TESTS
// ----------------------------------

TEST_CASE("constructors of box","[box]")
{
	Box b1 {"name"};
	Box b2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, glm::vec3{1.0f}};

	REQUIRE(b1.get_min() == b2.get_min());
	REQUIRE(b1.get_max() == b2.get_max());
}

TEST_CASE("get_min and get_max","[box]")
{
	Box b2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, glm::vec3{1.0f}};

	REQUIRE(b2.get_min() == glm::vec3{0.0f});
	REQUIRE(b2.get_max() == glm::vec3{1.0f});
}

TEST_CASE("area of box","[box]")
{
	Box b2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, glm::vec3{1.0f}};
	REQUIRE(6.0f == Approx(b2.area()).epsilon(0.001));
}

TEST_CASE("volume of box","[box]")
{
	Box b2 {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f}, glm::vec3{0.0f}, glm::vec3{1.0f}};

	REQUIRE(1.0f == Approx(b2.volume()).epsilon(0.001));
}

TEST_CASE("print box","[box]")
{
	Box eb {"empty_box"};
	std::cout << eb;

	Box b {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f}, glm::vec3{0.0f}, glm::vec3{1.0f}};
	std::cout << b;
}

// ----------------------------------
// AUFGABE 5.6 TEST
// ----------------------------------

TEST_CASE("intersectRaySphere", "[intersect]")
{
	//Ray
	glm::vec3 ray_origin{0.0f, 0.0f, 0.0f};
	//ray direction has to be normalized !
	//you can use:
		//v = glm::normalize(some_vector);
	glm::vec3 ray_direction{0.0f, 0.0f, 1.0f};

	//Sphere
	glm::vec3 sphere_center{0.0f, 0.0f, 5.0f};
	float sphere_radius{1.0f};

	float distance{0.0f};
	auto result = glm::intersectRaySphere(
		ray_origin, ray_direction,
		sphere_center,
		sphere_radius * sphere_radius,
		distance);

	REQUIRE(distance == Approx(4.0f));

	Sphere s {"some_sphere", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},
		sphere_center, sphere_radius};

	Ray r {ray_origin,ray_direction};

	s.intersect(r,distance);

	REQUIRE(distance == Approx(4.0f));
}

// ----------------------------------
// AUFGABE 5.8 TEST
// ----------------------------------

TEST_CASE("virtual", "[Destructors]")
{

	std::cout << "\ntesting virtual and non-virtual construction and destruction\n";

	Color c_red {255.0f, 0.0f, 0.0f};
	Material red {"", c_red, c_red, c_red, 0.0f};

	glm::vec3 position {0.0f,0.0f,0.0f};

	std::cout << "Create s1\n";
	Sphere* s1 = new Sphere("sphere0",red,position,1.2f);
	std::cout << "Create s2\n";
	Shape* s2 = new Sphere("sphere1",red,position,1.2f);

	std::cout << "Printing objects: \n";
	std::cout << *s1;
	std::cout << *s2 << "\n";

	std::cout << "Delete s1\n";
	delete s1;
	std::cout << "Delete s2\n";
	delete s2;
}

// ----------------------------------
// MATERIAL Tests
// ----------------------------------

TEST_CASE("material in place of color struct", "Material")
{
	Color c{0.0f,0.0f,0.0f};
	Material mate1 {};
	std::cout << mate1;

	Material mate2 {"some material",c,c,c,0.0f};
	std::cout << mate2;
}

int main(int argc, char *argv[])
{
  return Catch::Session().run(argc, argv);
}
<commit_msg>noch ein merge test<commit_after>//needed for testing
#define CATCH_CONFIG_RUNNER
#include <catch.hpp>

//other stuff needed
#include <glm/vec3.hpp>
#include <cmath>
#include <iostream>

//include classes for testing
#include "shape.hpp"
#include "sphere.hpp"
#include "box.hpp"
#include "material.hpp"
#include "color.hpp"

// ----------------------------------
// SHAPE TESTS
// ----------------------------------

//karos änderung

TEST_CASE("getter shape","[shape]")
{
	Sphere s {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f},1.0f};
	REQUIRE(s.get_name() == "name");

	Material c{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f};
	REQUIRE(s.get_material() == c); //added operator == in color.hpp
}

TEST_CASE("operator<< and print shape","[shape]")
{
	Sphere es {"empty_sphere"};
	std::cout << es;

	Sphere s {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f},1.0f};
	std::cout << s;
//trolololo
}

// ----------------------------------
// SPHERE TESTS
// ----------------------------------

TEST_CASE("constructors of sphere","[sphere]")
{
	Sphere s1 {"name"};
	Sphere s2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};

	REQUIRE(s1.get_center() == s2.get_center());
	REQUIRE(s1.get_radius() == s2.get_radius());
}

TEST_CASE("get_center and get_radius","[sphere]")
{
	Sphere s {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};

	REQUIRE(s.get_radius() == 1.0f);
	REQUIRE(s.get_center() == glm::vec3{0.0f});
}

TEST_CASE("area","[sphere]")
{
	Sphere s {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};
	REQUIRE(12.566f == Approx(s.area()).epsilon(0.001));
}

TEST_CASE("volume","[sphere]")
{
	Sphere s {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, 1.0f};
	REQUIRE(4.189 == Approx(s.volume()).epsilon(0.001));
}

TEST_CASE("print sphere","[sphere]")
{
	Sphere es {"empty_sphere"};
	std::cout << es;

	Sphere s {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f},1.0f};
	std::cout << s;
}

// ----------------------------------
// BOX TESTS
// ----------------------------------

TEST_CASE("constructors of box","[box]")
{
	Box b1 {"name"};
	Box b2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, glm::vec3{1.0f}};

	REQUIRE(b1.get_min() == b2.get_min());
	REQUIRE(b1.get_max() == b2.get_max());
}

TEST_CASE("get_min and get_max","[box]")
{
	Box b2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, glm::vec3{1.0f}};

	REQUIRE(b2.get_min() == glm::vec3{0.0f});
	REQUIRE(b2.get_max() == glm::vec3{1.0f});
}

TEST_CASE("area of box","[box]")
{
	Box b2 {"name",Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},glm::vec3{0.0f}, glm::vec3{1.0f}};
	REQUIRE(6.0f == Approx(b2.area()).epsilon(0.001));
}

TEST_CASE("volume of box","[box]")
{
	Box b2 {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f}, glm::vec3{0.0f}, glm::vec3{1.0f}};

	REQUIRE(1.0f == Approx(b2.volume()).epsilon(0.001));
}

TEST_CASE("print box","[box]")
{
	Box eb {"empty_box"};
	std::cout << eb;

	Box b {"name", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f}, glm::vec3{0.0f}, glm::vec3{1.0f}};
	std::cout << b;
}

// ----------------------------------
// AUFGABE 5.6 TEST
// ----------------------------------

TEST_CASE("intersectRaySphere", "[intersect]")
{
	//Ray
	glm::vec3 ray_origin{0.0f, 0.0f, 0.0f};
	//ray direction has to be normalized !
	//you can use:
		//v = glm::normalize(some_vector);
	glm::vec3 ray_direction{0.0f, 0.0f, 1.0f};

	//Sphere
	glm::vec3 sphere_center{0.0f, 0.0f, 5.0f};
	float sphere_radius{1.0f};

	float distance{0.0f};
	auto result = glm::intersectRaySphere(
		ray_origin, ray_direction,
		sphere_center,
		sphere_radius * sphere_radius,
		distance);

	REQUIRE(distance == Approx(4.0f));

	Sphere s {"some_sphere", Material{"",Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},Color{0.0f,0.0f,0.0f},0.0f},
		sphere_center, sphere_radius};

	Ray r {ray_origin,ray_direction};

	s.intersect(r,distance);

	REQUIRE(distance == Approx(4.0f));
}

// ----------------------------------
// AUFGABE 5.8 TEST
// ----------------------------------

TEST_CASE("virtual", "[Destructors]")
{

	std::cout << "\ntesting virtual and non-virtual construction and destruction\n";

	Color c_red {255.0f, 0.0f, 0.0f};
	Material red {"", c_red, c_red, c_red, 0.0f};

	glm::vec3 position {0.0f,0.0f,0.0f};

	std::cout << "Create s1\n";
	Sphere* s1 = new Sphere("sphere0",red,position,1.2f);
	std::cout << "Create s2\n";
	Shape* s2 = new Sphere("sphere1",red,position,1.2f);

	std::cout << "Printing objects: \n";
	std::cout << *s1;
	std::cout << *s2 << "\n";

	std::cout << "Delete s1\n";
	delete s1;
	std::cout << "Delete s2\n";
	delete s2;
}

// ----------------------------------
// MATERIAL Tests
// ----------------------------------

TEST_CASE("material in place of color struct", "Material")
{
	Color c{0.0f,0.0f,0.0f};
	Material mate1 {};
	std::cout << mate1;

	Material mate2 {"some material",c,c,c,0.0f};
	std::cout << mate2;
}

int main(int argc, char *argv[])
{
  return Catch::Session().run(argc, argv);
}
<|endoftext|>