<commit_before>/*
* Copyright (c) 2003-2015 Rony Shapiro <ronys@users.sourceforge.net>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

/**
 * \file Linux-specific implementation of dir.h
 */
#include "../dir.h"
#include "../utf8conv.h" // for pws_os::towc
#include "../env.h"
#include <unistd.h>
#include <limits.h>
#include <cassert>
#include <cstdlib>
#include <cstring> // for memset
#include <sys/stat.h>

stringT pws_os::getexecdir()
{
  char path[PATH_MAX];

  std::memset(path, 0, sizeof(path)); // keep valgrind happy
  if (::readlink("/proc/self/exe", path, PATH_MAX) < 0)
    return _T("?");
  else {
    stringT retval(pws_os::towc(path));
    stringT::size_type last_slash = retval.find_last_of(_T("/"));
    return retval.substr(0, last_slash + 1);
  }
}

stringT pws_os::getcwd()
{
  char curdir[PATH_MAX];
  if (::getcwd(curdir, PATH_MAX) == NULL) {
    curdir[0] = '?'; curdir[1] = '\0';
  }
  stringT CurDir(pws_os::towc(curdir));
  return CurDir;
}

bool pws_os::chdir(const stringT &dir)
{
  assert(!dir.empty());
  const char *szdir = NULL;
  size_t N = std::wcstombs(NULL, dir.c_str(), 0) + 1;
  assert(N > 0);
  szdir = new char[N];
  std::wcstombs(const_cast<char *>(szdir), dir.c_str(), N);
  bool retval = (::chdir(szdir) == 0);
  delete[] szdir;
  return retval;
}

bool pws_os::splitpath(const stringT &path,
                       stringT &drive, stringT &dir,
                       stringT &file, stringT &ext)
{
  if (path.empty())
    return false;

  stringT::size_type last_slash = path.find_last_of(_T("/"));
  dir = path.substr(0, last_slash + 1);
  if (dir.empty())
    dir = _T("./");
  drive = (dir[0] == '/') ? _T("/") : _T("./");
  stringT::size_type last_dot = path.find_last_of(_T("."));
  if (last_dot != stringT::npos && last_dot > last_slash) {
    file = path.substr(last_slash + 1, last_dot - last_slash - 1);
    ext = path.substr(last_dot + 1);
  } else {
    file = path.substr(last_slash + 1);
    ext = _T("");
  }
  return true;
}

stringT pws_os::makepath(const stringT &drive, const stringT &dir,
                         const stringT &file, const stringT &ext)
{
  stringT retval(drive);
  retval += dir;
  if (!dir.empty() && retval[retval.length()-1] != '/')
    retval += _T("/");
  retval += file;
  if (!ext.empty()) {
    retval += _T(".");
    retval += ext;
  }
  return retval;
}

static stringT createuserprefsdir(void)
{
  stringT cfgdir = pws_os::getenv("HOME", true);
  if (!cfgdir.empty()) {
    cfgdir += _S("/.pwsafe");
    struct stat statbuf;
    switch (::lstat(pws_os::tomb(cfgdir).c_str(), &statbuf)) {
    case 0:
      if (!S_ISDIR(statbuf.st_mode))
        cfgdir.clear();  // not a dir - can't use it.
      break;
    case -1:  // dir doesn't exist.  Or should we check errno too?
      {
        const mode_t oldmode = umask(0);
        if (mkdir(pws_os::tomb(cfgdir).c_str(), S_IRUSR|S_IWUSR|S_IXUSR) == -1)
          cfgdir.clear();
        umask(oldmode);
        break;
      }
    default:
      assert(false);
      cfgdir.clear();
      break;
    }
    if (!cfgdir.empty())
      cfgdir += _S('/');
  } // $HOME defined
  return cfgdir;
}

stringT pws_os::getuserprefsdir(void)
{
  /**
   * Returns $(HOME)/.pwsafe, creating it if needed.
   * If creation failed, return empty string, caller
   * will fallback to something else or fail gracefully
   */
  static const stringT cfgdir = createuserprefsdir();
  return cfgdir;
}

stringT pws_os::getsafedir(void)
{
  return getuserprefsdir(); // same-same on linux
}

stringT pws_os::getxmldir(void)
{
  return _S("/usr/share/pwsafe/xml/");
}

stringT pws_os::gethelpdir(void)
{
  return _S("/usr/share/doc/passwordsafe/help/");
}
<commit_msg>Fixed regression caused by d04da24 (BR1265)<commit_after>/*
* Copyright (c) 2003-2015 Rony Shapiro <ronys@users.sourceforge.net>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

/**
 * \file Linux-specific implementation of dir.h
 */
#include "../dir.h"
#include "../utf8conv.h" // for pws_os::towc
#include "../env.h"
#include <unistd.h>
#include <limits.h>
#include <cassert>
#include <cstdlib>
#include <cstring> // for memset
#include <sys/stat.h>

stringT pws_os::getexecdir()
{
  char path[PATH_MAX];

  std::memset(path, 0, sizeof(path)); // keep valgrind happy
  if (::readlink("/proc/self/exe", path, PATH_MAX) < 0)
    return _T("?");
  else {
    stringT retval(pws_os::towc(path));
    stringT::size_type last_slash = retval.find_last_of(_T("/"));
    return retval.substr(0, last_slash + 1);
  }
}

stringT pws_os::getcwd()
{
  char curdir[PATH_MAX];
  if (::getcwd(curdir, PATH_MAX) == NULL) {
    curdir[0] = '?'; curdir[1] = '\0';
  }
  stringT CurDir(pws_os::towc(curdir));
  return CurDir;
}

bool pws_os::chdir(const stringT &dir)
{
  assert(!dir.empty());
  const char *szdir = NULL;
  size_t N = std::wcstombs(NULL, dir.c_str(), 0) + 1;
  assert(N > 0);
  szdir = new char[N];
  std::wcstombs(const_cast<char *>(szdir), dir.c_str(), N);
  bool retval = (::chdir(szdir) == 0);
  delete[] szdir;
  return retval;
}

bool pws_os::splitpath(const stringT &path,
                       stringT &drive, stringT &dir,
                       stringT &file, stringT &ext)
{
  if (path.empty())
    return false;

  stringT::size_type last_slash = path.find_last_of(_T("/"));
  dir = path.substr(0, last_slash + 1);
  if (dir.empty())
    dir = _T("./");
  drive = (dir[0] == '/') ? _T("/") : _T("./");
  stringT::size_type last_dot = path.find_last_of(_T("."));
  if (last_dot != stringT::npos && last_dot > last_slash) {
    file = path.substr(last_slash + 1, last_dot - last_slash - 1);
    ext = path.substr(last_dot + 1);
  } else {
    file = path.substr(last_slash + 1);
    ext = _T("");
  }
  return true;
}

stringT pws_os::makepath(const stringT &drive, const stringT &dir,
                         const stringT &file, const stringT &ext)
{
  stringT retval;
  /**
   * drive and dir can semantically be "./" with no ill effect,
   * but wxFileDialog doesn't like a "filename" with "/"s.
   */
  if (drive != L"./")
    retval = drive;
  if (dir != L"./") {
    retval += dir;
    if (!dir.empty() && retval[retval.length()-1] != '/')
      retval += _T("/");
  }
  retval += file;
  if (!ext.empty()) {
    retval += _T(".");
    retval += ext;
  }
  return retval;
}

static stringT createuserprefsdir(void)
{
  stringT cfgdir = pws_os::getenv("HOME", true);
  if (!cfgdir.empty()) {
    cfgdir += _S("/.pwsafe");
    struct stat statbuf;
    switch (::lstat(pws_os::tomb(cfgdir).c_str(), &statbuf)) {
    case 0:
      if (!S_ISDIR(statbuf.st_mode))
        cfgdir.clear();  // not a dir - can't use it.
      break;
    case -1:  // dir doesn't exist.  Or should we check errno too?
      {
        const mode_t oldmode = umask(0);
        if (mkdir(pws_os::tomb(cfgdir).c_str(), S_IRUSR|S_IWUSR|S_IXUSR) == -1)
          cfgdir.clear();
        umask(oldmode);
        break;
      }
    default:
      assert(false);
      cfgdir.clear();
      break;
    }
    if (!cfgdir.empty())
      cfgdir += _S('/');
  } // $HOME defined
  return cfgdir;
}

stringT pws_os::getuserprefsdir(void)
{
  /**
   * Returns $(HOME)/.pwsafe, creating it if needed.
   * If creation failed, return empty string, caller
   * will fallback to something else or fail gracefully
   */
  static const stringT cfgdir = createuserprefsdir();
  return cfgdir;
}

stringT pws_os::getsafedir(void)
{
  return getuserprefsdir(); // same-same on linux
}

stringT pws_os::getxmldir(void)
{
  return _S("/usr/share/pwsafe/xml/");
}

stringT pws_os::gethelpdir(void)
{
  return _S("/usr/share/doc/passwordsafe/help/");
}
<|endoftext|>


<commit_before>/////////////////////////////////////////////////////////////////////////////
// James Cherry, Parallax Software, Inc.
//
// BSD 3-Clause License
//
// Copyright (c) 2020, James Cherry, Parallax Software, Inc.
// 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 the copyright holder nor the names of its
//   contributors 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 HOLDER 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 "opendp/Opendp.h"

#include <unordered_set>
#include "opendb/dbTypes.h"

