<commit_before>#include <Core/Tasks/Task.hpp>
#include <Core/Tasks/TaskQueue.hpp>

#include <algorithm>
#include <iostream>
#include <stack>

namespace Ra {
namespace Core {

TaskQueue::TaskQueue( uint numThreads ) : m_processingTasks( 0 ), m_shuttingDown( false ) {
    CORE_ASSERT( numThreads > 0, " You need at least one thread" );
    m_workerThreads.reserve( numThreads );
    for ( uint i = 0; i < numThreads; ++i )
    {
        m_workerThreads.emplace_back( &TaskQueue::runThread, this, i );
    }
}

TaskQueue::~TaskQueue() {
    flushTaskQueue();
    m_shuttingDown = true;
    m_threadNotifier.notify_all();
    for ( auto& t : m_workerThreads )
    {
        t.join();
    }
}

TaskQueue::TaskId TaskQueue::registerTask( Task* task ) {
    m_tasks.emplace_back( std::unique_ptr<Task>( task ) );
    m_dependencies.push_back( std::vector<TaskId>() );
    m_remainingDependencies.push_back( 0 );
    TimerData tdata;
    tdata.taskName = task->getName();
    m_timerData.push_back( tdata );

    CORE_ASSERT( m_tasks.size() == m_dependencies.size(), "Inconsistent task list" );
    CORE_ASSERT( m_tasks.size() == m_remainingDependencies.size(), "Inconsistent task list" );
    CORE_ASSERT( m_tasks.size() == m_timerData.size(), "Inconsistent task list" );
    return TaskId( m_tasks.size() - 1 );
}

void TaskQueue::addDependency( TaskQueue::TaskId predecessor, TaskQueue::TaskId successor ) {
    CORE_ASSERT( predecessor.isValid() && ( predecessor < m_tasks.size() ),
                 "Invalid predecessor task" );
    CORE_ASSERT( successor.isValid() && ( successor < m_tasks.size() ), "Invalid successor task" );
    CORE_ASSERT( predecessor != successor, "Cannot add self-dependency" );

    CORE_ASSERT( std::find( m_dependencies[predecessor].begin(),
                            m_dependencies[predecessor].end(),
                            successor ) == m_dependencies[predecessor].end(),
                 "Cannot add a dependency twice" );

    m_dependencies[predecessor].push_back( successor );
    ++m_remainingDependencies[successor];
}

bool TaskQueue::addDependency( const std::string& predecessors, TaskQueue::TaskId successor ) {
    bool added = false;
    for ( uint i = 0; i < m_tasks.size(); ++i )
    {
        if ( m_tasks[i]->getName() == predecessors )
        {
            added = true;
            addDependency( TaskId( i ), successor );
        }
    }
    return added;
}

bool TaskQueue::addDependency( TaskQueue::TaskId predecessor, const std::string& successors ) {
    bool added = false;
    for ( uint i = 0; i < m_tasks.size(); ++i )
    {
        if ( m_tasks[i]->getName() == successors )
        {
            added = true;
            addDependency( predecessor, TaskId( i ) );
        }
    }
    return added;
}

void TaskQueue::addPendingDependency( const std::string& predecessors,
                                      TaskQueue::TaskId successor ) {
    m_pendingDepsSucc.emplace_back( predecessors, successor );
}

void TaskQueue::addPendingDependency( TaskId predecessor, const std::string& successors ) {
    m_pendingDepsPre.emplace_back( predecessor, successors );
}

void TaskQueue::resolveDependencies() {
    for ( const auto& pre : m_pendingDepsPre )
    {
        ON_ASSERT( bool result = ) addDependency( pre.first, pre.second );
        CORE_WARN_IF( !result,
                      "Pending dependency unresolved : " << m_tasks[pre.first]->getName() << " -> ("
                                                         << pre.second << ")" );
    }
    for ( const auto& pre : m_pendingDepsSucc )
    {
        ON_ASSERT( bool result = ) addDependency( pre.first, pre.second );
        CORE_WARN_IF( !result,
                      "Pending dependency unresolved : (" << pre.first << ") -> "
                                                          << m_tasks[pre.second]->getName() );
    }
    m_pendingDepsPre.clear();
    m_pendingDepsSucc.clear();
}

void TaskQueue::queueTask( TaskQueue::TaskId task ) {
    CORE_ASSERT( m_remainingDependencies[task] == 0,
                 " Task" << m_tasks[task]->getName() << "has unmet dependencies" );
    m_taskQueue.push_front( task );
}

void TaskQueue::detectCycles() {
#if defined( CORE_DEBUG )
    // Do a depth-first search of the nodes.
    std::vector<bool> visited( m_tasks.size(), false );
    std::stack<TaskId> pending;

    for ( uint id = 0; id < m_tasks.size(); ++id )
    {
        if ( m_dependencies[id].size() == 0 ) { pending.push( TaskId( id ) ); }
    }

    // If you hit this assert, there are tasks in the list but
    // all tasks have dependencies so no task can start.
    CORE_ASSERT( m_tasks.empty() || !pending.empty(), "No free tasks." );

    while ( !pending.empty() )
    {
        TaskId id = pending.top();
        pending.pop();

        // The task has already been visited. It means there is a cycle in the task graph.
        CORE_ASSERT( !( visited[id] ), "Cycle detected in tasks !" );

        visited[id] = true;
        for ( const auto& dep : m_dependencies[id] )
        {
            pending.push( dep );
        }
    }
#endif
}

void TaskQueue::startTasks() {
    // Add pending dependencies.
    resolveDependencies();

    // Do a debug check
    detectCycles();

    // Enqueue all tasks with no dependencies.
    for ( uint t = 0; t < m_tasks.size(); ++t )
    {
        if ( m_remainingDependencies[t] == 0 ) { queueTask( TaskId( t ) ); }
    }

    // Wake up all threads.
    m_threadNotifier.notify_all();
}

void TaskQueue::waitForTasks() {
    bool isFinished = false;
    while ( !isFinished )
    {
        // TODO : use a notifier for task queue empty.
        m_taskQueueMutex.lock();
        isFinished = ( m_taskQueue.empty() && m_processingTasks == 0 );
        m_taskQueueMutex.unlock();
        if ( !isFinished ) { std::this_thread::yield(); }
    }
}

const std::vector<TaskQueue::TimerData>& TaskQueue::getTimerData() {
    return m_timerData;
}

void TaskQueue::flushTaskQueue() {
    CORE_ASSERT( m_processingTasks == 0, "You have tasks still in process" );
    CORE_ASSERT( m_taskQueue.empty(), " You have unprocessed tasks " );
    m_tasks.clear();
    m_dependencies.clear();
    m_timerData.clear();
    m_remainingDependencies.clear();
}

void TaskQueue::runThread( uint id ) {
    while ( true )
    {
        TaskId task;

        // Acquire mutex.
        {
            std::unique_lock<std::mutex> lock( m_taskQueueMutex );

            // Wait for a new task
            // TODO : use the second form of wait()
            while ( !m_shuttingDown && m_taskQueue.empty() )
            {
                m_threadNotifier.wait( lock );
            }
            // If the task queue is shutting down we quit, releasing
            // the lock.
            if ( m_shuttingDown ) { return; }

            // If we are here it means we got a task
            task = m_taskQueue.back();
            m_taskQueue.pop_back();
            ++m_processingTasks;
            CORE_ASSERT( task.isValid() && task < m_tasks.size(), "Invalid task" );
        }
        // Release mutex.

        // Run task
        m_timerData[task].start    = Utils::Clock::now();
        m_timerData[task].threadId = id;
        m_tasks[task]->process();
        m_timerData[task].end = Utils::Clock::now();

        // Critical section : mark task as finished and en-queue dependencies.
        uint newTasks = 0;
        {
            std::unique_lock<std::mutex> lock( m_taskQueueMutex );
            for ( auto t : m_dependencies[task] )
            {
                uint& nDepends = m_remainingDependencies[t];
                CORE_ASSERT( nDepends > 0, "Inconsistency in dependencies" );
                --nDepends;
                if ( nDepends == 0 )
                {
                    queueTask( t );
                    ++newTasks;
                }
                // TODO :Easy optimization : grab one of the new task and process it immediately.
            }
            --m_processingTasks;
        }
        // If we added new tasks, we wake up one thread to execute it.
        if ( newTasks > 0 ) { m_threadNotifier.notify_one(); }
    } // End of while(true)
}

void TaskQueue::printTaskGraph( std::ostream& output ) const {
    output << "digraph tasks {" << std::endl;

    for ( const auto& t : m_tasks )
    {
        output << "\"" << t->getName() << "\"" << std::endl;
    }

    for ( uint i = 0; i < m_dependencies.size(); ++i )
    {
        const auto& task1 = m_tasks[i];
        for ( const auto& dep : m_dependencies[i] )
        {
            const auto& task2 = m_tasks[dep];
            output << "\"" << task1->getName() << "\""
                   << " -> ";
            output << "\"" << task2->getName() << "\"" << std::endl;
        }
    }

    for ( const auto& preDep : m_pendingDepsPre )
    {
        const auto& task1  = m_tasks[preDep.first];
        std::string t2name = preDep.second;

        if ( std::find_if( m_tasks.begin(), m_tasks.end(), [=]( const auto& task ) {
                 return task->getName() == t2name;
             } ) == m_tasks.end() )
        { t2name += "?"; }
        output << "\"" << task1->getName() << "\""
               << " -> ";
        output << "\"" << t2name << "\"" << std::endl;
    }

    for ( const auto& postDep : m_pendingDepsSucc )
    {
        std::string t1name = postDep.first;
        const auto& t2     = m_tasks[postDep.second];

        if ( std::find_if( m_tasks.begin(), m_tasks.end(), [=]( const auto& task ) {
                 return task->getName() == t1name;
             } ) == m_tasks.end() )
        { t1name += "?"; }
        output << "\"" << t1name << "\""
               << " -> ";
        output << "\"" << t2->getName() << "\"" << std::endl;
    }

    output << "}" << std::endl;
}
} // namespace Core
} // namespace Ra
<commit_msg>[core] fix access to potentially invalid ptr (from codacy).<commit_after>#include <Core/Tasks/Task.hpp>
#include <Core/Tasks/TaskQueue.hpp>