namespace opendp {

using std::sort;
using std::unordered_set;

using odb::dbITerm;
using odb::dbOrientType;

static dbOrientType
orientMirrorY(dbOrientType orient);

NetBox::NetBox(dbNet *n) :
  net(n)
{
}

int64_t
NetBox::hpwl()
{
  return box.xMax() - box.xMin() + box.yMax() - box.yMin();
}

////////////////////////////////////////////////////////////////

void
Opendp::optimizeMirroring()
{
  NetBoxes net_boxes;
  findNetBoxes(net_boxes);
  // Sort net boxes by net hpwl.
  sort(net_boxes.begin(), net_boxes.end(),
       [] (NetBox &net_box1, NetBox &net_box2) -> bool {
	 return net_box1.hpwl() > net_box2.hpwl();
       });

  vector<dbInst*> mirror_candidates;
  findMirrorCandidates(net_boxes, mirror_candidates);
  int64_t hpwl_before = hpwl();
  mirrorCandidates(mirror_candidates);
  double hpwl_after = hpwl();
  printf("HPWL before          %8.1f u\n", dbuToMicrons(hpwl_before));
  printf("HPWL after           %8.1f u\n", dbuToMicrons(hpwl_after));
  double hpwl_delta = (hpwl_after - hpwl_before) / hpwl_before * 100;
  printf("HPWL delta           %8.0f %%\n", hpwl_delta);
}

void
Opendp::findNetBoxes(NetBoxes &net_boxes)
{
  auto nets = block_->getNets();
  net_boxes.reserve(nets.size());
  for (dbNet *net : nets) {
    if (!isSupply(net)) {
      NetBox net_box(net);
      getBox(net, net_box.box);
      net_boxes.push_back(net_box);
    }
  }
}

void
Opendp::findMirrorCandidates(NetBoxes &net_boxes,
			     vector<dbInst*> &mirror_candidates)
{
  unordered_set<dbInst*> existing;
  // Find inst terms on the boundary of the net boxes.
  for (NetBox &net_box : net_boxes) {
    dbNet *net = net_box.net;
    Rect &box = net_box.box;
    for (dbITerm *iterm : net->getITerms()) {
      int x, y;
      if (iterm->getAvgXY(&x, &y)) {
	if (x == box.xMin() || x == box.xMax()
	    || y == box.yMin() || y == box.yMax()) {
	  dbInst *inst = iterm->getInst();
	  if (existing.find(inst) == existing.end()) {
	    mirror_candidates.push_back(inst);
	    existing.insert(inst);
	  }
	}
      }
    }
  }
}

void
Opendp::mirrorCandidates(vector<dbInst*> &mirror_candidates)
{
  for (dbInst *inst : mirror_candidates) {
    // Use hpwl of all nets connected to the instance terms
    // before/after to determine incremental change to total hpwl.
    int64_t hpwl_before = hpwl(inst);
    dbOrientType orient = inst->getOrient();
    dbOrientType orient_my = orientMirrorY(orient);
    inst->setLocationOrient(orient_my);
    int64_t hpwl_after = hpwl(inst);
    if (hpwl_after > hpwl_before)
      // Undo mirroring if hpwl is worse.
      inst->setLocationOrient(orient);
  }
}

// apply mirror about Y axis to orient
static dbOrientType
orientMirrorY(dbOrientType orient)
{
  switch (orient) {
  case dbOrientType::R0:
    return dbOrientType::MY;
  case dbOrientType::MX:
    return dbOrientType::R180;
  case dbOrientType::MY:
    return dbOrientType::R0;
  case dbOrientType::R180:
    return dbOrientType::MX;
  case dbOrientType::R90:
    return dbOrientType::MXR90;
  case dbOrientType::MXR90:
    return dbOrientType::R90;
  case dbOrientType::R270:
    return dbOrientType::MYR90;
  case dbOrientType::MYR90:
    return dbOrientType::R270;
  }
}

int64_t
Opendp::hpwl(dbInst *inst)
{
  int64_t inst_hpwl = 0;
  for (dbITerm *iterm : inst->getITerms()) {
    dbNet *net = iterm->getNet();
    if (net)
      inst_hpwl += hpwl(net);
  }
  return inst_hpwl;
}

}  // namespace opendp
<commit_msg>gcc warning<commit_after>/////////////////////////////////////////////////////////////////////////////
// James Cherry, Parallax Software, Inc.
//
// BSD 3-Clause License
//
// Copyright (c) 2020, James Cherry, Parallax Software, Inc.
// 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 the copyright holder nor the names of its
//   contributors 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 HOLDER 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 "opendp/Opendp.h"

#include <unordered_set>
#include <cstdlib>
#include "opendb/dbTypes.h"

namespace opendp {

using std::sort;
using std::unordered_set;

using odb::dbITerm;
using odb::dbOrientType;

static dbOrientType
orientMirrorY(dbOrientType orient);

NetBox::NetBox(dbNet *n) :
  net(n)
{
}

int64_t
NetBox::hpwl()
{
  return box.xMax() - box.xMin() + box.yMax() - box.yMin();
}

////////////////////////////////////////////////////////////////

void
Opendp::optimizeMirroring()
{
  NetBoxes net_boxes;
  findNetBoxes(net_boxes);
  // Sort net boxes by net hpwl.
  sort(net_boxes.begin(), net_boxes.end(),
       [] (NetBox &net_box1, NetBox &net_box2) -> bool {
	 return net_box1.hpwl() > net_box2.hpwl();
       });

  vector<dbInst*> mirror_candidates;
  findMirrorCandidates(net_boxes, mirror_candidates);
  int64_t hpwl_before = hpwl();
  mirrorCandidates(mirror_candidates);
  double hpwl_after = hpwl();
  printf("HPWL before          %8.1f u\n", dbuToMicrons(hpwl_before));
  printf("HPWL after           %8.1f u\n", dbuToMicrons(hpwl_after));
  double hpwl_delta = (hpwl_after - hpwl_before) / hpwl_before * 100;
  printf("HPWL delta           %8.0f %%\n", hpwl_delta);
}

void
Opendp::findNetBoxes(NetBoxes &net_boxes)
{
  auto nets = block_->getNets();
  net_boxes.reserve(nets.size());
  for (dbNet *net : nets) {
    if (!isSupply(net)) {
      NetBox net_box(net);
      getBox(net, net_box.box);
      net_boxes.push_back(net_box);
    }
  }
}

void
Opendp::findMirrorCandidates(NetBoxes &net_boxes,
			     vector<dbInst*> &mirror_candidates)
{
  unordered_set<dbInst*> existing;
  // Find inst terms on the boundary of the net boxes.
  for (NetBox &net_box : net_boxes) {
    dbNet *net = net_box.net;
    Rect &box = net_box.box;
    for (dbITerm *iterm : net->getITerms()) {
      int x, y;
      if (iterm->getAvgXY(&x, &y)) {
	if (x == box.xMin() || x == box.xMax()
	    || y == box.yMin() || y == box.yMax()) {
	  dbInst *inst = iterm->getInst();
	  if (existing.find(inst) == existing.end()) {
	    mirror_candidates.push_back(inst);
	    existing.insert(inst);
	  }
	}
      }
    }
  }
}

void
Opendp::mirrorCandidates(vector<dbInst*> &mirror_candidates)
{
  for (dbInst *inst : mirror_candidates) {
    // Use hpwl of all nets connected to the instance terms
    // before/after to determine incremental change to total hpwl.
    int64_t hpwl_before = hpwl(inst);
    dbOrientType orient = inst->getOrient();
    dbOrientType orient_my = orientMirrorY(orient);
    inst->setLocationOrient(orient_my);
    int64_t hpwl_after = hpwl(inst);
    if (hpwl_after > hpwl_before)
      // Undo mirroring if hpwl is worse.
      inst->setLocationOrient(orient);
  }
}

// apply mirror about Y axis to orient
static dbOrientType
orientMirrorY(dbOrientType orient)
{
  switch (orient) {
  case dbOrientType::R0:
    return dbOrientType::MY;
  case dbOrientType::MX:
    return dbOrientType::R180;
  case dbOrientType::MY:
    return dbOrientType::R0;
  case dbOrientType::R180:
    return dbOrientType::MX;
  case dbOrientType::R90:
    return dbOrientType::MXR90;
  case dbOrientType::MXR90:
    return dbOrientType::R90;
  case dbOrientType::R270:
    return dbOrientType::MYR90;
  case dbOrientType::MYR90:
    return dbOrientType::R270;
  }
  // make lame gcc happy
  std::abort();
  return dbOrientType::R0;
}

int64_t
Opendp::hpwl(dbInst *inst)
{
  int64_t inst_hpwl = 0;
  for (dbITerm *iterm : inst->getITerms()) {
    dbNet *net = iterm->getNet();
    if (net)
      inst_hpwl += hpwl(net);
  }
  return inst_hpwl;
}

}  // namespace opendp
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 "OpenwireSimpleTest.h"

#include <activemq/util/CMSListener.h>
#include <activemq/core/ActiveMQConnection.h>
#include <activemq/exceptions/ActiveMQException.h>

#include <decaf/util/UUID.h>

using namespace std;
using namespace cms;
using namespace activemq;
using namespace activemq::core;
using namespace activemq::test;
using namespace activemq::test::openwire;
using namespace activemq::util;
using namespace activemq::exceptions;
using namespace decaf;
using namespace decaf::util;

////////////////////////////////////////////////////////////////////////////////
OpenwireSimpleTest::OpenwireSimpleTest() {
}

////////////////////////////////////////////////////////////////////////////////
OpenwireSimpleTest::~OpenwireSimpleTest() {
}

////////////////////////////////////////////////////////////////////////////////
void OpenwireSimpleTest::testWithZeroConsumerPrefetch() {

    try {

        cmsProvider->setTopic( false );
        cmsProvider->setDestinationName(
            UUID::randomUUID().toString() + "?consumer.prefetchSize=0" );

        cmsProvider->reconnectSession();

        // Create CMS Object for Comms
        cms::Session* session( cmsProvider->getSession() );
        cms::MessageConsumer* consumer = cmsProvider->getConsumer();
        cms::MessageProducer* producer = cmsProvider->getProducer();
        producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );

        auto_ptr<cms::TextMessage> txtMessage( session->createTextMessage( "TEST MESSAGE" ) );

        // Send some text messages
        producer->send( txtMessage.get() );

        auto_ptr<cms::Message> message( consumer->receive( 1000 ) );
        CPPUNIT_ASSERT( message.get() != NULL );
    }
    AMQ_CATCH_RETHROW( ActiveMQException )
    AMQ_CATCHALL_THROW( ActiveMQException )
}

////////////////////////////////////////////////////////////////////////////////
void OpenwireSimpleTest::testMapMessageSendToQueue() {

    try {

        cmsProvider->setTopic( false );
        cmsProvider->setDestinationName(
            UUID::randomUUID().toString() + "?consumer.prefetchSize=0" );

        cmsProvider->reconnectSession();

        // Create CMS Object for Comms
        cms::Session* session( cmsProvider->getSession() );
        cms::MessageConsumer* consumer = cmsProvider->getConsumer();
        cms::MessageProducer* producer = cmsProvider->getProducer();
        producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );

        unsigned char byteValue = 'A';
        char charValue = 'B';
        bool booleanValue = true;
        short shortValue = 2048;
        int intValue = 655369;
        long long longValue = 0xFFFFFFFF00000000ULL;
        float floatValue = 45.6545f;
        double doubleValue = 654564.654654;
        std::string stringValue = "The test string";

        auto_ptr<cms::MapMessage> mapMessage( session->createMapMessage() );

        mapMessage->setString( "stringKey", stringValue );
        mapMessage->setBoolean( "boolKey", booleanValue );
        mapMessage->setByte( "byteKey", byteValue );
        mapMessage->setChar( "charKey", charValue );
        mapMessage->setShort( "shortKey", shortValue );
        mapMessage->setInt( "intKey", intValue );
        mapMessage->setLong( "longKey", longValue );
        mapMessage->setFloat( "floatKey", floatValue );
        mapMessage->setDouble( "doubleKey", doubleValue );

        std::vector<unsigned char> bytes;
        bytes.push_back( 65 );
        bytes.push_back( 66 );
        bytes.push_back( 67 );
        bytes.push_back( 68 );
        bytes.push_back( 69 );
        mapMessage->setBytes( "bytesKey", bytes );

        // Send some text messages
        producer->send( mapMessage.get() );

        auto_ptr<cms::Message> message( consumer->receive( 2000 ) );
        CPPUNIT_ASSERT( message.get() != NULL );

        cms::MapMessage* recvMapMessage = dynamic_cast<MapMessage*>( message.get() );
        CPPUNIT_ASSERT( recvMapMessage != NULL );
        CPPUNIT_ASSERT( recvMapMessage->getString( "stringKey" ) == stringValue );
        CPPUNIT_ASSERT( recvMapMessage->getBoolean( "boolKey" ) == booleanValue );
        CPPUNIT_ASSERT( recvMapMessage->getByte( "byteKey" ) == byteValue );
        CPPUNIT_ASSERT( recvMapMessage->getChar( "charKey" ) == charValue );
        CPPUNIT_ASSERT( recvMapMessage->getShort( "shortKey" ) == shortValue );
        CPPUNIT_ASSERT( recvMapMessage->getInt( "intKey" ) == intValue );
        CPPUNIT_ASSERT( recvMapMessage->getLong( "longKey" ) == longValue );
        CPPUNIT_ASSERT( recvMapMessage->getFloat( "floatKey" ) == floatValue );
        CPPUNIT_ASSERT( recvMapMessage->getDouble( "doubleKey" ) == doubleValue );
        CPPUNIT_ASSERT( recvMapMessage->getBytes( "bytesKey" ) == bytes );
    }
    AMQ_CATCH_RETHROW( ActiveMQException )
    AMQ_CATCHALL_THROW( ActiveMQException )
}

////////////////////////////////////////////////////////////////////////////////
void OpenwireSimpleTest::testMapMessageSendToTopic() {

    try {

        // Create CMS Object for Comms
        cms::Session* session( cmsProvider->getSession() );
        cms::MessageConsumer* consumer = cmsProvider->getConsumer();
        cms::MessageProducer* producer = cmsProvider->getProducer();
        producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );

        unsigned char byteValue = 'A';
        char charValue = 'B';
        bool booleanValue = true;
        short shortValue = 2048;
        int intValue = 655369;
        long long longValue = 0xFFFFFFFF00000000ULL;
        float floatValue = 45.6545f;
        double doubleValue = 654564.654654;
        std::string stringValue = "The test string";

        auto_ptr<cms::MapMessage> mapMessage( session->createMapMessage() );

        mapMessage->setString( "stringKey", stringValue );
        mapMessage->setBoolean( "boolKey", booleanValue );
        mapMessage->setByte( "byteKey", byteValue );
        mapMessage->setChar( "charKey", charValue );
        mapMessage->setShort( "shortKey", shortValue );
        mapMessage->setInt( "intKey", intValue );
        mapMessage->setLong( "longKey", longValue );
        mapMessage->setFloat( "floatKey", floatValue );
        mapMessage->setDouble( "doubleKey", doubleValue );

        std::vector<unsigned char> bytes;
        bytes.push_back( 65 );
        bytes.push_back( 66 );
        bytes.push_back( 67 );
        bytes.push_back( 68 );
        bytes.push_back( 69 );
        mapMessage->setBytes( "bytesKey", bytes );

        // Send some text messages
        producer->send( mapMessage.get() );

        auto_ptr<cms::Message> message( consumer->receive( 2000 ) );
        CPPUNIT_ASSERT( message.get() != NULL );

        cms::MapMessage* recvMapMessage = dynamic_cast<MapMessage*>( message.get() );
        CPPUNIT_ASSERT( recvMapMessage != NULL );
        CPPUNIT_ASSERT( recvMapMessage->getString( "stringKey" ) == stringValue );
        CPPUNIT_ASSERT( recvMapMessage->getBoolean( "boolKey" ) == booleanValue );
        CPPUNIT_ASSERT( recvMapMessage->getByte( "byteKey" ) == byteValue );
        CPPUNIT_ASSERT( recvMapMessage->getChar( "charKey" ) == charValue );
        CPPUNIT_ASSERT( recvMapMessage->getShort( "shortKey" ) == shortValue );
        CPPUNIT_ASSERT( recvMapMessage->getInt( "intKey" ) == intValue );
        CPPUNIT_ASSERT( recvMapMessage->getLong( "longKey" ) == longValue );
        CPPUNIT_ASSERT( recvMapMessage->getFloat( "floatKey" ) == floatValue );
        CPPUNIT_ASSERT( recvMapMessage->getDouble( "doubleKey" ) == doubleValue );
        CPPUNIT_ASSERT( recvMapMessage->getBytes( "bytesKey" ) == bytes );
    }
    AMQ_CATCH_RETHROW( ActiveMQException )
    AMQ_CATCHALL_THROW( ActiveMQException )
}

////////////////////////////////////////////////////////////////////////////////
void OpenwireSimpleTest::testDestroyDestination() {

    try {

        cmsProvider->setDestinationName( "testDestroyDestination" );
        cmsProvider->reconnectSession();

        // Create CMS Object for Comms
        cms::Session* session( cmsProvider->getSession() );
        cms::MessageConsumer* consumer = cmsProvider->getConsumer();
        cms::MessageProducer* producer = cmsProvider->getProducer();
        producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );

        auto_ptr<cms::TextMessage> txtMessage( session->createTextMessage( "TEST MESSAGE" ) );

        // Send some text messages
        producer->send( txtMessage.get() );

        auto_ptr<cms::Message> message( consumer->receive( 1000 ) );
        CPPUNIT_ASSERT( message.get() != NULL );

        ActiveMQConnection* connection =
            dynamic_cast<ActiveMQConnection*>( cmsProvider->getConnection() );

        CPPUNIT_ASSERT( connection != NULL );

        try{
            connection->destroyDestination( cmsProvider->getDestination() );
            CPPUNIT_ASSERT_MESSAGE( "Destination Should be in use.", false );
        } catch( ActiveMQException& ex ) {
        }

        cmsProvider->reconnectSession();

        connection->destroyDestination( cmsProvider->getDestination() );

    } catch( ActiveMQException& ex ) {
        ex.printStackTrace();
        CPPUNIT_ASSERT_MESSAGE( "CAUGHT EXCEPTION", false );
    } AMQ_CATCHALL_THROW( ActiveMQException )
}

////////////////////////////////////////////////////////////////////////////////
void OpenwireSimpleTest::tesstStreamMessage() {

    try{

        // Create CMS Object for Comms
        cms::Session* session( cmsProvider->getSession() );
        cms::MessageConsumer* consumer = cmsProvider->getConsumer();
        cms::MessageProducer* producer = cmsProvider->getProducer();
        producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );

        unsigned char byteValue = 'A';
        char charValue = 'B';
        bool booleanValue = true;
        short shortValue = 2048;
        int intValue = 655369;
        long long longValue = 0xFFFFFFFF00000000ULL;
        float floatValue = 45.6545f;
        double doubleValue = 654564.654654;
        std::string stringValue = "The test string";

        auto_ptr<cms::StreamMessage> streamMessage( session->createStreamMessage() );

        streamMessage->writeString( stringValue );
        streamMessage->writeBoolean( booleanValue );
        streamMessage->writeByte( byteValue );
        streamMessage->writeChar( charValue );
        streamMessage->writeShort( shortValue );
        streamMessage->writeInt( intValue );
        streamMessage->writeLong( longValue );
        streamMessage->writeFloat( floatValue );
        streamMessage->writeDouble( doubleValue );

        std::vector<unsigned char> bytes;
        std::vector<unsigned char> readBytes;
        bytes.push_back( 65 );
        bytes.push_back( 66 );
        bytes.push_back( 67 );
        bytes.push_back( 68 );
        bytes.push_back( 69 );
        streamMessage->writeBytes( bytes );

        // Send some text messages
        producer->send( streamMessage.get() );

        auto_ptr<cms::Message> message( consumer->receive( 2000 ) );
        CPPUNIT_ASSERT( message.get() != NULL );

        cms::StreamMessage* rcvStreamMessage = dynamic_cast<StreamMessage*>( message.get() );
        CPPUNIT_ASSERT( rcvStreamMessage != NULL );
        CPPUNIT_ASSERT( rcvStreamMessage->readString() == stringValue );
        CPPUNIT_ASSERT( rcvStreamMessage->readBoolean() == booleanValue );
        CPPUNIT_ASSERT( rcvStreamMessage->readByte() == byteValue );
        CPPUNIT_ASSERT( rcvStreamMessage->readChar() == charValue );
        CPPUNIT_ASSERT( rcvStreamMessage->readShort() == shortValue );
        CPPUNIT_ASSERT( rcvStreamMessage->readInt() == intValue );
        CPPUNIT_ASSERT( rcvStreamMessage->readLong() == longValue );
        CPPUNIT_ASSERT( rcvStreamMessage->readFloat() == floatValue );
        CPPUNIT_ASSERT( rcvStreamMessage->readDouble() == doubleValue );
        CPPUNIT_ASSERT( rcvStreamMessage->readBytes( readBytes ) == bytes.size() );
    }
    AMQ_CATCH_RETHROW( ActiveMQException )
    AMQ_CATCHALL_THROW( ActiveMQException )
}
<commit_msg>https://issues.apache.org/activemq/browse/AMQCPP-265<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 "OpenwireSimpleTest.h"

#include <activemq/util/CMSListener.h>
#include <activemq/core/ActiveMQConnection.h>
#include <activemq/exceptions/ActiveMQException.h>

#include <decaf/util/UUID.h>

using namespace std;
using namespace cms;
using namespace activemq;
using namespace activemq::core;
using namespace activemq::test;
using namespace activemq::test::openwire;
using namespace activemq::util;
using namespace activemq::exceptions;
using namespace decaf;
using namespace decaf::util;

////////////////////////////////////////////////////////////////////////////////
OpenwireSimpleTest::OpenwireSimpleTest() {
}

////////////////////////////////////////////////////////////////////////////////
OpenwireSimpleTest::~OpenwireSimpleTest() {
}

////////////////////////////////////////////////////////////////////////////////
void OpenwireSimpleTest::testWithZeroConsumerPrefetch() {

    try {

        cmsProvider->setTopic( false );
        cmsProvider->setDestinationName(
            UUID::randomUUID().toString() + "?consumer.prefetchSize=0" );

        cmsProvider->reconnectSession();

        // Create CMS Object for Comms
        cms::Session* session( cmsProvider->getSession() );
        cms::MessageConsumer* consumer = cmsProvider->getConsumer();
        cms::MessageProducer* producer = cmsProvider->getProducer();
        producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );

        auto_ptr<cms::TextMessage> txtMessage( session->createTextMessage( "TEST MESSAGE" ) );

        // Send some text messages
        producer->send( txtMessage.get() );

        auto_ptr<cms::Message> message( consumer->receive( 1000 ) );
        CPPUNIT_ASSERT( message.get() != NULL );
    }
    AMQ_CATCH_RETHROW( ActiveMQException )
    AMQ_CATCHALL_THROW( ActiveMQException )
}