#include <algorithm>
#include <iostream>
#include <stack>

namespace Ra {
namespace Core {

TaskQueue::TaskQueue( uint numThreads ) : m_processingTasks( 0 ), m_shuttingDown( false ) {
    CORE_ASSERT( numThreads > 0, " You need at least one thread" );
    m_workerThreads.reserve( numThreads );
    for ( uint i = 0; i < numThreads; ++i )
    {
        m_workerThreads.emplace_back( &TaskQueue::runThread, this, i );
    }
}

TaskQueue::~TaskQueue() {
    flushTaskQueue();
    m_shuttingDown = true;
    m_threadNotifier.notify_all();
    for ( auto& t : m_workerThreads )
    {
        t.join();
    }
}

TaskQueue::TaskId TaskQueue::registerTask( Task* task ) {
    TimerData tdata;
    // init tdata with task name before moving ownership
    tdata.taskName = task->getName();
    m_timerData.push_back( tdata );

    m_tasks.emplace_back( std::unique_ptr<Task>( task ) );
    m_dependencies.push_back( std::vector<TaskId>() );
    m_remainingDependencies.push_back( 0 );

    CORE_ASSERT( m_tasks.size() == m_dependencies.size(), "Inconsistent task list" );
    CORE_ASSERT( m_tasks.size() == m_remainingDependencies.size(), "Inconsistent task list" );
    CORE_ASSERT( m_tasks.size() == m_timerData.size(), "Inconsistent task list" );
    return TaskId( m_tasks.size() - 1 );
}

void TaskQueue::addDependency( TaskQueue::TaskId predecessor, TaskQueue::TaskId successor ) {
    CORE_ASSERT( predecessor.isValid() && ( predecessor < m_tasks.size() ),
                 "Invalid predecessor task" );
    CORE_ASSERT( successor.isValid() && ( successor < m_tasks.size() ), "Invalid successor task" );
    CORE_ASSERT( predecessor != successor, "Cannot add self-dependency" );

    CORE_ASSERT( std::find( m_dependencies[predecessor].begin(),
                            m_dependencies[predecessor].end(),
                            successor ) == m_dependencies[predecessor].end(),
                 "Cannot add a dependency twice" );

    m_dependencies[predecessor].push_back( successor );
    ++m_remainingDependencies[successor];
}

bool TaskQueue::addDependency( const std::string& predecessors, TaskQueue::TaskId successor ) {
    bool added = false;
    for ( uint i = 0; i < m_tasks.size(); ++i )
    {
        if ( m_tasks[i]->getName() == predecessors )
        {
            added = true;
            addDependency( TaskId( i ), successor );
        }
    }
    return added;
}

bool TaskQueue::addDependency( TaskQueue::TaskId predecessor, const std::string& successors ) {
    bool added = false;
    for ( uint i = 0; i < m_tasks.size(); ++i )
    {
        if ( m_tasks[i]->getName() == successors )
        {
            added = true;
            addDependency( predecessor, TaskId( i ) );
        }
    }
    return added;
}

void TaskQueue::addPendingDependency( const std::string& predecessors,
                                      TaskQueue::TaskId successor ) {
    m_pendingDepsSucc.emplace_back( predecessors, successor );
}

void TaskQueue::addPendingDependency( TaskId predecessor, const std::string& successors ) {
    m_pendingDepsPre.emplace_back( predecessor, successors );
}

void TaskQueue::resolveDependencies() {
    for ( const auto& pre : m_pendingDepsPre )
    {
        ON_ASSERT( bool result = ) addDependency( pre.first, pre.second );
        CORE_WARN_IF( !result,
                      "Pending dependency unresolved : " << m_tasks[pre.first]->getName() << " -> ("
                                                         << pre.second << ")" );
    }
    for ( const auto& pre : m_pendingDepsSucc )
    {
        ON_ASSERT( bool result = ) addDependency( pre.first, pre.second );
        CORE_WARN_IF( !result,
                      "Pending dependency unresolved : (" << pre.first << ") -> "
                                                          << m_tasks[pre.second]->getName() );
    }
    m_pendingDepsPre.clear();
    m_pendingDepsSucc.clear();
}

void TaskQueue::queueTask( TaskQueue::TaskId task ) {
    CORE_ASSERT( m_remainingDependencies[task] == 0,
                 " Task" << m_tasks[task]->getName() << "has unmet dependencies" );
    m_taskQueue.push_front( task );
}

void TaskQueue::detectCycles() {
#if defined( CORE_DEBUG )
    // Do a depth-first search of the nodes.
    std::vector<bool> visited( m_tasks.size(), false );
    std::stack<TaskId> pending;

    for ( uint id = 0; id < m_tasks.size(); ++id )
    {
        if ( m_dependencies[id].size() == 0 ) { pending.push( TaskId( id ) ); }
    }

    // If you hit this assert, there are tasks in the list but
    // all tasks have dependencies so no task can start.
    CORE_ASSERT( m_tasks.empty() || !pending.empty(), "No free tasks." );

    while ( !pending.empty() )
    {
        TaskId id = pending.top();
        pending.pop();

        // The task has already been visited. It means there is a cycle in the task graph.
        CORE_ASSERT( !( visited[id] ), "Cycle detected in tasks !" );

        visited[id] = true;
        for ( const auto& dep : m_dependencies[id] )
        {
            pending.push( dep );
        }
    }
#endif
}

void TaskQueue::startTasks() {
    // Add pending dependencies.
    resolveDependencies();

    // Do a debug check
    detectCycles();

    // Enqueue all tasks with no dependencies.
    for ( uint t = 0; t < m_tasks.size(); ++t )
    {
        if ( m_remainingDependencies[t] == 0 ) { queueTask( TaskId( t ) ); }
    }

    // Wake up all threads.
    m_threadNotifier.notify_all();
}

void TaskQueue::waitForTasks() {
    bool isFinished = false;
    while ( !isFinished )
    {
        // TODO : use a notifier for task queue empty.
        m_taskQueueMutex.lock();
        isFinished = ( m_taskQueue.empty() && m_processingTasks == 0 );
        m_taskQueueMutex.unlock();
        if ( !isFinished ) { std::this_thread::yield(); }
    }
}

const std::vector<TaskQueue::TimerData>& TaskQueue::getTimerData() {
    return m_timerData;
}

void TaskQueue::flushTaskQueue() {
    CORE_ASSERT( m_processingTasks == 0, "You have tasks still in process" );
    CORE_ASSERT( m_taskQueue.empty(), " You have unprocessed tasks " );
    m_tasks.clear();
    m_dependencies.clear();
    m_timerData.clear();
    m_remainingDependencies.clear();
}

void TaskQueue::runThread( uint id ) {
    while ( true )
    {
        TaskId task;

        // Acquire mutex.
        {
            std::unique_lock<std::mutex> lock( m_taskQueueMutex );

            // Wait for a new task
            // TODO : use the second form of wait()
            while ( !m_shuttingDown && m_taskQueue.empty() )
            {
                m_threadNotifier.wait( lock );
            }
            // If the task queue is shutting down we quit, releasing
            // the lock.
            if ( m_shuttingDown ) { return; }

            // If we are here it means we got a task
            task = m_taskQueue.back();
            m_taskQueue.pop_back();
            ++m_processingTasks;
            CORE_ASSERT( task.isValid() && task < m_tasks.size(), "Invalid task" );
        }
        // Release mutex.

        // Run task
        m_timerData[task].start    = Utils::Clock::now();
        m_timerData[task].threadId = id;
        m_tasks[task]->process();
        m_timerData[task].end = Utils::Clock::now();

        // Critical section : mark task as finished and en-queue dependencies.
        uint newTasks = 0;
        {
            std::unique_lock<std::mutex> lock( m_taskQueueMutex );
            for ( auto t : m_dependencies[task] )
            {
                uint& nDepends = m_remainingDependencies[t];
                CORE_ASSERT( nDepends > 0, "Inconsistency in dependencies" );
                --nDepends;
                if ( nDepends == 0 )
                {
                    queueTask( t );
                    ++newTasks;
                }
                // TODO :Easy optimization : grab one of the new task and process it immediately.
            }
            --m_processingTasks;
        }
        // If we added new tasks, we wake up one thread to execute it.
        if ( newTasks > 0 ) { m_threadNotifier.notify_one(); }
    } // End of while(true)
}

void TaskQueue::printTaskGraph( std::ostream& output ) const {
    output << "digraph tasks {" << std::endl;

    for ( const auto& t : m_tasks )
    {
        output << "\"" << t->getName() << "\"" << std::endl;
    }

    for ( uint i = 0; i < m_dependencies.size(); ++i )
    {
        const auto& task1 = m_tasks[i];
        for ( const auto& dep : m_dependencies[i] )
        {
            const auto& task2 = m_tasks[dep];
            output << "\"" << task1->getName() << "\""
                   << " -> ";
            output << "\"" << task2->getName() << "\"" << std::endl;
        }
    }

    for ( const auto& preDep : m_pendingDepsPre )
    {
        const auto& task1  = m_tasks[preDep.first];
        std::string t2name = preDep.second;

        if ( std::find_if( m_tasks.begin(), m_tasks.end(), [=]( const auto& task ) {
                 return task->getName() == t2name;
             } ) == m_tasks.end() )
        { t2name += "?"; }
        output << "\"" << task1->getName() << "\""
               << " -> ";
        output << "\"" << t2name << "\"" << std::endl;
    }

    for ( const auto& postDep : m_pendingDepsSucc )
    {
        std::string t1name = postDep.first;
        const auto& t2     = m_tasks[postDep.second];

        if ( std::find_if( m_tasks.begin(), m_tasks.end(), [=]( const auto& task ) {
                 return task->getName() == t1name;
             } ) == m_tasks.end() )
        { t1name += "?"; }
        output << "\"" << t1name << "\""
               << " -> ";
        output << "\"" << t2->getName() << "\"" << std::endl;
    }

    output << "}" << std::endl;
}
} // namespace Core
} // namespace Ra
<|endoftext|>