////////////////////////////////////////////////////////////////////////////////
void OpenwireSimpleTest::testMapMessageSendToQueue() {

    try {

        cmsProvider->setTopic( false );
        cmsProvider->setDestinationName(
            UUID::randomUUID().toString() + "?consumer.prefetchSize=0" );

        cmsProvider->reconnectSession();

        // Create CMS Object for Comms
        cms::Session* session( cmsProvider->getSession() );
        cms::MessageConsumer* consumer = cmsProvider->getConsumer();
        cms::MessageProducer* producer = cmsProvider->getProducer();
        producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );

        unsigned char byteValue = 'A';
        char charValue = 'B';
        bool booleanValue = true;
        short shortValue = 2048;
        int intValue = 655369;
        long long longValue = 0xFFFFFFFF00000000ULL;
        float floatValue = 45.6545f;
        double doubleValue = 654564.654654;
        std::string stringValue = "The test string";

        auto_ptr<cms::MapMessage> mapMessage( session->createMapMessage() );

        mapMessage->setString( "stringKey", stringValue );
        mapMessage->setBoolean( "boolKey", booleanValue );
        mapMessage->setByte( "byteKey", byteValue );
        mapMessage->setChar( "charKey", charValue );
        mapMessage->setShort( "shortKey", shortValue );
        mapMessage->setInt( "intKey", intValue );
        mapMessage->setLong( "longKey", longValue );
        mapMessage->setFloat( "floatKey", floatValue );
        mapMessage->setDouble( "doubleKey", doubleValue );

        std::vector<unsigned char> bytes;
        bytes.push_back( 65 );
        bytes.push_back( 66 );
        bytes.push_back( 67 );
        bytes.push_back( 68 );
        bytes.push_back( 69 );
        mapMessage->setBytes( "bytesKey", bytes );

        // Send some text messages
        producer->send( mapMessage.get() );

        auto_ptr<cms::Message> message( consumer->receive( 2000 ) );
        CPPUNIT_ASSERT( message.get() != NULL );

        cms::MapMessage* recvMapMessage = dynamic_cast<MapMessage*>( message.get() );
        CPPUNIT_ASSERT( recvMapMessage != NULL );
        CPPUNIT_ASSERT( recvMapMessage->getString( "stringKey" ) == stringValue );
        CPPUNIT_ASSERT( recvMapMessage->getBoolean( "boolKey" ) == booleanValue );
        CPPUNIT_ASSERT( recvMapMessage->getByte( "byteKey" ) == byteValue );
        CPPUNIT_ASSERT( recvMapMessage->getChar( "charKey" ) == charValue );
        CPPUNIT_ASSERT( recvMapMessage->getShort( "shortKey" ) == shortValue );
        CPPUNIT_ASSERT( recvMapMessage->getInt( "intKey" ) == intValue );
        CPPUNIT_ASSERT( recvMapMessage->getLong( "longKey" ) == longValue );
        CPPUNIT_ASSERT( recvMapMessage->getFloat( "floatKey" ) == floatValue );
        CPPUNIT_ASSERT( recvMapMessage->getDouble( "doubleKey" ) == doubleValue );
        CPPUNIT_ASSERT( recvMapMessage->getBytes( "bytesKey" ) == bytes );
    }
    AMQ_CATCH_RETHROW( ActiveMQException )
    AMQ_CATCHALL_THROW( ActiveMQException )
}

////////////////////////////////////////////////////////////////////////////////
void OpenwireSimpleTest::testMapMessageSendToTopic() {

    try {

        // Create CMS Object for Comms
        cms::Session* session( cmsProvider->getSession() );
        cms::MessageConsumer* consumer = cmsProvider->getConsumer();
        cms::MessageProducer* producer = cmsProvider->getProducer();
        producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );

        unsigned char byteValue = 'A';
        char charValue = 'B';
        bool booleanValue = true;
        short shortValue = 2048;
        int intValue = 655369;
        long long longValue = 0xFFFFFFFF00000000ULL;
        float floatValue = 45.6545f;
        double doubleValue = 654564.654654;
        std::string stringValue = "The test string";

        auto_ptr<cms::MapMessage> mapMessage( session->createMapMessage() );

        mapMessage->setString( "stringKey", stringValue );
        mapMessage->setBoolean( "boolKey", booleanValue );
        mapMessage->setByte( "byteKey", byteValue );
        mapMessage->setChar( "charKey", charValue );
        mapMessage->setShort( "shortKey", shortValue );
        mapMessage->setInt( "intKey", intValue );
        mapMessage->setLong( "longKey", longValue );
        mapMessage->setFloat( "floatKey", floatValue );
        mapMessage->setDouble( "doubleKey", doubleValue );

        std::vector<unsigned char> bytes;
        bytes.push_back( 65 );
        bytes.push_back( 66 );
        bytes.push_back( 67 );
        bytes.push_back( 68 );
        bytes.push_back( 69 );
        mapMessage->setBytes( "bytesKey", bytes );

        // Send some text messages
        producer->send( mapMessage.get() );

        auto_ptr<cms::Message> message( consumer->receive( 2000 ) );
        CPPUNIT_ASSERT( message.get() != NULL );

        cms::MapMessage* recvMapMessage = dynamic_cast<MapMessage*>( message.get() );
        CPPUNIT_ASSERT( recvMapMessage != NULL );
        CPPUNIT_ASSERT( recvMapMessage->getString( "stringKey" ) == stringValue );
        CPPUNIT_ASSERT( recvMapMessage->getBoolean( "boolKey" ) == booleanValue );
        CPPUNIT_ASSERT( recvMapMessage->getByte( "byteKey" ) == byteValue );
        CPPUNIT_ASSERT( recvMapMessage->getChar( "charKey" ) == charValue );
        CPPUNIT_ASSERT( recvMapMessage->getShort( "shortKey" ) == shortValue );
        CPPUNIT_ASSERT( recvMapMessage->getInt( "intKey" ) == intValue );
        CPPUNIT_ASSERT( recvMapMessage->getLong( "longKey" ) == longValue );
        CPPUNIT_ASSERT( recvMapMessage->getFloat( "floatKey" ) == floatValue );
        CPPUNIT_ASSERT( recvMapMessage->getDouble( "doubleKey" ) == doubleValue );
        CPPUNIT_ASSERT( recvMapMessage->getBytes( "bytesKey" ) == bytes );
    }
    AMQ_CATCH_RETHROW( ActiveMQException )
    AMQ_CATCHALL_THROW( ActiveMQException )
}

////////////////////////////////////////////////////////////////////////////////
void OpenwireSimpleTest::testDestroyDestination() {

    try {

        cmsProvider->setDestinationName( "testDestroyDestination" );
        cmsProvider->reconnectSession();

        // Create CMS Object for Comms
        cms::Session* session( cmsProvider->getSession() );
        cms::MessageConsumer* consumer = cmsProvider->getConsumer();
        cms::MessageProducer* producer = cmsProvider->getProducer();
        producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );

        auto_ptr<cms::TextMessage> txtMessage( session->createTextMessage( "TEST MESSAGE" ) );

        // Send some text messages
        producer->send( txtMessage.get() );

        auto_ptr<cms::Message> message( consumer->receive( 1000 ) );
        CPPUNIT_ASSERT( message.get() != NULL );

        ActiveMQConnection* connection =
            dynamic_cast<ActiveMQConnection*>( cmsProvider->getConnection() );

        CPPUNIT_ASSERT( connection != NULL );

        try{
            connection->destroyDestination( cmsProvider->getDestination() );
            CPPUNIT_ASSERT_MESSAGE( "Destination Should be in use.", false );
        } catch( ActiveMQException& ex ) {
        }

        cmsProvider->reconnectSession();

        connection->destroyDestination( cmsProvider->getDestination() );

    } catch( ActiveMQException& ex ) {
        ex.printStackTrace();
        CPPUNIT_ASSERT_MESSAGE( "CAUGHT EXCEPTION", false );
    } AMQ_CATCHALL_THROW( ActiveMQException )
}

////////////////////////////////////////////////////////////////////////////////
void OpenwireSimpleTest::tesstStreamMessage() {

    // Create CMS Object for Comms
    cms::Session* session( cmsProvider->getSession() );
    cms::MessageConsumer* consumer = cmsProvider->getConsumer();
    cms::MessageProducer* producer = cmsProvider->getProducer();
    producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );

    unsigned char byteValue = 'A';
    char charValue = 'B';
    bool booleanValue = true;
    short shortValue = 2048;
    int intValue = 655369;
    long long longValue = 0xFFFFFFFF00000000ULL;
    float floatValue = 45.6545f;
    double doubleValue = 654564.654654;
    std::string stringValue = "The test string";

    auto_ptr<cms::StreamMessage> streamMessage( session->createStreamMessage() );

    streamMessage->writeString( stringValue );
    streamMessage->writeBoolean( booleanValue );
    streamMessage->writeByte( byteValue );
    streamMessage->writeChar( charValue );
    streamMessage->writeShort( shortValue );
    streamMessage->writeInt( intValue );
    streamMessage->writeLong( longValue );
    streamMessage->writeFloat( floatValue );
    streamMessage->writeDouble( doubleValue );

    std::vector<unsigned char> bytes;
    std::vector<unsigned char> readBytes( 100 );
    bytes.push_back( 65 );
    bytes.push_back( 66 );
    bytes.push_back( 67 );
    bytes.push_back( 68 );
    bytes.push_back( 69 );
    streamMessage->writeBytes( bytes );

    // Send some text messages
    producer->send( streamMessage.get() );

    auto_ptr<cms::Message> message( consumer->receive( 2000 ) );
    CPPUNIT_ASSERT( message.get() != NULL );

    cms::StreamMessage* rcvStreamMessage = dynamic_cast<StreamMessage*>( message.get() );
    CPPUNIT_ASSERT( rcvStreamMessage != NULL );
    CPPUNIT_ASSERT( rcvStreamMessage->readString() == stringValue );
    CPPUNIT_ASSERT( rcvStreamMessage->readBoolean() == booleanValue );
    CPPUNIT_ASSERT( rcvStreamMessage->readByte() == byteValue );
    CPPUNIT_ASSERT( rcvStreamMessage->readChar() == charValue );
    CPPUNIT_ASSERT( rcvStreamMessage->readShort() == shortValue );
    CPPUNIT_ASSERT( rcvStreamMessage->readInt() == intValue );
    CPPUNIT_ASSERT( rcvStreamMessage->readLong() == longValue );
    CPPUNIT_ASSERT( rcvStreamMessage->readFloat() == floatValue );
    CPPUNIT_ASSERT( rcvStreamMessage->readDouble() == doubleValue );
    CPPUNIT_ASSERT( rcvStreamMessage->readBytes( readBytes ) == bytes.size() );
}
<|endoftext|>


<commit_before>/**
 * @file llmaterial.cpp
 * @brief Material definition
 *
 * $LicenseInfo:firstyear=2006&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "linden_common.h"

#include "llmaterial.h"

/**
 * Materials cap parameters
 */
#define MATERIALS_CAP_NORMAL_MAP_FIELD            "NormMap"
#define MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD   "NormOffsetX"
#define MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD   "NormOffsetY"
#define MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD   "NormRepeatX"
#define MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD   "NormRepeatY"
#define MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD   "NormRotation"

#define MATERIALS_CAP_SPECULAR_MAP_FIELD          "SpecMap"
#define MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD "SpecOffsetX"
#define MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD "SpecOffsetY"
#define MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD "SpecRepeatX"
#define MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD "SpecRepeatY"
#define MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD "SpecRotation"

#define MATERIALS_CAP_SPECULAR_COLOR_FIELD        "SpecColor"
#define MATERIALS_CAP_SPECULAR_EXP_FIELD          "SpecExp"
#define MATERIALS_CAP_ENV_INTENSITY_FIELD         "EnvIntensity"
#define MATERIALS_CAP_ALPHA_MASK_CUTOFF_FIELD     "AlphaMaskCutoff"
#define MATERIALS_CAP_DIFFUSE_ALPHA_MODE_FIELD    "DiffuseAlphaMode"

/**
 * Materials constants
 */

const LLColor4U MATERIALS_DEFAULT_SPECULAR_COLOR = LLColor4U(255, 255, 255, 255);
const U8 MATERIALS_DEFAULT_SPECULAR_EXP          = 128;
const U8 MATERIALS_DEFAULT_ENV_INTENSITY         = 128;
const U8 MATERIALS_DEFAULT_DIFFUSE_ALPHA_MODE    = 0;
const U8 MATERIALS_DEFAULT_ALPHA_MASK_CUTOFF     = 128;
const F32 MATERIALS_MULTIPLIER                   = 10000.f;

/**
 * Helper functions
 */

template<typename T> T getMaterialField(const LLSD& data, const std::string& field, const LLSD::Type field_type)
{
	if ( (data.has(field)) && (field_type == data[field].type()) )
	{
		return (T)data[field];
	}
	llerrs << "Missing or mistyped field '" << field << "' in material definition" << llendl;
	return (T)LLSD();
}

/**
 * LLMaterial class
 */

const LLMaterial LLMaterial::null;

LLMaterial::LLMaterial()
	: mSpecularLightColor(MATERIALS_DEFAULT_SPECULAR_COLOR)
	, mSpecularLightExponent(MATERIALS_DEFAULT_SPECULAR_EXP)
	, mEnvironmentIntensity(MATERIALS_DEFAULT_ENV_INTENSITY)
	, mDiffuseAlphaMode(MATERIALS_DEFAULT_DIFFUSE_ALPHA_MODE)
	, mAlphaMaskCutoff(MATERIALS_DEFAULT_ALPHA_MASK_CUTOFF)
{
}

LLMaterial::LLMaterial(const LLSD& material_data)
{
	fromLLSD(material_data);
}

LLSD LLMaterial::asLLSD() const
{
	LLSD material_data;

	material_data[MATERIALS_CAP_NORMAL_MAP_FIELD] = mNormalID;
	material_data[MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD] = llround(mNormalOffsetX * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD] = llround(mNormalOffsetY * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD] = llround(mNormalRepeatX * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD] = llround(mNormalRepeatY * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD] = llround(mNormalRotation * MATERIALS_MULTIPLIER);

	material_data[MATERIALS_CAP_SPECULAR_MAP_FIELD] = mSpecularID;
	material_data[MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD] = llround(mSpecularOffsetX * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD] = llround(mSpecularOffsetY * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD] = llround(mSpecularRepeatX * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD] = llround(mSpecularRepeatY * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD] = llround(mSpecularRotation * MATERIALS_MULTIPLIER);

	material_data[MATERIALS_CAP_SPECULAR_COLOR_FIELD]     = mSpecularLightColor.getValue();
	material_data[MATERIALS_CAP_SPECULAR_EXP_FIELD]       = mSpecularLightExponent;
	material_data[MATERIALS_CAP_ENV_INTENSITY_FIELD]      = mEnvironmentIntensity;
	material_data[MATERIALS_CAP_DIFFUSE_ALPHA_MODE_FIELD] = mDiffuseAlphaMode;
	material_data[MATERIALS_CAP_ALPHA_MASK_CUTOFF_FIELD]  = mAlphaMaskCutoff;

	return material_data;
}

void LLMaterial::fromLLSD(const LLSD& material_data)
{
	mNormalID = getMaterialField<LLSD::UUID>(material_data, MATERIALS_CAP_NORMAL_MAP_FIELD, LLSD::TypeUUID);
	mNormalOffsetX  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mNormalOffsetY  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mNormalRepeatX  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mNormalRepeatY  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mNormalRotation = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;

	mSpecularID = getMaterialField<LLSD::UUID>(material_data, MATERIALS_CAP_SPECULAR_MAP_FIELD, LLSD::TypeUUID);
	mSpecularOffsetX  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mSpecularOffsetY  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mSpecularRepeatX  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mSpecularRepeatY  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mSpecularRotation = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;

	mSpecularLightColor.setValue(getMaterialField<LLSD>(material_data, MATERIALS_CAP_SPECULAR_COLOR_FIELD, LLSD::TypeArray));
	mSpecularLightExponent = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_EXP_FIELD,       LLSD::TypeInteger);
	mEnvironmentIntensity  = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_ENV_INTENSITY_FIELD,      LLSD::TypeInteger);
	mDiffuseAlphaMode      = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_DIFFUSE_ALPHA_MODE_FIELD, LLSD::TypeInteger);
	mAlphaMaskCutoff       = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_ALPHA_MASK_CUTOFF_FIELD,  LLSD::TypeInteger);
}

bool LLMaterial::isNull() const
{
	// *TODO: find a better way of defining a 'null' material?
	return
		(mNormalID.isNull()) && (.0f == mNormalOffsetX) && (.0f == mNormalOffsetY) && (.0f == mNormalRepeatX) && (.0f == mNormalRepeatY) &&
		(mSpecularID.isNull()) && (.0f == mSpecularOffsetX) && (.0f == mSpecularOffsetY) && (.0f == mSpecularRepeatX) && (.0f == mSpecularRepeatY);
}
<commit_msg>Bypass GCC template stumble by specializing getMaterialField for LLUUID<commit_after>/**
 * @file llmaterial.cpp
 * @brief Material definition
 *
 * $LicenseInfo:firstyear=2006&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "linden_common.h"

#include "llmaterial.h"

/**
 * Materials cap parameters
 */
#define MATERIALS_CAP_NORMAL_MAP_FIELD            "NormMap"
#define MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD   "NormOffsetX"
#define MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD   "NormOffsetY"
#define MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD   "NormRepeatX"
#define MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD   "NormRepeatY"
#define MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD   "NormRotation"

#define MATERIALS_CAP_SPECULAR_MAP_FIELD          "SpecMap"
#define MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD "SpecOffsetX"
#define MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD "SpecOffsetY"
#define MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD "SpecRepeatX"
#define MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD "SpecRepeatY"
#define MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD "SpecRotation"

#define MATERIALS_CAP_SPECULAR_COLOR_FIELD        "SpecColor"
#define MATERIALS_CAP_SPECULAR_EXP_FIELD          "SpecExp"
#define MATERIALS_CAP_ENV_INTENSITY_FIELD         "EnvIntensity"
#define MATERIALS_CAP_ALPHA_MASK_CUTOFF_FIELD     "AlphaMaskCutoff"
#define MATERIALS_CAP_DIFFUSE_ALPHA_MODE_FIELD    "DiffuseAlphaMode"

/**
 * Materials constants
 */

const LLColor4U MATERIALS_DEFAULT_SPECULAR_COLOR = LLColor4U(255, 255, 255, 255);
const U8 MATERIALS_DEFAULT_SPECULAR_EXP          = 128;
const U8 MATERIALS_DEFAULT_ENV_INTENSITY         = 128;
const U8 MATERIALS_DEFAULT_DIFFUSE_ALPHA_MODE    = 0;
const U8 MATERIALS_DEFAULT_ALPHA_MASK_CUTOFF     = 128;
const F32 MATERIALS_MULTIPLIER                   = 10000.f;

/**
 * Helper functions
 */

template<typename T> T getMaterialField(const LLSD& data, const std::string& field, const LLSD::Type field_type)
{
	if ( (data.has(field)) && (field_type == data[field].type()) )
	{
		return (T)data[field];
	}
	llerrs << "Missing or mistyped field '" << field << "' in material definition" << llendl;
	return (T)LLSD();
}

// GCC didn't like the generic form above for some reason
template<> LLUUID getMaterialField(const LLSD& data, const std::string& field, const LLSD::Type field_type)
{
	if ( (data.has(field)) && (field_type == data[field].type()) )
	{
		return data[field].asUUID();
	}
	llerrs << "Missing or mistyped field '" << field << "' in material definition" << llendl;
	return LLUUID::null;
}

/**
 * LLMaterial class
 */

const LLMaterial LLMaterial::null;

LLMaterial::LLMaterial()
	: mSpecularLightColor(MATERIALS_DEFAULT_SPECULAR_COLOR)
	, mSpecularLightExponent(MATERIALS_DEFAULT_SPECULAR_EXP)
	, mEnvironmentIntensity(MATERIALS_DEFAULT_ENV_INTENSITY)
	, mDiffuseAlphaMode(MATERIALS_DEFAULT_DIFFUSE_ALPHA_MODE)
	, mAlphaMaskCutoff(MATERIALS_DEFAULT_ALPHA_MASK_CUTOFF)
{
}

LLMaterial::LLMaterial(const LLSD& material_data)
{
	fromLLSD(material_data);
}

LLSD LLMaterial::asLLSD() const
{
	LLSD material_data;

	material_data[MATERIALS_CAP_NORMAL_MAP_FIELD] = mNormalID;
	material_data[MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD] = llround(mNormalOffsetX * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD] = llround(mNormalOffsetY * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD] = llround(mNormalRepeatX * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD] = llround(mNormalRepeatY * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD] = llround(mNormalRotation * MATERIALS_MULTIPLIER);

	material_data[MATERIALS_CAP_SPECULAR_MAP_FIELD] = mSpecularID;
	material_data[MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD] = llround(mSpecularOffsetX * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD] = llround(mSpecularOffsetY * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD] = llround(mSpecularRepeatX * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD] = llround(mSpecularRepeatY * MATERIALS_MULTIPLIER);
	material_data[MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD] = llround(mSpecularRotation * MATERIALS_MULTIPLIER);

	material_data[MATERIALS_CAP_SPECULAR_COLOR_FIELD]     = mSpecularLightColor.getValue();
	material_data[MATERIALS_CAP_SPECULAR_EXP_FIELD]       = mSpecularLightExponent;
	material_data[MATERIALS_CAP_ENV_INTENSITY_FIELD]      = mEnvironmentIntensity;
	material_data[MATERIALS_CAP_DIFFUSE_ALPHA_MODE_FIELD] = mDiffuseAlphaMode;
	material_data[MATERIALS_CAP_ALPHA_MASK_CUTOFF_FIELD]  = mAlphaMaskCutoff;

	return material_data;
}