<commit_before>/********** tell emacs we use -*- c++ -*- style comments *******************
 $Revision: 1.5 $  $Author: trey $  $Date: 2006-04-28 17:57:41 $

 @file    testSpec.cc
 @brief   No brief

 Copyright (c) 2002-2005, Trey Smith. All rights reserved.

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

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

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

 ***************************************************************************/

/***************************************************************************
 * INCLUDES
 ***************************************************************************/

#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

#include <iostream>
using namespace std;

#include "pomdpCassandraWrapper.h"

void usage(void) {
  cerr <<
    "usage: testSpec OPTIONS <foo.POMDP>\n"
    "  -h or --help   Display this help\n";
  exit(EXIT_FAILURE);
}

int main(int argc, char *argv[]) {
  int argi;
  char *pomdpFileName = 0;
  for (argi=1; argi < argc; argi++) {
    if (0 == strcmp("-h",argv[argi]) || 0 == strcmp("--help",argv[argi])) {
      usage();
    } else if (0 == pomdpFileName) {
      pomdpFileName = argv[argi];
    } else {
      cerr << "too many arguments" << endl;
      usage();
    }
  }
  if ( 0 == pomdpFileName ) {
    usage();
  }

  // read it in
  PomdpCassandraWrapper p;
  p.readFromFile(pomdpFileName);

  // print out stats
  cout << "numStates = " << p.numStates() << endl;
  cout << "numActions = " << p.numActions() << endl;
  cout << "numObservations = " << p.numObservations() << endl;
  cout << "discount = " << p.discount() << endl;
  cout << endl;

  int s, sp, a, o;

  cout << "R(s,a) matrix = " << endl;
  for (s=0; s < p.numStates(); s++) {
    for (a=0; a < p.numActions(); a++) {
      printf("%9.2f ", p.R(s,a));
    }
    cout << endl;
  }
  cout << endl;

  for (a=0; a < p.numActions(); a++) {
    cout << "T(s," << a << ",sp) matrix = " << endl;
    for (s=0; s < p.numStates(); s++) {
      for (sp=0; sp < p.numStates(); sp++) {
	printf("%5.3f ", p.T(s,a,sp));
      }
      cout << endl;
    }
    cout << endl;
  }

  for (a=0; a < p.numActions(); a++) {
    cout << "O(sp," << a << ",o) matrix = " << endl;
    for (sp=0; sp < p.numStates(); sp++) {
      for (o=0; o < p.numObservations(); o++) {
	printf("%5.3f ", p.O(sp,a,o));
      }
      cout << endl;
    }
    cout << endl;
  }
}

/***************************************************************************
 * REVISION HISTORY:
 * $Log: not supported by cvs2svn $
 * Revision 1.4  2005/10/28 03:54:39  trey
 * simplified license
 *
 * Revision 1.3  2005/10/28 03:05:27  trey
 * added copyright header
 *
 * Revision 1.2  2005/10/27 21:37:17  trey
 * brought names up to date
 *
 * Revision 1.1.1.1  2004/11/09 16:18:56  trey
 * imported hsvi into new repository
 *
 * Revision 1.1.1.1  2003/01/07 19:19:41  trey
 * Imported sources
 *
 *
 ***************************************************************************/
<commit_msg>removed test code, out of date<commit_after><|endoftext|>


<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2014-2021 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <modules/basegl/processors/volumeprocessing/volumecombiner.h>
#include <modules/opengl/volume/volumegl.h>
#include <modules/opengl/texture/textureunit.h>
#include <inviwo/core/util/shuntingyard.h>
#include <inviwo/core/util/zip.h>
#include <modules/opengl/texture/textureutils.h>
#include <modules/opengl/shader/shaderutils.h>
#include <modules/opengl/volume/volumeutils.h>

namespace {
std::string idToString(const size_t& id) {
    if (id == 0) return "";
    return inviwo::toString(id);
}
}  // namespace

namespace inviwo {

const ProcessorInfo VolumeCombiner::processorInfo_{
    "org.inviwo.VolumeCombiner",  // Class identifier
    "Volume Combiner",            // Display name
    "Volume Operation",           // Category
    CodeState::Experimental,      // Code state
    Tags::GL,                     // Tags
};
const ProcessorInfo VolumeCombiner::getProcessorInfo() const { return processorInfo_; }

VolumeCombiner::VolumeCombiner()
    : Processor()
    , inport_("inport")
    , outport_("outport")
    , description_("description", "Volumes")
    , eqn_("eqn", "Equation", "v1")
    , normalizationMode_(
          "normalizationMode", "Normalization Mode",
          {{"normalized", "Normalize volumes", NormalizationMode::Normalized},
           {"signedNormalized", "Normalize volumes with sign", NormalizationMode::SignedNormalized},
           {"noNormalization", "No normalization", NormalizationMode::NotNormalized}},
          0)
    , scales_("scales", "Scale factors")
    , addScale_("addScale", "Add Scale Factor")
    , removeScale_("removeScale", "Remove Scale Factor")
    , useWorldSpace_("useWorldSpaceCoordinateSystem", "World Space", false)
    , borderValue_("borderValue", "Border value", vec4(0.f), vec4(0.f), vec4(1.f), vec4(0.1f))
    , dataRange_("dataRange", "Data Range")
    , rangeMode_("rangeMode", "Mode")
    , outputDataRange_("outputDataRange", "Output Data Range", 0.0, 1.0,
                       std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(),
                       0.01, 0.0, InvalidationLevel::Valid, PropertySemantics::Text)
    , outputValueRange_("outputValueRange", "Output ValueRange", 0.0, 1.0,
                        std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(),
                        0.01, 0.0, InvalidationLevel::Valid, PropertySemantics::Text)
    , customRange_("customRange", "Custom Range")
    , customDataRange_("customDataRange", "Custom Data Range", 0.0, 1.0,
                       std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(),
                       0.01, 0.0, InvalidationLevel::InvalidOutput, PropertySemantics::Text)
    , customValueRange_("customValueRange", "Custom Value Range", 0.0, 1.0,
                        std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(),
                        0.01, 0.0, InvalidationLevel::InvalidOutput, PropertySemantics::Text)
    , fragment_{std::make_shared<StringShaderResource>("volumecombiner.frag", "")}
    , shader_({{ShaderType::Vertex, utilgl::findShaderResource("volume_gpu.vert")},
               {ShaderType::Geometry, utilgl::findShaderResource("volume_gpu.geom")},
               {ShaderType::Fragment, fragment_}},
              Shader::Build::No)
    , fbo_() {

    description_.setSemantics(PropertySemantics::Multiline);
    description_.setReadOnly(true);
    description_.setCurrentStateAsDefault();

    isReady_.setUpdate([this]() { return allInportsAreReady() && (valid_ || dirty_); });

    addPort(inport_);
    addPort(outport_);
    addProperty(description_);
    addProperty(eqn_);
    addProperty(normalizationMode_);
    addProperty(addScale_);
    addProperty(removeScale_);
    addProperty(scales_);
    addProperty(useWorldSpace_);

    useWorldSpace_.addProperty(borderValue_);

    addProperty(dataRange_);
    dataRange_.addProperties(rangeMode_, outputDataRange_, outputValueRange_, customRange_,
                             customDataRange_, customValueRange_);

    outputDataRange_.setReadOnly(true);
    outputValueRange_.setReadOnly(true);

    customRange_.onChange([&]() {
        customDataRange_.setReadOnly(!customRange_.get());
        customValueRange_.setReadOnly(!customRange_.get());
    });
    customDataRange_.setReadOnly(!customRange_.get());
    customValueRange_.setReadOnly(!customRange_.get());

    normalizationMode_.onChange([&]() { dirty_ = true; });

    addScale_.onChange([&]() {
        size_t i = scales_.size();
        auto p = std::make_unique<FloatProperty>("scale" + toString(i), "s" + toString(i + 1), 1.0f,
                                                 -2.f, 2.f, 0.01f);
        p->setSerializationMode(PropertySerializationMode::All);
        scales_.addProperty(p.release());
    });

    removeScale_.onChange([&]() {
        if (scales_.size() > 0) {
            dirty_ = true;
            delete scales_.removeProperty(scales_.getProperties().back());
            isReady_.update();
        }
    });

    eqn_.onChange([&]() {
        dirty_ = true;
        isReady_.update();
    });

    inport_.onConnect([&]() {
        dirty_ = true;
        updateProperties();
        isReady_.update();
    });
    inport_.onDisconnect([&]() {
        dirty_ = true;
        updateProperties();
        isReady_.update();
    });

    useWorldSpace_.onChange([this]() {
        dirty_ = true;
        isReady_.update();
    });
}

std::string VolumeCombiner::buildEquation() const {
    std::map<std::string, double> vars = {};
    std::map<std::string, std::string> symbols;

    for (auto&& i : util::enumerate(inport_)) {
        symbols["s" + toString(i.first() + 1)] = "scale" + toString(i.first());
        symbols["v" + toString(i.first() + 1)] = "vol" + toString(i.first());
    }

    return shuntingyard::Calculator::shaderCode(eqn_.get(), vars, symbols);
}

void VolumeCombiner::buildShader(const std::string& eqn) {
    std::stringstream ss;
    ss << "#include \"utils/structs.glsl\"\n";
    ss << "#include \"utils/sampler3d.glsl\"\n\n";
    ss << "in vec4 texCoord_;\n";

    ss << "uniform vec4 " << borderValue_.getIdentifier() << ";\n";

    for (auto&& i : util::enumerate(inport_)) {
        ss << "uniform sampler3D volume" << idToString(i.first()) << ";\n";
        ss << "uniform VolumeParameters volume" << idToString(i.first()) << "Parameters;\n";
    }
    for (auto prop : scales_.getProperties()) {
        ss << "uniform float " << prop->getIdentifier() << ";\n";
    }

    ss << "\nvoid main() {\n";

    // Determine which normalization mode should be used
    std::string getVoxel;
    auto mode = normalizationMode_.get();
    if (mode == NormalizationMode::Normalized)
        getVoxel = "getNormalizedVoxel";
    else if (mode == NormalizationMode::SignedNormalized)
        getVoxel = "getSignNormalizedVoxel";
    else
        getVoxel = "getVoxel";

    for (auto&& i : util::enumerate(inport_)) {
        const std::string vol = "vol" + toString(i.first());
        const std::string v = "volume" + idToString(i.first());
        const std::string vp = "volume" + idToString(i.first()) + "Parameters";
        if (useWorldSpace_) {
            const std::string coord = "coord" + toString(i.first());
            // Retrieve data from world space and use border value if outside of volume
            ss << "    vec3 " << coord << " = (" << vp << ".worldToTexture * "
               << "volumeParameters.textureToWorld * texCoord_).xyz;\n";
            ss << "    vec4 " << vol << ";\n";
            ss << "    if (all(greaterThanEqual(" << coord << ", vec3(0))) &&"
               << " all(lessThanEqual(" << coord << ", vec3(1)))) {\n";
            ss << "        " << vol << " = " << getVoxel << "(" << v << ", " << vp << ", " << coord
               << ");\n";
            ss << "    } else {\n";
            ss << "        " << vol << " = borderValue;\n";
            ss << "    }\n\n";
        } else {
            ss << "    vec4 " << vol << " = " << getVoxel << "(" << v << ", " << vp
               << ", texCoord_.xyz);\n";
        }
    }

    ss << "    FragData0 = " << eqn << ";\n";
    ss << "    gl_FragDepth = 1.0;\n";
    ss << "}\n";

    fragment_->setSource(ss.str());
    shader_.build();
}

void VolumeCombiner::updateProperties() {
    std::stringstream desc;
    std::vector<OptionPropertyIntOption> options;
    for (auto&& p : util::enumerate(inport_.getConnectedOutports())) {
        const std::string str =
            "v" + toString(p.first() + 1) + ": " + p.second()->getProcessor()->getDisplayName();
        desc << str << "\n";
        options.emplace_back("v" + toString(p.first() + 1), str, static_cast<int>(p.first()));
    }
    options.emplace_back("maxRange", "min/max {v1, v2, ...}", -1);
    description_.set(desc.str());

    rangeMode_.replaceOptions(options);
}

void VolumeCombiner::process() {
    if (inport_.isChanged()) {
        volume_ = std::make_shared<Volume>(*inport_.getData(), noData);
    }

    updateDataRange();

    if (dirty_) {
        dirty_ = false;
        try {
            buildShader(buildEquation());
            valid_ = true;
        } catch (Exception& e) {
            valid_ = false;
            isReady_.update();
            throw Exception(e.getMessage() + ": " + eqn_.get(), IVW_CONTEXT);
        }
    }

    shader_.activate();

    TextureUnitContainer cont;
    int i = 0;
    for (const auto& vol : inport_) {
        utilgl::bindAndSetUniforms(shader_, cont, *vol, "volume" + idToString(i));
        ++i;
    }

    utilgl::setUniforms(shader_, borderValue_);
    for (auto prop : scales_.getProperties()) {
        utilgl::setUniforms(shader_, *static_cast<FloatProperty*>(prop));
    }

    const size3_t dim{inport_.getData()->getDimensions()};
    fbo_.activate();
    glViewport(0, 0, static_cast<GLsizei>(dim.x), static_cast<GLsizei>(dim.y));

    // We always need to ask for a editable representation
    // this will invalidate any other representations
    VolumeGL* outVolumeGL = volume_->getEditableRepresentation<VolumeGL>();
    if (inport_.isChanged()) {
        fbo_.attachColorTexture(outVolumeGL->getTexture().get(), 0);
    }

    utilgl::multiDrawImagePlaneRect(static_cast<int>(dim.z));

    shader_.deactivate();
    fbo_.deactivate();
    isReady_.update();
}

void VolumeCombiner::updateDataRange() {
    dvec2 dataRange = inport_.getData()->dataMap_.dataRange;
    dvec2 valueRange = inport_.getData()->dataMap_.valueRange;

    if (rangeMode_.getSelectedIdentifier() == "maxRange") {
        auto minmax = [](const dvec2& a, const dvec2& b) {
            return dvec2{std::min(a.x, b.x), std::max(a.y, b.y)};
        };

        for (const auto& vol : inport_) {
            dataRange = minmax(dataRange, vol->dataMap_.dataRange);
            valueRange = minmax(valueRange, vol->dataMap_.valueRange);
        }
    } else {
        dataRange = inport_.getVectorData()[rangeMode_.getSelectedValue()]->dataMap_.dataRange;
        valueRange = inport_.getVectorData()[rangeMode_.getSelectedValue()]->dataMap_.valueRange;
    }
    outputDataRange_.set(dataRange);
    outputValueRange_.set(valueRange);

    if (customRange_) {
        dataRange = customDataRange_;
        valueRange = customValueRange_;
    }

    volume_->dataMap_.dataRange = dataRange;
    volume_->dataMap_.valueRange = valueRange;
}

}  // namespace inviwo
<commit_msg>BaseGL: VolumeCombiner fix<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2014-2021 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <modules/basegl/processors/volumeprocessing/volumecombiner.h>
#include <modules/opengl/volume/volumegl.h>
#include <modules/opengl/texture/textureunit.h>
#include <inviwo/core/util/shuntingyard.h>
#include <inviwo/core/util/zip.h>
#include <modules/opengl/texture/textureutils.h>
#include <modules/opengl/shader/shaderutils.h>
#include <modules/opengl/volume/volumeutils.h>