void LLMaterial::fromLLSD(const LLSD& material_data)
{
	mNormalID = getMaterialField<LLSD::UUID>(material_data, MATERIALS_CAP_NORMAL_MAP_FIELD, LLSD::TypeUUID);
	mNormalOffsetX  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mNormalOffsetY  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mNormalRepeatX  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mNormalRepeatY  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mNormalRotation = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;

	mSpecularID = getMaterialField<LLSD::UUID>(material_data, MATERIALS_CAP_SPECULAR_MAP_FIELD, LLSD::TypeUUID);
	mSpecularOffsetX  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mSpecularOffsetY  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mSpecularRepeatX  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mSpecularRepeatY  = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;
	mSpecularRotation = (F32)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD, LLSD::TypeInteger) / MATERIALS_MULTIPLIER;

	mSpecularLightColor.setValue(getMaterialField<LLSD>(material_data, MATERIALS_CAP_SPECULAR_COLOR_FIELD, LLSD::TypeArray));
	mSpecularLightExponent = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_EXP_FIELD,       LLSD::TypeInteger);
	mEnvironmentIntensity  = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_ENV_INTENSITY_FIELD,      LLSD::TypeInteger);
	mDiffuseAlphaMode      = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_DIFFUSE_ALPHA_MODE_FIELD, LLSD::TypeInteger);
	mAlphaMaskCutoff       = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_ALPHA_MASK_CUTOFF_FIELD,  LLSD::TypeInteger);
}

bool LLMaterial::isNull() const
{
	// *TODO: find a better way of defining a 'null' material?
	return
		(mNormalID.isNull()) && (.0f == mNormalOffsetX) && (.0f == mNormalOffsetY) && (.0f == mNormalRepeatX) && (.0f == mNormalRepeatY) &&
		(mSpecularID.isNull()) && (.0f == mSpecularOffsetX) && (.0f == mSpecularOffsetY) && (.0f == mSpecularRepeatX) && (.0f == mSpecularRepeatY);
}
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * 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.
 *
 * 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 HOLDER 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.
 *
 * \file EventHandler.hxx
 * Interface for handling NMRAnet events.
 *
 * @author Balazs Racz
 * @date 29 September 2013
 */

#ifndef _OPENLCB_EVENTHANDLER_HXX_
#define _OPENLCB_EVENTHANDLER_HXX_

#include <stdint.h>

#include "executor/Notifiable.hxx"
#include "utils/AsyncMutex.hxx"
#include "utils/macros.h"
#include "utils/Singleton.hxx"
#include "openlcb/WriteHelper.hxx"

namespace openlcb
{

typedef uint64_t EventId;
class Node;
class EventHandler;

/*enum EventMask {
  EVENT_EXACT_MASK = 1,
  EVENT_ALL_MASK = 0xffffffffffffffffULL
  };*/

enum TestingEnum
{
    FOR_TESTING
};

/// Shared notification structure that is assembled for each incoming
/// event-related message, and passed around to all event handlers.
struct EventReport
{
    /// The event ID from the incoming message.
    EventId event;
    /// Specifies the mask in case the request is for an event range. The low
    /// bits are set to one, the high bits are set to zero. Ranges of size 1
    /// have
    /// mask==0, a range that covers all events has mask==0xffff...f.
    EventId mask;
    /// Information about the sender of the incoming event-related OpenLCB
    /// message. It is not specified whether the node_id or the alias is
    /// specified, but they are not both zero.
    NodeHandle src_node;
    /// nullptr for global messages; points to the specific virtual node for
    /// addressed events identify message.
    Node *dst_node;
    /// For producer/consumer identified messages, specifies the state of the
    /// producer/consumer as the sender of the message
    /// (valid/invalid/unknown/reserved).
    EventState state;

    /// These allow event handlers to produce up to four messages per
    /// invocation. They are always available at the entry to an event handler
    /// function.
    template <int N> WriteHelper *event_write_helper()
    {
        static_assert(1 <= N && N <= 4, "WriteHelper out of range.");
        return write_helpers + (N - 1);
    }

    /// Public constructor for use in tests only.
    EventReport(TestingEnum)
    {
    }

private:
    /// Constrained access to the constructors. We do this because the
    /// EventReport structure is pretty expensive due to the statically
    /// allocated memory of the write helpers. Only the EventIteratorFlow
    /// should have objects of this type.
    EventReport() {}
    friend class EventIteratorFlow;
    friend class DecoderRangeTest;

    /// Static objects usable by all event handler implementations.
    WriteHelper write_helpers[4];
};

/// Structure used in registering event handlers.
class EventRegistryEntry
{
public:
    /// Stores the event ID or beginning of range for which to register the
    /// given handler.
    EventId event;
    /// Pointer to the handler.
    EventHandler *handler;
    /// Opaque user argument. The event handlers may use this to store
    /// arbitrary data.
    uint32_t user_arg;
    EventRegistryEntry(EventHandler *_handler, EventId _event)
        : event(_event)
        , handler(_handler)
        , user_arg(0)
    {
    }
    EventRegistryEntry(EventHandler *_handler, EventId _event,
                       unsigned _user_arg)
        : event(_event)
        , handler(_handler)
        , user_arg(_user_arg)
    {
    }
};

/// Abstract base class for all event handlers. Instances of this class can
/// get registered with the event service to receive notifications of incoming
/// event messages from the bus.
class EventHandler
{
public:
    using EventReport = openlcb::EventReport;
    using EventRegistryEntry = openlcb::EventRegistryEntry;
    using EventId = openlcb::EventId;

    virtual ~EventHandler()
    {
    }

    /// Called on incoming EventReport messages. @param event stores
    /// information about the incoming message. Filled: src_node, event. Mask
    /// is always 1 (filled in). state is not filled in. @param registry_entry
    /// gives the registry entry for which the current handler is being
    /// called. @param done must be notified when the processing is done.
    virtual void handle_event_report(const EventRegistryEntry &registry_entry,
                                   EventReport *event,
                                   BarrierNotifiable *done) = 0;

    /// Called on another node sending ConsumerIdentified for this event.
    /// @param event stores information about the incoming message. Filled:
    /// event_id, mask=1, src_node, state.  @param registry_entry gives the
    /// registry entry for which the current handler is being called. @param
    /// done must be notified when the processing is done.
    virtual void
    handle_consumer_identified(const EventRegistryEntry &registry_entry,
                             EventReport *event, BarrierNotifiable *done)
    {
        done->notify();
    };

    /// Called on another node sending ConsumerRangeIdentified. @param event
    /// stores information about the incoming message. Filled: event id, mask
    /// (!= 1), src_node. Not filled: state.  @param registry_entry gives the
    /// registry entry for which the current handler is being called. @param
    /// done must be notified when the processing is done.
    virtual void
    handle_consumer_range_identified(const EventRegistryEntry &registry_entry,
                                  EventReport *event, BarrierNotifiable *done)
    {
        done->notify();
    }

    /// Called on another node sending ProducerIdentified for this event.
    /// @param event stores information about the incoming message. Filled:
    /// event_id, mask=1, src_node, state.  @param registry_entry gives the
    /// registry entry for which the current handler is being called. @param
    /// user_arg is an opaque argument passed in from the registration. @param
    /// done must be notified when the processing is done.
    virtual void
    handle_producer_identified(const EventRegistryEntry &registry_entry,
                             EventReport *event, BarrierNotifiable *done)
    {
        done->notify();
    }

    /// Called on another node sending ProducerRangeIdentified for this
    /// event. @param event stores information about the incoming
    /// message. Filled: event id, mask (!= 1), src_node. Not filled: state.
    /// @param registry_entry gives the registry entry for which the current
    /// handler is being called. @param done must be notified when the
    /// processing is done.
    virtual void
    handle_producer_range_identified(const EventRegistryEntry &registry_entry,
                                  EventReport *event, BarrierNotifiable *done)
    {
        done->notify();
    }

    /// Called on the need of sending out identification messages. @param event
    /// is NULL. This happens on startup, or when a global or addressed
    /// IdentifyGlobal message arrives. Might have destination node id! @param
    /// registry_entry gives the registry entry for which the current handler
    /// is being called. @param done must be notified when the processing is
    /// done.
    virtual void handle_identify_global(const EventRegistryEntry &registry_entry,
                                      EventReport *event,
                                      BarrierNotifiable *done) = 0;

    /// Called on another node sending IdentifyConsumer. @param event stores
    /// information about the incoming message. Filled: src_node, event,
    /// mask=1. Not filled: state. @param registry_entry gives the registry
    /// entry for which the current handler is being called. @param done must
    /// be notified when the processing is done.
    virtual void
    handle_identify_consumer(const EventRegistryEntry &registry_entry,
                           EventReport *event, BarrierNotifiable *done) = 0;

    /// Called on another node sending IdentifyProducer. @param event stores
    /// information about the incoming message. Filled: src_node, event,
    /// mask=1. Not filled: state.  @param registry_entry gives the registry
    /// entry for which the current handler is being called. @param done must
    /// be notified when the processing is done.
    virtual void
    handle_identify_producer(const EventRegistryEntry &registry_entry,
                           EventReport *event, BarrierNotifiable *done) = 0;
};

typedef void (EventHandler::*EventHandlerFunction)(
    const EventRegistryEntry &registry_entry, EventReport *event,
    BarrierNotifiable *done);

class EventIterator;

/// Global static object for registering event handlers.
///
/// Usage: create one of the implementation classes depending on the resource
/// requirements of your binary. In the event handlers constructor, register
/// the event handler via the singleton pointer.
///
class EventRegistry : public Singleton<EventRegistry>
{
public:
    virtual ~EventRegistry();

    /** Computes the alignment mask for registering an event range. Updates the
     * event by rounding and returns the mask value to be sent to the
     * register_handler function.
     * @param event is the event id to be registered. Will be modified.
     * @param size is the number of events to register from that offset. [event,
     * event+size) will be the registration range.
     */
    static unsigned align_mask(EventId *event, unsigned size);

    /// Adds a new event handler to the registry.
    virtual void register_handler(const EventRegistryEntry &entry,
                                  unsigned mask) = 0;
    /// Removes all registered instances of a given event handler pointer.
    virtual void unregister_handler(EventHandler *handler) = 0;

    /// Creates a new event iterator. Caller takes ownership of object.
    virtual EventIterator *create_iterator() = 0;

    /// Returns a monotonically increasing number that will change every time
    /// the set of registered event handlers change. Whenever this number
    /// changes, the iterators are invalidated and must be cleared.
    unsigned get_epoch()
    {
        return dirtyCounter_;
    }

protected:
    EventRegistry();

    /// Implementations must call this function from register and unregister
    /// handler to mark iterators being invalidated.
    void set_dirty()
    {
        ++dirtyCounter_;
    }

private:
    static EventRegistry *instance_;

    /// This counter will be incremented every time the set of event handlers
    /// change (and thus the event iterators are invalidated).
    unsigned dirtyCounter_ = 0;

    DISALLOW_COPY_AND_ASSIGN(EventRegistry);
};

}; /* namespace openlcb */

#endif // _OPENLCB_EVENTHANDLER_HXX_
<commit_msg>Adds more documentation comments.<commit_after>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * 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.
 *
 * 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 HOLDER 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.
 *
 * \file EventHandler.hxx
 * Interface for handling NMRAnet events.
 *
 * @author Balazs Racz
 * @date 29 September 2013
 */