namespace {
std::string idToString(const size_t& id) {
    if (id == 0) return "";
    return inviwo::toString(id);
}
}  // namespace

namespace inviwo {

const ProcessorInfo VolumeCombiner::processorInfo_{
    "org.inviwo.VolumeCombiner",  // Class identifier
    "Volume Combiner",            // Display name
    "Volume Operation",           // Category
    CodeState::Experimental,      // Code state
    Tags::GL,                     // Tags
};
const ProcessorInfo VolumeCombiner::getProcessorInfo() const { return processorInfo_; }

VolumeCombiner::VolumeCombiner()
    : Processor()
    , inport_("inport")
    , outport_("outport")
    , description_("description", "Volumes")
    , eqn_("eqn", "Equation", "v1")
    , normalizationMode_(
          "normalizationMode", "Normalization Mode",
          {{"normalized", "Normalize volumes", NormalizationMode::Normalized},
           {"signedNormalized", "Normalize volumes with sign", NormalizationMode::SignedNormalized},
           {"noNormalization", "No normalization", NormalizationMode::NotNormalized}},
          0)
    , scales_("scales", "Scale factors")
    , addScale_("addScale", "Add Scale Factor")
    , removeScale_("removeScale", "Remove Scale Factor")
    , useWorldSpace_("useWorldSpaceCoordinateSystem", "World Space", false)
    , borderValue_("borderValue", "Border value", vec4(0.f), vec4(0.f), vec4(1.f), vec4(0.1f))
    , dataRange_("dataRange", "Data Range")
    , rangeMode_("rangeMode", "Mode")
    , outputDataRange_("outputDataRange", "Output Data Range", 0.0, 1.0,
                       std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(),
                       0.01, 0.0, InvalidationLevel::Valid, PropertySemantics::Text)
    , outputValueRange_("outputValueRange", "Output ValueRange", 0.0, 1.0,
                        std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(),
                        0.01, 0.0, InvalidationLevel::Valid, PropertySemantics::Text)
    , customRange_("customRange", "Custom Range")
    , customDataRange_("customDataRange", "Custom Data Range", 0.0, 1.0,
                       std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(),
                       0.01, 0.0, InvalidationLevel::InvalidOutput, PropertySemantics::Text)
    , customValueRange_("customValueRange", "Custom Value Range", 0.0, 1.0,
                        std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(),
                        0.01, 0.0, InvalidationLevel::InvalidOutput, PropertySemantics::Text)
    , fragment_{std::make_shared<StringShaderResource>("volumecombiner.frag", "")}
    , shader_({{ShaderType::Vertex, utilgl::findShaderResource("volume_gpu.vert")},
               {ShaderType::Geometry, utilgl::findShaderResource("volume_gpu.geom")},
               {ShaderType::Fragment, fragment_}},
              Shader::Build::No)
    , fbo_() {

    description_.setSemantics(PropertySemantics::Multiline);
    description_.setReadOnly(true);
    description_.setCurrentStateAsDefault();

    isReady_.setUpdate([this]() { return allInportsAreReady() && (valid_ || dirty_); });

    addPort(inport_);
    addPort(outport_);
    addProperty(description_);
    addProperty(eqn_);
    addProperty(normalizationMode_);
    addProperty(addScale_);
    addProperty(removeScale_);
    addProperty(scales_);
    addProperty(useWorldSpace_);

    useWorldSpace_.addProperty(borderValue_);

    addProperty(dataRange_);
    dataRange_.addProperties(rangeMode_, outputDataRange_, outputValueRange_, customRange_,
                             customDataRange_, customValueRange_);

    outputDataRange_.setReadOnly(true);
    outputValueRange_.setReadOnly(true);

    customRange_.onChange([&]() {
        customDataRange_.setReadOnly(!customRange_.get());
        customValueRange_.setReadOnly(!customRange_.get());
    });
    customDataRange_.setReadOnly(!customRange_.get());
    customValueRange_.setReadOnly(!customRange_.get());

    normalizationMode_.onChange([&]() { dirty_ = true; });

    addScale_.onChange([&]() {
        size_t i = scales_.size();
        auto p = std::make_unique<FloatProperty>("scale" + toString(i), "s" + toString(i + 1), 1.0f,
                                                 -2.f, 2.f, 0.01f);
        p->setSerializationMode(PropertySerializationMode::All);
        scales_.addProperty(p.release());
    });

    removeScale_.onChange([&]() {
        if (scales_.size() > 0) {
            dirty_ = true;
            delete scales_.removeProperty(scales_.getProperties().back());
            isReady_.update();
        }
    });

    eqn_.onChange([&]() {
        dirty_ = true;
        isReady_.update();
    });

    inport_.onConnect([&]() {
        dirty_ = true;
        updateProperties();
        isReady_.update();
    });
    inport_.onDisconnect([&]() {
        dirty_ = true;
        updateProperties();
        isReady_.update();
    });

    useWorldSpace_.onChange([this]() {
        dirty_ = true;
        isReady_.update();
    });
}

std::string VolumeCombiner::buildEquation() const {
    std::map<std::string, double> vars = {};
    std::map<std::string, std::string> symbols;

    for (auto&& i : util::enumerate(inport_)) {
        symbols["s" + toString(i.first() + 1)] = "scale" + toString(i.first());
        symbols["v" + toString(i.first() + 1)] = "vol" + toString(i.first());
    }

    return shuntingyard::Calculator::shaderCode(eqn_.get(), vars, symbols);
}

void VolumeCombiner::buildShader(const std::string& eqn) {
    std::stringstream ss;
    ss << "#include \"utils/structs.glsl\"\n";
    ss << "#include \"utils/sampler3d.glsl\"\n\n";
    ss << "in vec4 texCoord_;\n";

    ss << "uniform vec4 " << borderValue_.getIdentifier() << ";\n";

    for (auto&& i : util::enumerate(inport_)) {
        ss << "uniform sampler3D volume" << idToString(i.first()) << ";\n";
        ss << "uniform VolumeParameters volume" << idToString(i.first()) << "Parameters;\n";
    }
    for (auto prop : scales_.getProperties()) {
        ss << "uniform float " << prop->getIdentifier() << ";\n";
    }

    ss << "\nvoid main() {\n";

    // Determine which normalization mode should be used
    std::string getVoxel;
    auto mode = normalizationMode_.get();
    if (mode == NormalizationMode::Normalized)
        getVoxel = "getNormalizedVoxel";
    else if (mode == NormalizationMode::SignedNormalized)
        getVoxel = "getSignNormalizedVoxel";
    else
        getVoxel = "getVoxel";

    for (auto&& i : util::enumerate(inport_)) {
        const std::string vol = "vol" + toString(i.first());
        const std::string v = "volume" + idToString(i.first());
        const std::string vp = "volume" + idToString(i.first()) + "Parameters";
        if (useWorldSpace_) {
            const std::string coord = "coord" + toString(i.first());
            // Retrieve data from world space and use border value if outside of volume
            ss << "    vec3 " << coord << " = (" << vp << ".worldToTexture * "
               << "volumeParameters.textureToWorld * texCoord_).xyz;\n";
            ss << "    vec4 " << vol << ";\n";
            ss << "    if (all(greaterThanEqual(" << coord << ", vec3(0))) &&"
               << " all(lessThanEqual(" << coord << ", vec3(1)))) {\n";
            ss << "        " << vol << " = " << getVoxel << "(" << v << ", " << vp << ", " << coord
               << ");\n";
            ss << "    } else {\n";
            ss << "        " << vol << " = borderValue;\n";
            ss << "    }\n\n";
        } else {
            ss << "    vec4 " << vol << " = " << getVoxel << "(" << v << ", " << vp
               << ", texCoord_.xyz);\n";
        }
    }

    ss << "    FragData0 = " << eqn << ";\n";
    ss << "    gl_FragDepth = 1.0;\n";
    ss << "}\n";

    fragment_->setSource(ss.str());
    shader_.build();
}

void VolumeCombiner::updateProperties() {
    std::stringstream desc;
    std::vector<OptionPropertyIntOption> options;
    for (auto&& p : util::enumerate(inport_.getConnectedOutports())) {
        const std::string str =
            "v" + toString(p.first() + 1) + ": " + p.second()->getProcessor()->getDisplayName();
        desc << str << "\n";
        options.emplace_back("v" + toString(p.first() + 1), str, static_cast<int>(p.first()));
    }
    options.emplace_back("maxRange", "min/max {v1, v2, ...}", -1);
    description_.set(desc.str());

    rangeMode_.replaceOptions(options);
}

void VolumeCombiner::process() {
    if (inport_.isChanged()) {
        volume_ = std::make_shared<Volume>(*inport_.getData(), noData);
        outport_.setData(volume_);
    }

    updateDataRange();

    if (dirty_) {
        dirty_ = false;
        try {
            buildShader(buildEquation());
            valid_ = true;
        } catch (Exception& e) {
            valid_ = false;
            isReady_.update();
            throw Exception(e.getMessage() + ": " + eqn_.get(), IVW_CONTEXT);
        }
    }

    shader_.activate();

    TextureUnitContainer cont;
    int i = 0;
    for (const auto& vol : inport_) {
        utilgl::bindAndSetUniforms(shader_, cont, *vol, "volume" + idToString(i));
        ++i;
    }

    utilgl::setUniforms(shader_, borderValue_);
    for (auto prop : scales_.getProperties()) {
        utilgl::setUniforms(shader_, *static_cast<FloatProperty*>(prop));
    }

    const size3_t dim{inport_.getData()->getDimensions()};
    fbo_.activate();
    glViewport(0, 0, static_cast<GLsizei>(dim.x), static_cast<GLsizei>(dim.y));

    // We always need to ask for a editable representation
    // this will invalidate any other representations
    VolumeGL* outVolumeGL = volume_->getEditableRepresentation<VolumeGL>();
    if (inport_.isChanged()) {
        fbo_.attachColorTexture(outVolumeGL->getTexture().get(), 0);
    }

    utilgl::multiDrawImagePlaneRect(static_cast<int>(dim.z));

    shader_.deactivate();
    fbo_.deactivate();
    isReady_.update();
}

void VolumeCombiner::updateDataRange() {
    dvec2 dataRange = inport_.getData()->dataMap_.dataRange;
    dvec2 valueRange = inport_.getData()->dataMap_.valueRange;

    if (rangeMode_.getSelectedIdentifier() == "maxRange") {
        auto minmax = [](const dvec2& a, const dvec2& b) {
            return dvec2{std::min(a.x, b.x), std::max(a.y, b.y)};
        };

        for (const auto& vol : inport_) {
            dataRange = minmax(dataRange, vol->dataMap_.dataRange);
            valueRange = minmax(valueRange, vol->dataMap_.valueRange);
        }
    } else {
        dataRange = inport_.getVectorData()[rangeMode_.getSelectedValue()]->dataMap_.dataRange;
        valueRange = inport_.getVectorData()[rangeMode_.getSelectedValue()]->dataMap_.valueRange;
    }
    outputDataRange_.set(dataRange);
    outputValueRange_.set(valueRange);

    if (customRange_) {
        dataRange = customDataRange_;
        valueRange = customValueRange_;
    }

    volume_->dataMap_.dataRange = dataRange;
    volume_->dataMap_.valueRange = valueRange;
}

}  // namespace inviwo
<|endoftext|>