#ifndef _OPENLCB_EVENTHANDLER_HXX_
#define _OPENLCB_EVENTHANDLER_HXX_

#include <stdint.h>

#include "executor/Notifiable.hxx"
#include "utils/AsyncMutex.hxx"
#include "utils/macros.h"
#include "utils/Singleton.hxx"
#include "openlcb/WriteHelper.hxx"

namespace openlcb
{

typedef uint64_t EventId;
class Node;
class EventHandler;

/*enum EventMask {
  EVENT_EXACT_MASK = 1,
  EVENT_ALL_MASK = 0xffffffffffffffffULL
  };*/

enum TestingEnum
{
    FOR_TESTING
};

/// Shared notification structure that is assembled for each incoming
/// event-related message, and passed around to all event handlers.
struct EventReport
{
    /// The event ID from the incoming message.
    EventId event;
    /// Specifies the mask in case the request is for an event range. The low
    /// bits are set to one, the high bits are set to zero. Ranges of size 1
    /// have
    /// mask==0, a range that covers all events has mask==0xffff...f.
    EventId mask;
    /// Information about the sender of the incoming event-related OpenLCB
    /// message. It is not specified whether the node_id or the alias is
    /// specified, but they are not both zero.
    NodeHandle src_node;
    /// nullptr for global messages; points to the specific virtual node for
    /// addressed events identify message.
    Node *dst_node;
    /// For producer/consumer identified messages, specifies the state of the
    /// producer/consumer as the sender of the message
    /// (valid/invalid/unknown/reserved).
    EventState state;

    /// These allow event handlers to produce up to four messages per
    /// invocation. They are always available at the entry to an event handler
    /// function.
    template <int N> WriteHelper *event_write_helper()
    {
        static_assert(1 <= N && N <= 4, "WriteHelper out of range.");
        return write_helpers + (N - 1);
    }

    /// Public constructor for use in tests only.
    EventReport(TestingEnum)
    {
    }

private:
    /// Constrained access to the constructors. We do this because the
    /// EventReport structure is pretty expensive due to the statically
    /// allocated memory of the write helpers. Only the EventIteratorFlow
    /// should have objects of this type.
    EventReport() {}
    friend class EventIteratorFlow;
    friend class DecoderRangeTest;

    /// Static objects usable by all event handler implementations.
    WriteHelper write_helpers[4];
};

/// Structure used in registering event handlers.
class EventRegistryEntry
{
public:
    /// Stores the event ID or beginning of range for which to register the
    /// given handler.
    EventId event;
    /// Pointer to the handler.
    EventHandler *handler;
    /// Opaque user argument. The event handlers may use this to store
    /// arbitrary data.
    uint32_t user_arg;
    EventRegistryEntry(EventHandler *_handler, EventId _event)
        : event(_event)
        , handler(_handler)
        , user_arg(0)
    {
    }
    EventRegistryEntry(EventHandler *_handler, EventId _event,
                       unsigned _user_arg)
        : event(_event)
        , handler(_handler)
        , user_arg(_user_arg)
    {
    }
};

/// Abstract base class for all event handlers. Instances of this class can
/// get registered with the event service to receive notifications of incoming
/// event messages from the bus.
class EventHandler
{
public:
    using EventReport = openlcb::EventReport;
    using EventRegistryEntry = openlcb::EventRegistryEntry;
    using EventId = openlcb::EventId;

    virtual ~EventHandler()
    {
    }

    /// Called on incoming EventReport messages. @param event stores
    /// information about the incoming message. Filled: src_node, event. Mask
    /// is always 1 (filled in). state is not filled in. @param registry_entry
    /// gives the registry entry for which the current handler is being
    /// called. @param done must be notified when the processing is done.
    virtual void handle_event_report(const EventRegistryEntry &registry_entry,
                                   EventReport *event,
                                   BarrierNotifiable *done) = 0;

    /// Called on another node sending ConsumerIdentified for this event.
    /// @param event stores information about the incoming message. Filled:
    /// event_id, mask=1, src_node, state.  @param registry_entry gives the
    /// registry entry for which the current handler is being called. @param
    /// done must be notified when the processing is done.
    virtual void
    handle_consumer_identified(const EventRegistryEntry &registry_entry,
                             EventReport *event, BarrierNotifiable *done)
    {
        done->notify();
    };

    /// Called on another node sending ConsumerRangeIdentified. @param event
    /// stores information about the incoming message. Filled: event id, mask
    /// (!= 1), src_node. Not filled: state.  @param registry_entry gives the
    /// registry entry for which the current handler is being called. @param
    /// done must be notified when the processing is done.
    virtual void
    handle_consumer_range_identified(const EventRegistryEntry &registry_entry,
                                  EventReport *event, BarrierNotifiable *done)
    {
        done->notify();
    }

    /// Called on another node sending ProducerIdentified for this event.
    /// @param event stores information about the incoming message. Filled:
    /// event_id, mask=1, src_node, state.  @param registry_entry gives the
    /// registry entry for which the current handler is being called. @param
    /// user_arg is an opaque argument passed in from the registration. @param
    /// done must be notified when the processing is done.
    virtual void
    handle_producer_identified(const EventRegistryEntry &registry_entry,
                             EventReport *event, BarrierNotifiable *done)
    {
        done->notify();
    }

    /// Called on another node sending ProducerRangeIdentified for this
    /// event. @param event stores information about the incoming
    /// message. Filled: event id, mask (!= 1), src_node. Not filled: state.
    /// @param registry_entry gives the registry entry for which the current
    /// handler is being called. @param done must be notified when the
    /// processing is done.
    virtual void
    handle_producer_range_identified(const EventRegistryEntry &registry_entry,
                                  EventReport *event, BarrierNotifiable *done)
    {
        done->notify();
    }

    /// Called on the need of sending out identification messages. @param event
    /// is NULL. This happens on startup, or when a global or addressed
    /// IdentifyGlobal message arrives. Might have destination node id! @param
    /// registry_entry gives the registry entry for which the current handler
    /// is being called. @param done must be notified when the processing is
    /// done.
    virtual void handle_identify_global(const EventRegistryEntry &registry_entry,
                                      EventReport *event,
                                      BarrierNotifiable *done) = 0;

    /// Called on another node sending IdentifyConsumer. @param event stores
    /// information about the incoming message. Filled: src_node, event,
    /// mask=1. Not filled: state. @param registry_entry gives the registry
    /// entry for which the current handler is being called. @param done must
    /// be notified when the processing is done.
    virtual void
    handle_identify_consumer(const EventRegistryEntry &registry_entry,
                           EventReport *event, BarrierNotifiable *done) = 0;

    /// Called on another node sending IdentifyProducer. @param event stores
    /// information about the incoming message. Filled: src_node, event,
    /// mask=1. Not filled: state.  @param registry_entry gives the registry
    /// entry for which the current handler is being called. @param done must
    /// be notified when the processing is done.
    virtual void
    handle_identify_producer(const EventRegistryEntry &registry_entry,
                           EventReport *event, BarrierNotifiable *done) = 0;
};

typedef void (EventHandler::*EventHandlerFunction)(
    const EventRegistryEntry &registry_entry, EventReport *event,
    BarrierNotifiable *done);

class EventIterator;

/// Global static object for registering event handlers.
///
/// Usage: create one of the implementation classes depending on the resource
/// requirements of your binary. In the event handlers constructor, register
/// the event handler via the singleton pointer.
///
class EventRegistry : public Singleton<EventRegistry>
{
public:
    virtual ~EventRegistry();

    /** Computes the alignment mask for registering an event range. Updates the
     * event by rounding and returns the mask value to be sent to the
     * register_handler function.
     * @param event is the event id to be registered. Will be modified.
     * @param size is the number of events to register from that offset. [event,
     * event+size) will be the registration range.
     */
    static unsigned align_mask(EventId *event, unsigned size);

    /// Adds a new event handler to the registry.
    /// @param entry is the structure parametrizingthe registration.
    /// @param mask is used for registering consecutive events; zero for
    /// registering just one event; otherwise use {@ref align_mask} to compute
    /// the base value and mask to use for registering a range of events.
    virtual void register_handler(const EventRegistryEntry &entry,
                                  unsigned mask) = 0;
    /// Removes all registered instances of a given event handler pointer.
    virtual void unregister_handler(EventHandler *handler) = 0;

    /// Creates a new event iterator. Caller takes ownership of object.
    virtual EventIterator *create_iterator() = 0;

    /// Returns a monotonically increasing number that will change every time
    /// the set of registered event handlers change. Whenever this number
    /// changes, the iterators are invalidated and must be cleared.
    unsigned get_epoch()
    {
        return dirtyCounter_;
    }

protected:
    EventRegistry();

    /// Implementations must call this function from register and unregister
    /// handler to mark iterators being invalidated.
    void set_dirty()
    {
        ++dirtyCounter_;
    }

private:
    static EventRegistry *instance_;

    /// This counter will be incremented every time the set of event handlers
    /// change (and thus the event iterators are invalidated).
    unsigned dirtyCounter_ = 0;

    DISALLOW_COPY_AND_ASSIGN(EventRegistry);
};

}; /* namespace openlcb */

#endif // _OPENLCB_EVENTHANDLER_HXX_
<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version.  The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* OpenSceneGraph Public License for more details.
*/

#include <osg/TransferFunction>

#include <osg/Notify>
#include <osg/io_utils>

using namespace osg;

///////////////////////////////////////////////////////////////////////
//
// TransferFunction base class
//
TransferFunction::TransferFunction()
{
}

TransferFunction::TransferFunction(const TransferFunction& tf, const CopyOp& copyop):
    Object(tf,copyop)
{
}

TransferFunction::~TransferFunction()
{
}

///////////////////////////////////////////////////////////////////////
//
// TransferFunction1D class
//
TransferFunction1D::TransferFunction1D()
{
}

TransferFunction1D::TransferFunction1D(const TransferFunction1D& tf, const CopyOp& copyop):
    TransferFunction(tf,copyop)
{
    allocate(tf.getNumberImageCells());
    assign(_colorMap);
}

void TransferFunction1D::allocate(unsigned int numX)
{
    _image = new osg::Image;
    _image->allocateImage(numX,1,1,GL_RGBA, GL_FLOAT);
    if (!_colorMap.empty()) assign(_colorMap);
}

void TransferFunction1D::clear(const osg::Vec4& color)
{
    ColorMap newColours;
    newColours[getMinimum()] = color;
    newColours[getMaximum()] = color;

    assign(newColours);
}