<commit_before>/* Copyright (C) 2014 Slawomir Cygan <slawomir.cygan@gmail.com>
*
* 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 "backtrace.h"
#include "dl-intercept.h"
#include "dl.h"
#include "globalstate.h"

#include <DGLCommon/os.h>

#ifdef __ANDROID__
class BackTraceImpl: public BackTrace {
public:
    BackTraceImpl():m_Supported(false) {

        if (!s_CorkscrewDSO) {
            try {
                s_CorkscrewDSO = EarlyGlobalState::getDynLoader().getLibrary("libcorkscrew.so");
            } catch (const std::runtime_error& e) {
                Os::info(e.what());
            }
        }
        if (s_CorkscrewDSO && (!p_unwind_backtrace || !p_get_backtrace_symbols || !p_free_backtrace_symbols)) {
            p_unwind_backtrace = reinterpret_cast<t_unwind_backtrace>(s_CorkscrewDSO->getFunction("unwind_backtrace"));
            p_get_backtrace_symbols = reinterpret_cast<t_get_backtrace_symbols>(s_CorkscrewDSO->getFunction("get_backtrace_symbols"));
            p_free_backtrace_symbols = reinterpret_cast<t_free_backtrace_symbols>(s_CorkscrewDSO->getFunction("free_backtrace_symbols"));
        };

        m_Supported =  (p_unwind_backtrace && p_get_backtrace_symbols && p_free_backtrace_symbols);
    }

   /*
   * Describes a single frame of a backtrace.
   */
    typedef struct {
        uintptr_t absolute_pc;     /* absolute PC offset */
        uintptr_t stack_top;       /* top of stack for this frame */
        size_t stack_size;         /* size of this stack frame */
    } backtrace_frame_t;

    /*
    * Describes the symbols associated with a backtrace frame.
    */
    typedef struct {
        uintptr_t relative_pc;       /* relative frame PC offset from the start of the library,
                                     or the absolute PC if the library is unknown */
        uintptr_t relative_symbol_addr; /* relative offset of the symbol from the start of the
                                        library or 0 if the library is unknown */
        char* map_name;              /* executable or library name, or NULL if unknown */
        char* symbol_name;           /* symbol name, or NULL if unknown */
        char* demangled_name;        /* demangled symbol name, or NULL if unknown */
    } backtrace_symbol_t;

    typedef ssize_t (* t_unwind_backtrace)(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth);
    typedef void (* t_get_backtrace_symbols)(const backtrace_frame_t* backtrace, size_t frames,
        backtrace_symbol_t* backtrace_symbols);
    typedef void (*t_format_backtrace_line)(unsigned frameNumber, const backtrace_frame_t* frame,
        const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize);

    typedef void (* t_free_backtrace_symbols)(backtrace_symbol_t* backtrace_symbols, size_t frames);

private:
    enum {
        MAX_DEPTH = 31,
        MAX_BACKTRACE_LINE_LENGTH = 800
    };

    virtual void streamTo(std::vector<std::string>& str) override {

        if (!m_Supported) {
            throw std::runtime_error("Backtrace support not implemented on this platform.");
        }

        backtrace_frame_t frames[MAX_DEPTH];

        const int ignoreDepth = 1;

        ssize_t count = p_unwind_backtrace(frames, ignoreDepth + 1, MAX_DEPTH);

        void* currentLibraryAddress = DynamicLoader::getCurrentLibraryBaseAddress();

        if (count > 0) {
            std::vector<backtrace_symbol_t> symbols(count);
            p_get_backtrace_symbols(frames, count, &symbols[0]);
            for (ssize_t i = 0; i < count; i++) {

                Dl_info symbolInfo;
                dladdr(reinterpret_cast<void*>(frames[i].absolute_pc), &symbolInfo);
                Os::info("XXX %x == %x\n", symbolInfo.dli_fbase,  currentLibraryAddress);
                if (symbolInfo.dli_fbase == currentLibraryAddress) {
                    //we assume, that removing current library symbols from backtrace
                    //is enough, to filter out DGL from backtrace. This implies no 3rd party
                    //libraries should be called from exporter to get here.
                    continue;
                }

                std::stringstream line;

                if (!symbols[i].map_name) {
                    line << "<unknown module>";
                } else {
                    line << symbols[i].map_name;
                }
                line << '!';
                line << "0x" << std::hex << (int) frames[i].absolute_pc << std::dec << ' ';

                if (symbols[i].demangled_name) {
                    line << symbols[i].demangled_name;
                } else if (symbols[i].symbol_name) {
                    line << symbols[i].symbol_name;
                } else {
                    line << "<unknown function>";
                }

                uint32_t pc_offset = symbols[i].relative_pc - symbols[i].relative_symbol_addr;

                if (pc_offset) {
                    line << " +" << pc_offset;
                }

                str.push_back(line.str());
            }
            p_free_backtrace_symbols(&symbols[0], count);
        }
     }

    bool m_Supported;

    static DynamicLibrary* s_CorkscrewDSO;
    static t_unwind_backtrace p_unwind_backtrace;
    static t_get_backtrace_symbols p_get_backtrace_symbols;
    static t_free_backtrace_symbols p_free_backtrace_symbols;

};
DynamicLibrary* BackTraceImpl::s_CorkscrewDSO = nullptr;
BackTraceImpl::t_unwind_backtrace BackTraceImpl::p_unwind_backtrace = nullptr;
BackTraceImpl::t_get_backtrace_symbols BackTraceImpl::p_get_backtrace_symbols = nullptr;
BackTraceImpl::t_free_backtrace_symbols BackTraceImpl::p_free_backtrace_symbols = nullptr;

#else
#ifdef _WIN32

#include "external/StackWalker/StackWalker.h"
#include "Psapi.h"
#include <sstream>
#include <boost/noncopyable.hpp>

class BackTraceImpl: public BackTrace {

    class DGLStackWalker: public StackWalker, boost::noncopyable {
    public:
        DGLStackWalker(std::vector<std::string>& buffer): m_buffer(buffer) {
            MODULEINFO modInfo;
            if (GetModuleInformation(GetCurrentProcess(), (HMODULE)Os::getCurrentModuleHandle(), &modInfo, sizeof(modInfo))) {
                m_BoundsOfDGLLibrary[0] = reinterpret_cast<intptr_t>(modInfo.lpBaseOfDll);
                m_BoundsOfDGLLibrary[1] = reinterpret_cast<intptr_t>(reinterpret_cast<char*>(modInfo.lpBaseOfDll) + modInfo.SizeOfImage);
            }
        }

        virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) {

            //we assume, that removing current library symbols from backtrace
            //is enough, to filter out DGL from backtrace. This implies no 3rd party
            //libraries should be called from exporter to get here.
            bool outSideOfCurrentLibrary = ((intptr_t)entry.offset <  m_BoundsOfDGLLibrary[0] ||
                                            (intptr_t)entry.offset >= m_BoundsOfDGLLibrary[1]);

            if ( (eType != lastEntry) && (entry.offset != 0) && outSideOfCurrentLibrary)
            {
                std::stringstream line;

                if (entry.moduleName[0] == 0 && entry.loadedImageName == 0) {
                    line << "<unknown module>";
                } else {
                    const char* moduleName;
                    if (!entry.moduleName) {
                        //use full filepath
                        moduleName = entry.loadedImageName;
                    } else {
                        //use module name
                        moduleName = entry.moduleName;

                        if (entry.loadedImageName) {
                            //if we have both, use full file path to obtain file ext.
                            const char* offset = strstr(entry.loadedImageName, entry.moduleName);
                            while (offset) {
                                moduleName = offset;
                                offset = strstr(offset + 1, entry.moduleName);
                            }
                        }
                    }
                    line << moduleName;
                }
                line << '!';
                line << "0x" << std::hex << (int) entry.offset << std::dec << ' ';

                if (entry.undFullName[0] != 0) {
                    line << entry.undFullName;
                } else if (entry.undName[0] != 0) {
                    line << entry.undName;
                } else  if (entry.name[0] != 0) {
                    line << entry.name;
                } else {
                    line << "<unknown function>";
                }
                line << ' ';

                if (entry.lineFileName[0] == 0) {
                    line << "<unknown source file>";
                } else {
                    line << entry.lineFileName;
                    if (entry.lineNumber) {
                        line << " Line " << entry.lineNumber;
                    }
                }
                m_buffer.push_back(line.str());
            }
            StackWalker::OnCallstackEntry(eType, entry);
        }
        std::vector<std::string>& m_buffer;
        intptr_t m_BoundsOfDGLLibrary[2];
    };

    virtual void streamTo(std::vector<std::string>& str) override {
        DGLStackWalker sw(str);
        sw.ShowCallstack();
    }
};

#else
#ifdef __linux__

#include <execinfo.h>

class BackTraceImpl: public BackTrace {
    virtual void streamTo(std::vector<std::string>& ret) override {
        ret.resize(20);
        std::vector<void*> buffer;
        int nptrs = backtrace(&buffer[0], buffer.size());
        while (nptrs > 0 && nptrs == static_cast<int>(buffer.size())) {
            buffer.resize(buffer.size() * 2);
            nptrs = backtrace(&buffer[0], buffer.size());
        }
        char** strings = backtrace_symbols(&buffer[0], buffer.size());
        if (!strings) {
            throw std::runtime_error("Cannot get backtrace: backtrace_symbols failed.");
        }
        ret.resize(buffer.size());
        for (size_t i = 0; i < ret.size(); i++) {
            ret[i] = strings[i];
        }
        free(strings);
    }
};

#else
class BackTraceImpl: public BackTrace {
    virtual void streamTo(std::vector<std::string>&) override {
        throw std::runtime_error("Backtrace support not implemented on this platform.");
    }
};
#endif
#endif
#endif

std::shared_ptr<BackTrace> BackTrace::Get() {
    return std::make_shared<BackTraceImpl>();
}
<commit_msg>Backtrace filtering and fixes for linux<commit_after>/* Copyright (C) 2014 Slawomir Cygan <slawomir.cygan@gmail.com>
*
* 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 "backtrace.h"
#include "dl-intercept.h"
#include "dl.h"
#include "globalstate.h"

#include <DGLCommon/os.h>

#ifdef __ANDROID__
class BackTraceImpl: public BackTrace {
public:
    BackTraceImpl():m_Supported(false) {

        if (!s_CorkscrewDSO) {
            try {
                s_CorkscrewDSO = EarlyGlobalState::getDynLoader().getLibrary("libcorkscrew.so");
            } catch (const std::runtime_error& e) {
                Os::info(e.what());
            }
        }
        if (s_CorkscrewDSO && (!p_unwind_backtrace || !p_get_backtrace_symbols || !p_free_backtrace_symbols)) {
            p_unwind_backtrace = reinterpret_cast<t_unwind_backtrace>(s_CorkscrewDSO->getFunction("unwind_backtrace"));
            p_get_backtrace_symbols = reinterpret_cast<t_get_backtrace_symbols>(s_CorkscrewDSO->getFunction("get_backtrace_symbols"));
            p_free_backtrace_symbols = reinterpret_cast<t_free_backtrace_symbols>(s_CorkscrewDSO->getFunction("free_backtrace_symbols"));
        };

        m_Supported =  (p_unwind_backtrace && p_get_backtrace_symbols && p_free_backtrace_symbols);
    }

   /*
   * Describes a single frame of a backtrace.
   */
    typedef struct {
        uintptr_t absolute_pc;     /* absolute PC offset */
        uintptr_t stack_top;       /* top of stack for this frame */
        size_t stack_size;         /* size of this stack frame */
    } backtrace_frame_t;

    /*
    * Describes the symbols associated with a backtrace frame.
    */
    typedef struct {
        uintptr_t relative_pc;       /* relative frame PC offset from the start of the library,
                                     or the absolute PC if the library is unknown */
        uintptr_t relative_symbol_addr; /* relative offset of the symbol from the start of the
                                        library or 0 if the library is unknown */
        char* map_name;              /* executable or library name, or NULL if unknown */
        char* symbol_name;           /* symbol name, or NULL if unknown */
        char* demangled_name;        /* demangled symbol name, or NULL if unknown */
    } backtrace_symbol_t;

    typedef ssize_t (* t_unwind_backtrace)(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth);
    typedef void (* t_get_backtrace_symbols)(const backtrace_frame_t* backtrace, size_t frames,
        backtrace_symbol_t* backtrace_symbols);
    typedef void (*t_format_backtrace_line)(unsigned frameNumber, const backtrace_frame_t* frame,
        const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize);

    typedef void (* t_free_backtrace_symbols)(backtrace_symbol_t* backtrace_symbols, size_t frames);