void TransferFunction1D::assignToImage(float lower_v, const osg::Vec4& lower_c, float upper_v, const osg::Vec4& upper_c)
{
    int endPos = getNumberImageCells()-1;
    float minimum = _colorMap.begin()->first;
    float maximum = _colorMap.rbegin()->first;
    float multiplier = float(endPos)/(maximum - minimum);
    osg::Vec4* imageData = reinterpret_cast<osg::Vec4*>(_image->data());

    float lower_iPos = (lower_v - minimum)*multiplier;
    float upper_iPos = (upper_v - minimum)*multiplier;

    int start_iPos = static_cast<int>(ceilf(lower_iPos));
    if (start_iPos < 0) start_iPos = 0;
    if (start_iPos > endPos) return;

    int end_iPos = static_cast<int>(floorf(upper_iPos));
    if (end_iPos < 0) return;
    if (end_iPos > endPos) end_iPos = endPos;

    //OSG_NOTICE<<"TransferFunction1D::assignToImage[lower_v="<<lower_v<<", lower_c="<<lower_c<<", upper_v="<<upper_v<<" upper_c="<<upper_c<<std::endl;
    //OSG_NOTICE<<"  lower_iPos="<<lower_iPos<<"  start_iPpos="<<start_iPos<<std::endl;
    //OSG_NOTICE<<"  upper_iPos="<<upper_iPos<<"  end_iPpos="<<end_iPos<<std::endl;

    // upper_iPos can be identical to lower_iPos in case an underflow occurred while calculation the values;
    // if both values are identical delta_c is identical to the null vector otherwise it is calculated like follows:
    Vec4 delta_c;
    if (upper_iPos != lower_iPos)
      delta_c = (upper_c-lower_c)/(upper_iPos-lower_iPos);

    float iPos = static_cast<float>(start_iPos);
    for(int i=start_iPos; i<=end_iPos; ++i, ++iPos)
    {
        imageData[i] = lower_c + delta_c*(iPos-lower_iPos);
        //OSG_NOTICE<<"    imageData["<<i<<"] = "<<imageData[i]<<std::endl;
    }

    _image->dirty();
}

void TransferFunction1D::setColor(float v, const osg::Vec4& color, bool updateImage)
{
    if (!updateImage)
    {
        _colorMap[v] = color;
        return;
    }

    if (!_image) allocate(1024);

    if (_colorMap.empty() || v<getMinimum() || v>getMaximum())
    {
        _colorMap[v] = color;

        assign(_colorMap);
        return;
    }

    _colorMap[v] = color;

    ColorMap::iterator itr = _colorMap.find(v);

    if (itr != _colorMap.begin())
    {
        ColorMap::iterator previous_itr = itr;
        --previous_itr;

        assignToImage(previous_itr->first, previous_itr->second, v, color);
    }

    ColorMap::iterator next_itr = itr;
    ++next_itr;

    if (next_itr != _colorMap.end())
    {
        assignToImage(v, color, next_itr->first, next_itr->second);
    }
}

osg::Vec4 TransferFunction1D::getColor(float v) const
{
    if (_colorMap.empty()) return osg::Vec4(1.0f,1.0f,1.0f,1.0f);
    if (_colorMap.size()==1) return _colorMap.begin()->second;

    if (v <= _colorMap.begin()->first) return _colorMap.begin()->second;
    if (v >= _colorMap.rbegin()->first) return _colorMap.rbegin()->second;

    // need to implement
    std::pair<ColorMap::const_iterator, ColorMap::const_iterator> range = _colorMap.equal_range(v);

    // we have an identical match
    if (v == range.first->first) return range.first->second;

    // range.first will be at the next element after v, so move it before.
    --range.first;

    float vBefore = range.first->first;
    const osg::Vec4& cBefore = range.first->second;

    float vAfter = range.second->first;
    const osg::Vec4& cAfter = range.second->second;

    float r = (v-vBefore)/(vAfter-vBefore);

    return cBefore*(1.0f-r) + cAfter*r;
}

void TransferFunction1D::assign(const ColorMap& newColours)
{
    _colorMap = newColours;

    updateImage();
}

void TransferFunction1D::updateImage()
{
    if (_colorMap.empty()) return;

    if (!_image || _image->data()==0) allocate(1024);

    osg::Vec4* imageData = reinterpret_cast<osg::Vec4*>(_image->data());

    if (_colorMap.size()==1)
    {
        osg::Vec4 color = _colorMap.begin()->second;

        for(int i=0; i<_image->s(); ++i)
        {
            imageData[i] = color;
        }
        _image->dirty();
        return;
    }

    ColorMap::const_iterator lower_itr = _colorMap.begin();
    ColorMap::const_iterator upper_itr = lower_itr;
    ++upper_itr;

    for(;
        upper_itr != _colorMap.end();
        ++upper_itr)
    {
        float lower_v = lower_itr->first;
        const osg::Vec4& lower_c = lower_itr->second;
        float upper_v = upper_itr->first;
        const osg::Vec4& upper_c = upper_itr->second;

        assignToImage(lower_v, lower_c, upper_v, upper_c);

        lower_itr = upper_itr;
    }

    _image->dirty();
}
<commit_msg>Fixed image allocation operations.<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version.  The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* OpenSceneGraph Public License for more details.
*/

#include <osg/TransferFunction>

#include <osg/Notify>
#include <osg/io_utils>

using namespace osg;

///////////////////////////////////////////////////////////////////////
//
// TransferFunction base class
//
TransferFunction::TransferFunction()
{
}

TransferFunction::TransferFunction(const TransferFunction& tf, const CopyOp& copyop):
    Object(tf,copyop)
{
}

TransferFunction::~TransferFunction()
{
}

///////////////////////////////////////////////////////////////////////
//
// TransferFunction1D class
//
TransferFunction1D::TransferFunction1D()
{
}

TransferFunction1D::TransferFunction1D(const TransferFunction1D& tf, const CopyOp& copyop):
    TransferFunction(tf,copyop)
{
    allocate(tf.getNumberImageCells());
    assign(tf._colorMap);
}

void TransferFunction1D::allocate(unsigned int numX)
{
    _image = new osg::Image;
    _image->allocateImage(numX,1,1,GL_RGBA, GL_FLOAT);

    updateImage();
}

void TransferFunction1D::clear(const osg::Vec4& color)
{
    ColorMap newColours;
    newColours[getMinimum()] = color;
    newColours[getMaximum()] = color;

    assign(newColours);
}

void TransferFunction1D::assignToImage(float lower_v, const osg::Vec4& lower_c, float upper_v, const osg::Vec4& upper_c)
{
    int endPos = getNumberImageCells()-1;
    float minimum = _colorMap.begin()->first;
    float maximum = _colorMap.rbegin()->first;
    float multiplier = float(endPos)/(maximum - minimum);
    osg::Vec4* imageData = reinterpret_cast<osg::Vec4*>(_image->data());

    float lower_iPos = (lower_v - minimum)*multiplier;
    float upper_iPos = (upper_v - minimum)*multiplier;

    int start_iPos = static_cast<int>(ceilf(lower_iPos));
    if (start_iPos < 0) start_iPos = 0;
    if (start_iPos > endPos) return;

    int end_iPos = static_cast<int>(floorf(upper_iPos));
    if (end_iPos < 0) return;
    if (end_iPos > endPos) end_iPos = endPos;

    //OSG_NOTICE<<"TransferFunction1D::assignToImage[lower_v="<<lower_v<<", lower_c="<<lower_c<<", upper_v="<<upper_v<<" upper_c="<<upper_c<<std::endl;
    //OSG_NOTICE<<"  lower_iPos="<<lower_iPos<<"  start_iPpos="<<start_iPos<<std::endl;
    //OSG_NOTICE<<"  upper_iPos="<<upper_iPos<<"  end_iPpos="<<end_iPos<<std::endl;

    // upper_iPos can be identical to lower_iPos in case an underflow occurred while calculation the values;
    // if both values are identical delta_c is identical to the null vector otherwise it is calculated like follows:
    Vec4 delta_c;
    if (upper_iPos != lower_iPos)
      delta_c = (upper_c-lower_c)/(upper_iPos-lower_iPos);

    float iPos = static_cast<float>(start_iPos);
    for(int i=start_iPos; i<=end_iPos; ++i, ++iPos)
    {
        imageData[i] = lower_c + delta_c*(iPos-lower_iPos);
        //OSG_NOTICE<<"    imageData["<<i<<"] = "<<imageData[i]<<std::endl;
    }

    _image->dirty();
}

void TransferFunction1D::setColor(float v, const osg::Vec4& color, bool updateImage)
{
    if (!updateImage)
    {
        _colorMap[v] = color;
        return;
    }

    if (!_image) allocate(1024);

    if (_colorMap.empty() || v<getMinimum() || v>getMaximum())
    {
        _colorMap[v] = color;

        assign(_colorMap);
        return;
    }

    _colorMap[v] = color;

    ColorMap::iterator itr = _colorMap.find(v);

    if (itr != _colorMap.begin())
    {
        ColorMap::iterator previous_itr = itr;
        --previous_itr;

        assignToImage(previous_itr->first, previous_itr->second, v, color);
    }

    ColorMap::iterator next_itr = itr;
    ++next_itr;

    if (next_itr != _colorMap.end())
    {
        assignToImage(v, color, next_itr->first, next_itr->second);
    }
}

osg::Vec4 TransferFunction1D::getColor(float v) const
{
    if (_colorMap.empty()) return osg::Vec4(1.0f,1.0f,1.0f,1.0f);
    if (_colorMap.size()==1) return _colorMap.begin()->second;

    if (v <= _colorMap.begin()->first) return _colorMap.begin()->second;
    if (v >= _colorMap.rbegin()->first) return _colorMap.rbegin()->second;

    // need to implement
    std::pair<ColorMap::const_iterator, ColorMap::const_iterator> range = _colorMap.equal_range(v);

    // we have an identical match
    if (v == range.first->first) return range.first->second;

    // range.first will be at the next element after v, so move it before.
    --range.first;

    float vBefore = range.first->first;
    const osg::Vec4& cBefore = range.first->second;

    float vAfter = range.second->first;
    const osg::Vec4& cAfter = range.second->second;

    float r = (v-vBefore)/(vAfter-vBefore);

    return cBefore*(1.0f-r) + cAfter*r;
}

void TransferFunction1D::assign(const ColorMap& newColours)
{
    if (&_colorMap != &newColours) _colorMap = newColours;

    updateImage();
}

void TransferFunction1D::updateImage()
{
    if (_colorMap.empty()) return;

    if (!_image || _image->data()==0) allocate(1024);

    osg::Vec4* imageData = reinterpret_cast<osg::Vec4*>(_image->data());

    if (_colorMap.size()==1)
    {
        osg::Vec4 color = _colorMap.begin()->second;

        for(int i=0; i<_image->s(); ++i)
        {
            imageData[i] = color;
        }
        _image->dirty();
        return;
    }

    ColorMap::const_iterator lower_itr = _colorMap.begin();
    ColorMap::const_iterator upper_itr = lower_itr;
    ++upper_itr;

    for(;
        upper_itr != _colorMap.end();
        ++upper_itr)
    {
        float lower_v = lower_itr->first;
        const osg::Vec4& lower_c = lower_itr->second;
        float upper_v = upper_itr->first;
        const osg::Vec4& upper_c = upper_itr->second;

        assignToImage(lower_v, lower_c, upper_v, upper_c);

        lower_itr = upper_itr;
    }

    _image->dirty();
}
<|endoftext|>