private:
    enum {
        MAX_DEPTH = 31,
        MAX_BACKTRACE_LINE_LENGTH = 800
    };

    virtual void streamTo(std::vector<std::string>& str) override {

        if (!m_Supported) {
            throw std::runtime_error("Backtrace support not implemented on this platform.");
        }

        backtrace_frame_t frames[MAX_DEPTH];

        const int ignoreDepth = 1;

        ssize_t count = p_unwind_backtrace(frames, ignoreDepth + 1, MAX_DEPTH);

        void* currentLibraryAddress = DynamicLoader::getCurrentLibraryBaseAddress();

        if (count > 0) {
            std::vector<backtrace_symbol_t> symbols(count);
            p_get_backtrace_symbols(frames, count, &symbols[0]);
            for (ssize_t i = 0; i < count; i++) {

                Dl_info symbolInfo;
                dladdr(reinterpret_cast<void*>(frames[i].absolute_pc), &symbolInfo);
                if (symbolInfo.dli_fbase == currentLibraryAddress) {
                    //we assume, that removing current library symbols from backtrace
                    //is enough, to filter out DGL from backtrace. This implies no 3rd party
                    //libraries should be called from exporter to get here.
                    continue;
                }

                std::stringstream line;

                if (!symbols[i].map_name) {
                    line << "<unknown module>";
                } else {
                    line << symbols[i].map_name;
                }
                line << '!';
                line << "0x" << std::hex << (int) frames[i].absolute_pc << std::dec << ' ';

                if (symbols[i].demangled_name) {
                    line << symbols[i].demangled_name;
                } else if (symbols[i].symbol_name) {
                    line << symbols[i].symbol_name;
                } else {
                    line << "<unknown function>";
                }

                uint32_t pc_offset = symbols[i].relative_pc - symbols[i].relative_symbol_addr;

                if (pc_offset) {
                    line << " +" << pc_offset;
                }

                str.push_back(line.str());
            }
            p_free_backtrace_symbols(&symbols[0], count);
        }
     }

    bool m_Supported;

    static DynamicLibrary* s_CorkscrewDSO;
    static t_unwind_backtrace p_unwind_backtrace;
    static t_get_backtrace_symbols p_get_backtrace_symbols;
    static t_free_backtrace_symbols p_free_backtrace_symbols;

};
DynamicLibrary* BackTraceImpl::s_CorkscrewDSO = nullptr;
BackTraceImpl::t_unwind_backtrace BackTraceImpl::p_unwind_backtrace = nullptr;
BackTraceImpl::t_get_backtrace_symbols BackTraceImpl::p_get_backtrace_symbols = nullptr;
BackTraceImpl::t_free_backtrace_symbols BackTraceImpl::p_free_backtrace_symbols = nullptr;

#else
#ifdef _WIN32

#include "external/StackWalker/StackWalker.h"
#include "Psapi.h"
#include <sstream>
#include <boost/noncopyable.hpp>

class BackTraceImpl: public BackTrace {

    class DGLStackWalker: public StackWalker, boost::noncopyable {
    public:
        DGLStackWalker(std::vector<std::string>& buffer): m_buffer(buffer) {
            MODULEINFO modInfo;
            if (GetModuleInformation(GetCurrentProcess(), (HMODULE)Os::getCurrentModuleHandle(), &modInfo, sizeof(modInfo))) {
                m_BoundsOfDGLLibrary[0] = reinterpret_cast<intptr_t>(modInfo.lpBaseOfDll);
                m_BoundsOfDGLLibrary[1] = reinterpret_cast<intptr_t>(reinterpret_cast<char*>(modInfo.lpBaseOfDll) + modInfo.SizeOfImage);
            }
        }

        virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) {

            //we assume, that removing current library symbols from backtrace
            //is enough, to filter out DGL from backtrace. This implies no 3rd party
            //libraries should be called from exporter to get here.
            bool outSideOfCurrentLibrary = ((intptr_t)entry.offset <  m_BoundsOfDGLLibrary[0] ||
                                            (intptr_t)entry.offset >= m_BoundsOfDGLLibrary[1]);

            if ( (eType != lastEntry) && (entry.offset != 0) && outSideOfCurrentLibrary)
            {
                std::stringstream line;

                if (entry.moduleName[0] == 0 && entry.loadedImageName == 0) {
                    line << "<unknown module>";
                } else {
                    const char* moduleName;
                    if (!entry.moduleName) {
                        //use full filepath
                        moduleName = entry.loadedImageName;
                    } else {
                        //use module name
                        moduleName = entry.moduleName;

                        if (entry.loadedImageName) {
                            //if we have both, use full file path to obtain file ext.
                            const char* offset = strstr(entry.loadedImageName, entry.moduleName);
                            while (offset) {
                                moduleName = offset;
                                offset = strstr(offset + 1, entry.moduleName);
                            }
                        }
                    }
                    line << moduleName;
                }
                line << '!';
                line << "0x" << std::hex << (int) entry.offset << std::dec << ' ';

                if (entry.undFullName[0] != 0) {
                    line << entry.undFullName;
                } else if (entry.undName[0] != 0) {
                    line << entry.undName;
                } else  if (entry.name[0] != 0) {
                    line << entry.name;
                } else {
                    line << "<unknown function>";
                }
                line << ' ';

                if (entry.lineFileName[0] == 0) {
                    line << "<unknown source file>";
                } else {
                    line << entry.lineFileName;
                    if (entry.lineNumber) {
                        line << " Line " << entry.lineNumber;
                    }
                }
                m_buffer.push_back(line.str());
            }
            StackWalker::OnCallstackEntry(eType, entry);
        }
        std::vector<std::string>& m_buffer;
        intptr_t m_BoundsOfDGLLibrary[2];
    };

    virtual void streamTo(std::vector<std::string>& str) override {
        DGLStackWalker sw(str);
        sw.ShowCallstack();
    }
};

#else
#ifdef __linux__

#include <execinfo.h>

class BackTraceImpl: public BackTrace {
    virtual void streamTo(std::vector<std::string>& ret) override {
        std::vector<void*> buffer(20);
        int nptrs = backtrace(&buffer[0], buffer.size());
        while (nptrs > 0 && nptrs == static_cast<int>(buffer.size())) {
            buffer.resize(buffer.size() * 2);
            nptrs = backtrace(&buffer[0], buffer.size());
        }
        char** strings = backtrace_symbols(&buffer[0], buffer.size());
        if (!strings) {
            throw std::runtime_error("Cannot get backtrace: backtrace_symbols failed.");
        }

        void* currentLibraryAddress = DynamicLoader::getCurrentLibraryBaseAddress();

        ret.resize(nptrs);

        int realNptrs = 0;

        for (size_t i = 0; i < ret.size(); i++) {
            Dl_info symbolInfo;
            dladdr(reinterpret_cast<void*>(buffer[i]), &symbolInfo);
            Os::info("XXX %x == %x\n", symbolInfo.dli_fbase,  currentLibraryAddress);
            if (symbolInfo.dli_fbase == currentLibraryAddress) {
                //we assume, that removing current library symbols from backtrace
                //is enough, to filter out DGL from backtrace. This implies no 3rd party
                //libraries should be called from exporter to get here.
                continue;
            }
            ret[realNptrs++] = strings[i];
        }
        free(strings);
        ret.resize(realNptrs);
    }
};

#else
class BackTraceImpl: public BackTrace {
    virtual void streamTo(std::vector<std::string>&) override {
        throw std::runtime_error("Backtrace support not implemented on this platform.");
    }
};
#endif
#endif
#endif

std::shared_ptr<BackTrace> BackTrace::Get() {
    return std::make_shared<BackTraceImpl>();
}
<|endoftext|>


<commit_before>/** @file
    @brief Comprehensive example: Implementation of a dummy Hardware Detect
   Callback that creates a dummy device when it is "detected"

    @date 2014

    @author
    Sensics, Inc.
    <http://sensics.com/osvr>
*/

// Copyright 2014 Sensics, Inc.
//
// 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.

// Internal Includes
#include <osvr/PluginKit/PluginKit.h>
#include <osvr/PluginKit/AnalogInterfaceC.h>
#include <osvr/PluginKit/ButtonInterfaceC.h>
#include <osvr/PluginKit/TrackerInterfaceC.h>

// Generated JSON header file
#include "NodOSVR.h"
#include "NodPlugin.h"

#pragma comment(lib, "OpenSpatialDll.lib")

// Library/third-party includes
// - none

// Standard includes
#include <iostream>

// Global device count for path incrementation
int deviceCount = 0;

// Anonymous namespace to avoid symbol collision
namespace {

class Backspin {
  public:
    Backspin(OSVR_PluginRegContext ctx) {
        /// Create the initialization options
        OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);

        /// config channels
        osvrDeviceAnalogConfigure(opts, &m_analog, 3);
        osvrDeviceButtonConfigure(opts, &m_button, 10);
        osvrDeviceTrackerConfigure(opts, &m_orientation);
        /// Create the device token with the options
        m_dev.initAsync(ctx, std::to_string(deviceCount).c_str(), opts);

        /// Send JSON descriptor
        m_dev.sendJsonDescriptor(NodOSVR);

        /// Register update callback
        m_dev.registerUpdateCallback(this);

        // initialize orientation
        m_orientationval.data[0] = 0.0;
        m_orientationval.data[1] = 0.0;
        m_orientationval.data[2] = 0.0;
        m_orientationval.data[3] = 0.0;
    }
    int flip = 1;
    OSVR_ReturnCode update() {

        /// send trigger, joystick x and joystick y
        osvrDeviceAnalogSetValues(m_dev, m_analog, m_analogval, 3);

        // Don't send the first value of the button (plugin was sending 0 when it started up)
        if (buttonPressed)
        {
            //send all buttons (proto1 10 buttons)
            osvrDeviceButtonSetValues(m_dev, m_button, m_buttons, 10);
        }

        // send orientation quaternion and 0,0,0 for position (could just send orientation, but
        // pose is expected by most programs
        OSVR_Pose3 pose;
        pose.rotation = m_orientationval;
        pose.translation = { 0,0,0 };
        osvrDeviceTrackerSendPose(m_dev, m_orientation, &pose, 0);

        return OSVR_RETURN_SUCCESS;
    }

    OSVR_AnalogState m_analogval[3] = { 255, 128, 128 };
    OSVR_ButtonState m_buttons[10] = { 0,0,0,0,0,0,0,0,0,0 };
    OSVR_OrientationState m_orientationval;
    bool buttonPressed = false;
  private:
      osvr::pluginkit::DeviceToken m_dev;
      OSVR_AnalogDeviceInterface m_analog;
      OSVR_ButtonDeviceInterface m_button;
      OSVR_TrackerDeviceInterface m_orientation;
};

std::vector<Backspin*> devices;

void euler(const float& pitch, const float& roll, const float& yaw, double* mData)
{
    const float sinHalfYaw = (float)sin(yaw / 2.0f);
    const float cosHalfYaw = (float)cos(yaw / 2.0f);
    const float sinHalfPitch = (float)sin(pitch / 2.0f);
    const float cosHalfPitch = (float)cos(pitch / 2.0f);
    const float sinHalfRoll = (float)sin(roll / 2.0f);
    const float cosHalfRoll = (float)cos(roll / 2.0f);

    mData[0] = -cosHalfRoll * sinHalfPitch * sinHalfYaw
        + cosHalfPitch * cosHalfYaw * sinHalfRoll;
    mData[1] = cosHalfRoll * cosHalfYaw * sinHalfPitch
        + sinHalfRoll * cosHalfPitch * sinHalfYaw;
    mData[2] = cosHalfRoll * cosHalfPitch * sinHalfYaw
        - sinHalfRoll * cosHalfYaw * sinHalfPitch;
    mData[3] = cosHalfRoll * cosHalfPitch * cosHalfYaw
        + sinHalfRoll * sinHalfPitch * sinHalfYaw;
}

void OpenSpatialEventFired(NodEvent ev)
{
    if (ev.type == EventType::ServiceReady)
    {
        std::cout << "[NOD PLUGIN] Service Ready" << std::endl;
        NodSubscribe(Modality::GameControlMode, "nod2-004008");
        NodSubscribe(Modality::ButtonMode, "nod2-004008");
        NodSubscribe(Modality::EulerMode, "nod2-004008");
    }
    if (ev.type == EventType::AnalogData)
    {
        Backspin* dev = devices.at(0);
        dev->m_analogval[0] = ev.trigger;
        dev->m_analogval[1] = ev.x;
        dev->m_analogval[2] = ev.y;
    }
    if (ev.type == EventType::Button)
    {
        Backspin* dev = devices.at(0);
        int index = ev.buttonID;
        dev->buttonPressed = true;
        dev->m_buttons[index] = (ev.buttonState == UP) ? 0 : 1;
    }
    if (ev.type == EventType::EulerAngles)
    {
        Backspin* dev = devices.at(0);
        double val[4];
        euler(ev.pitch, ev.roll, ev.yaw, val);
        dev->m_orientationval.data[0] = val[0];
        dev->m_orientationval.data[1] = val[1];
        dev->m_orientationval.data[2] = val[2];
        dev->m_orientationval.data[3] = val[3];
    }
}

class HardwareDetection {
  public:
    HardwareDetection() : m_found(false) { NodInitialize(OpenSpatialEventFired); }
    OSVR_ReturnCode operator()(OSVR_PluginRegContext ctx) {
        std::cout << "[NOD PLUGIN] Got a hardware detection request" << std::endl;
        if (!m_found)
        {
            if (NodNumRings() > 0)
            {
                std::cout << "[NOD PLUGIN] found device" << std::endl;
                for (int i = 0; i < NodNumRings(); i++)
                {
                    Backspin* dev = new Backspin(ctx);
                    devices.push_back(dev);
                    osvr::pluginkit::registerObjectForDeletion(
                        ctx, dev);
                    m_found = true;
                }
            }
            else
            {
                std::cout << "[NOD PLUGIN]: no devices found" << std::endl;
            }
        }
        return OSVR_RETURN_SUCCESS;
    }

  private:
    /// @brief Have we found our device yet? (this limits the plugin to one
    /// instance)
    bool m_found;
};
} // namespace

OSVR_PLUGIN(com_osvr_example_selfcontained) {
    osvr::pluginkit::PluginContext context(ctx);

    /// Register a detection callback function object.
    context.registerHardwareDetectCallback(new HardwareDetection());

    return OSVR_RETURN_SUCCESS;
}
<commit_msg>Fixes + Multiple devices<commit_after>/** @file
    @brief Comprehensive example: Implementation of a dummy Hardware Detect
   Callback that creates a dummy device when it is "detected"

    @date 2014

    @author
    Sensics, Inc.
    <http://sensics.com/osvr>
*/

// Copyright 2014 Sensics, Inc.
//
// 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.

// Internal Includes
#include <osvr/PluginKit/PluginKit.h>
#include <osvr/PluginKit/AnalogInterfaceC.h>
#include <osvr/PluginKit/ButtonInterfaceC.h>
#include <osvr/PluginKit/TrackerInterfaceC.h>

// Generated JSON header file
#include "NodOSVR.h"
#include "NodPlugin.h"

#pragma comment(lib, "OpenSpatialDll.lib")

// Library/third-party includes
// - none

// Standard includes
#include <iostream>

// Global device count for path incrementation
int deviceCount = 0;
std::vector<Backspin*> devices;

// Anonymous namespace to avoid symbol collision
namespace {

class Backspin {
  public:
    Backspin(OSVR_PluginRegContext ctx) {
        /// Create the initialization options
        OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);

        /// config channels
        osvrDeviceAnalogConfigure(opts, &m_analog, 3);
        osvrDeviceButtonConfigure(opts, &m_button, 10);
        osvrDeviceTrackerConfigure(opts, &m_orientation);
        /// Create the device token with the options
        m_dev.initAsync(ctx, std::to_string(deviceCount).c_str(), opts);
        deviceCount++;

        /// Send JSON descriptor
        m_dev.sendJsonDescriptor(NodOSVR);

        /// Register update callback
        m_dev.registerUpdateCallback(this);

        // initialize orientation
        m_orientationval.data[0] = 0.0;
        m_orientationval.data[1] = 0.0;
        m_orientationval.data[2] = 0.0;
        m_orientationval.data[3] = 0.0;
    }
    int flip = 1;
    OSVR_ReturnCode update() {

        /// send trigger, joystick x and joystick y
        osvrDeviceAnalogSetValues(m_dev, m_analog, m_analogval, 3);

        // Don't send the first value of the button (plugin was sending 0 when it started up)
        if (buttonPressed)
        {
            //send all buttons (proto1 10 buttons)
            osvrDeviceButtonSetValues(m_dev, m_button, m_buttons, 10);
        }

        // send orientation quaternion and 0,0,0 for position (could just send orientation, but
        // pose is expected by most programs
        OSVR_Pose3 pose;
        pose.rotation = m_orientationval;
        pose.translation = { 0,0,0 };
        osvrDeviceTrackerSendPose(m_dev, m_orientation, &pose, 0);

        return OSVR_RETURN_SUCCESS;
    }

    OSVR_AnalogState m_analogval[3] = { 255, 128, 128 };
    OSVR_ButtonState m_buttons[10] = { 0,0,0,0,0,0,0,0,0,0 };
    OSVR_OrientationState m_orientationval;
    bool buttonPressed = false;
  private:
      osvr::pluginkit::DeviceToken m_dev;
      OSVR_AnalogDeviceInterface m_analog;
      OSVR_ButtonDeviceInterface m_button;
      OSVR_TrackerDeviceInterface m_orientation;
};

void euler(const float& pitch, const float& roll, const float& yaw, double* mData)
{
    const float sinHalfYaw = (float)sin(yaw / 2.0f);
    const float cosHalfYaw = (float)cos(yaw / 2.0f);
    const float sinHalfPitch = (float)sin(pitch / 2.0f);
    const float cosHalfPitch = (float)cos(pitch / 2.0f);
    const float sinHalfRoll = (float)sin(roll / 2.0f);
    const float cosHalfRoll = (float)cos(roll / 2.0f);

    mData[0] = -cosHalfRoll * sinHalfPitch * sinHalfYaw
        + cosHalfPitch * cosHalfYaw * sinHalfRoll;
    mData[1] = cosHalfRoll * cosHalfYaw * sinHalfPitch
        + sinHalfRoll * cosHalfPitch * sinHalfYaw;
    mData[2] = cosHalfRoll * cosHalfPitch * sinHalfYaw
        - sinHalfRoll * cosHalfYaw * sinHalfPitch;
    mData[3] = cosHalfRoll * cosHalfPitch * cosHalfYaw
        + sinHalfRoll * sinHalfPitch * sinHalfYaw;
}

void OpenSpatialEventFired(NodEvent ev)
{
    if (ev.type == EventType::ServiceReady)
    {
        std::cout << "[NOD PLUGIN] Service Ready" << std::endl;
        for (int i = 0; i < NodNumRings(); i++)
        {
            NodSubscribe(Modality::GameControlMode, NodGetRingName(i));
            NodSubscribe(Modality::ButtonMode, NodGetRingName(i));
            NodSubscribe(Modality::EulerMode, NodGetRingName(i));
        }
    }
    if (ev.type == EventType::AnalogData)
    {
        Backspin* dev = devices.at(ev.sender);
        dev->m_analogval[0] = ev.trigger;
        dev->m_analogval[1] = ev.x;
        dev->m_analogval[2] = ev.y;
    }
    if (ev.type == EventType::Button)
    {
        Backspin* dev = devices.at(ev.sender);
        int index = ev.buttonID;
        dev->buttonPressed = true;
        dev->m_buttons[index] = (ev.buttonState == UP) ? 0 : 1;
    }
    if (ev.type == EventType::EulerAngles)
    {
        Backspin* dev = devices.at(ev.sender);
        double val[4];
        euler(ev.pitch, ev.roll, ev.yaw, val);
        dev->m_orientationval.data[0] = val[0];
        dev->m_orientationval.data[1] = val[1];
        dev->m_orientationval.data[2] = val[2];
        dev->m_orientationval.data[3] = val[3];
    }
}

class HardwareDetection {
  public:
    HardwareDetection() : m_found(false) { NodInitialize(OpenSpatialEventFired); }
    OSVR_ReturnCode operator()(OSVR_PluginRegContext ctx) {
        std::cout << "[NOD PLUGIN] Got a hardware detection request" << std::endl;
        if (!m_found)
        {
            if (NodNumRings() > 0)
            {
                std::cout << "[NOD PLUGIN] found device" << std::endl;
                for (int i = 0; i < NodNumRings(); i++)
                {
                    Backspin* dev = new Backspin(ctx);
                    devices.push_back(dev);
                    osvr::pluginkit::registerObjectForDeletion(
                        ctx, dev);
                    m_found = true;
                }
            }
            else
            {
                std::cout << "[NOD PLUGIN]: no devices found" << std::endl;
            }
        }
        return OSVR_RETURN_SUCCESS;
    }

  private:
    /// @brief Have we found our device yet? (this limits the plugin to one
    /// instance)
    bool m_found;
};
} // namespace

OSVR_PLUGIN(com_osvr_example_selfcontained) {
    osvr::pluginkit::PluginContext context(ctx);

    /// Register a detection callback function object.
    context.registerHardwareDetectCallback(new HardwareDetection());

    return OSVR_RETURN_SUCCESS;
}
<|endoftext|>