<commit_before>#define TP_QT4_ENABLE_LOWLEVEL_API

#include <TelepathyQt4/AvatarData>
#include <TelepathyQt4/ChannelFactory>
#include <TelepathyQt4/Connection>
#include <TelepathyQt4/ConnectionLowlevel>
#include <TelepathyQt4/Contact>
#include <TelepathyQt4/ContactFactory>
#include <TelepathyQt4/ContactManager>
#include <TelepathyQt4/PendingContacts>
#include <TelepathyQt4/PendingReady>

#include <telepathy-glib/telepathy-glib.h>

#include <tests/lib/glib/contacts-conn.h>
#include <tests/lib/test.h>

using namespace Tp;

class SmartDir : public QDir
{
public:
    SmartDir(const QString &path) : QDir(path) { }
    bool rmdir() { return QDir().rmdir(path()); }
    bool removeDirectory();
};

bool SmartDir::removeDirectory()
{
    bool ret = true;

    QFileInfoList list = entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
    Q_FOREACH (QFileInfo info, list) {
        if (info.isDir()) {
            SmartDir subDir(info.filePath());
            if (!subDir.removeDirectory()) {
                ret = false;
            }
        } else {
            qDebug() << "deleting" << info.filePath();
            if (!QFile(info.filePath()).remove()) {
                ret = false;
            }
        }
    }

    if (ret) {
        qDebug() << "deleting" << path();
        ret = rmdir();
    }

    return ret;
}

class TestContactsAvatar : public Test
{
    Q_OBJECT

public:
    TestContactsAvatar(QObject *parent = 0)
        : Test(parent), mConnService(0)
    { }

protected Q_SLOTS:
    void expectConnInvalidated();
    void expectPendingContactsFinished(Tp::PendingOperation *);
    void onAvatarRetrieved(uint, const QString &, const QByteArray &, const QString &);
    void onAvatarDataChanged(const Tp::AvatarData &);
    void createContactWithFakeAvatar(const char *);

private Q_SLOTS:
    void initTestCase();
    void init();

    void testAvatar();

    void cleanup();
    void cleanupTestCase();

private:
    QString mConnName, mConnPath;
    TpTestsContactsConnection *mConnService;
    ConnectionPtr mConn;
    QList<ContactPtr> mContacts;
    bool mAvatarRetrievedCalled;
};

void TestContactsAvatar::onAvatarRetrieved(uint handle, const QString &token,
    const QByteArray &data, const QString &mimeType)
{
    Q_UNUSED(handle);
    Q_UNUSED(token);
    Q_UNUSED(data);
    Q_UNUSED(mimeType);

    mAvatarRetrievedCalled = true;
}

void TestContactsAvatar::onAvatarDataChanged(const AvatarData &avatar)
{
    Q_UNUSED(avatar);
    mLoop->exit(0);
}

void TestContactsAvatar::createContactWithFakeAvatar(const char *id)
{
    TpHandleRepoIface *serviceRepo = tp_base_connection_get_handles(
        (TpBaseConnection *) mConnService, TP_HANDLE_TYPE_CONTACT);
    const gchar avatarData[] = "fake-avatar-data";
    const gchar avatarToken[] = "fake-avatar-token";
    const gchar avatarMimeType[] = "fake-avatar-mime-type";
    TpHandle handle;
    GArray *array;

    handle = tp_handle_ensure(serviceRepo, id, NULL, NULL);
    array = g_array_new(FALSE, FALSE, sizeof(gchar));
    g_array_append_vals (array, avatarData, strlen(avatarData));

    tp_tests_contacts_connection_change_avatar_data(mConnService, handle,
        array, avatarMimeType, avatarToken);

    Tp::UIntList handles = Tp::UIntList() << handle;
    Features features = Features()
        << Contact::FeatureAvatarToken
        << Contact::FeatureAvatarData;

    PendingContacts *pending = mConn->contactManager()->contactsForHandles(
        handles, features);
    QVERIFY(connect(pending,
                    SIGNAL(finished(Tp::PendingOperation*)),
                    SLOT(expectPendingContactsFinished(Tp::PendingOperation*))));
    QCOMPARE(mLoop->exec(), 0);
    QCOMPARE(mContacts.size(), 1);

    if (mContacts[0]->avatarData().fileName.isEmpty()) {
        QVERIFY(connect(mContacts[0].data(),
                        SIGNAL(avatarDataChanged(const Tp::AvatarData &)),
                        SLOT(onAvatarDataChanged(const Tp::AvatarData &))));
        QCOMPARE(mLoop->exec(), 0);
    }

    AvatarData avatar = mContacts[0]->avatarData();

    qDebug() << "Contact created:";
    qDebug() << "Avatar token:" << mContacts[0]->avatarToken();
    qDebug() << "Avatar file:" << avatar.fileName;
    qDebug() << "Avatar MimeType:" << avatar.mimeType;

    QFile file(avatar.fileName);
    file.open(QIODevice::ReadOnly);
    QByteArray data(file.readAll());
    file.close();

    QCOMPARE(mContacts[0]->avatarToken(), QString(QLatin1String(avatarToken)));
    QCOMPARE(data, QByteArray(avatarData));
    QCOMPARE(avatar.mimeType, QString(QLatin1String(avatarMimeType)));
}

#define RAND_STR_LEN 6

void TestContactsAvatar::testAvatar()
{
    /* Make sure our tests does not mess up user's avatar cache */
    qsrand(time(0));
    static const char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    static const int DirNameLength = 6;
    QString dirName;
    for (int i = 0; i < DirNameLength; ++i) {
        dirName += QLatin1Char(letters[qrand() % qstrlen(letters)]);
    }
    QString tmpDir = QString(QLatin1String("%1/%2")).arg(QDir::tempPath()).arg(dirName);
    QByteArray a = tmpDir.toLatin1();
    setenv ("XDG_CACHE_HOME", a.constData(), true);

    Client::ConnectionInterfaceAvatarsInterface *connAvatarsInterface =
        mConn->optionalInterface<Client::ConnectionInterfaceAvatarsInterface>();

    /* Check if AvatarRetrieved gets called */
    connect(connAvatarsInterface,
            SIGNAL(AvatarRetrieved(uint, const QString &, const QByteArray &, const QString &)),
            SLOT(onAvatarRetrieved(uint, const QString &, const QByteArray &, const QString &)));

    /* First time we create a contact, avatar should not be in cache, so
     * AvatarRetrieved should be called */
    mAvatarRetrievedCalled = false;
    createContactWithFakeAvatar("foo");
    QVERIFY(mAvatarRetrievedCalled);

    /* Second time we create a contact, avatar should be in cache now, so
     * AvatarRetrieved should NOT be called */
    mAvatarRetrievedCalled = false;
    createContactWithFakeAvatar("bar");
    QVERIFY(!mAvatarRetrievedCalled);

    QVERIFY(SmartDir(tmpDir).removeDirectory());
}

void TestContactsAvatar::expectConnInvalidated()
{
    mLoop->exit(0);
}

void TestContactsAvatar::expectPendingContactsFinished(PendingOperation *op)
{
    if (!op->isFinished()) {
        qWarning() << "unfinished";
        mLoop->exit(1);
        return;
    }

    if (op->isError()) {
        qWarning().nospace() << op->errorName()
            << ": " << op->errorMessage();
        mLoop->exit(2);
        return;
    }

    if (!op->isValid()) {
        qWarning() << "inconsistent results";
        mLoop->exit(3);
        return;
    }

    qDebug() << "finished";
    PendingContacts *pending = qobject_cast<PendingContacts *>(op);
    mContacts = pending->contacts();

    mLoop->exit(0);
}

void TestContactsAvatar::initTestCase()
{
    initTestCaseImpl();

    g_type_init();
    g_set_prgname("contacts-avatar");
    tp_debug_set_flags("all");
    dbus_g_bus_get(DBUS_BUS_STARTER, 0);

    gchar *name;
    gchar *connPath;
    GError *error = 0;

    mConnService = TP_TESTS_CONTACTS_CONNECTION(g_object_new(
            TP_TESTS_TYPE_CONTACTS_CONNECTION,
            "account", "me@example.com",
            "protocol", "foo",
            NULL));
    QVERIFY(mConnService != 0);
    QVERIFY(tp_base_connection_register(TP_BASE_CONNECTION(mConnService),
                "foo", &name, &connPath, &error));
    QVERIFY(error == 0);

    QVERIFY(name != 0);
    QVERIFY(connPath != 0);

    mConnName = QLatin1String(name);
    mConnPath = QLatin1String(connPath);

    g_free(name);
    g_free(connPath);

    mConn = Connection::create(mConnName, mConnPath,
            ChannelFactory::create(QDBusConnection::sessionBus()),
            ContactFactory::create());
    QCOMPARE(mConn->isReady(), false);

    QVERIFY(connect(mConn->lowlevel()->requestConnect(),
                    SIGNAL(finished(Tp::PendingOperation*)),
                    SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
    QCOMPARE(mLoop->exec(), 0);
    QCOMPARE(mConn->isReady(), true);

    QCOMPARE(mConn->status(), ConnectionStatusConnected);

    QVERIFY(mConn->contactManager()->supportedFeatures().contains(Contact::FeatureAvatarData));
}

void TestContactsAvatar::init()
{
    initImpl();
}

void TestContactsAvatar::cleanup()
{
    cleanupImpl();
}

void TestContactsAvatar::cleanupTestCase()
{
    if (mConn) {
        // Disconnect and wait for invalidated
        QVERIFY(connect(mConn->lowlevel()->requestDisconnect(),
                        SIGNAL(finished(Tp::PendingOperation*)),
                        SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
        QCOMPARE(mLoop->exec(), 0);

        if (mConn->isValid()) {
            QVERIFY(connect(mConn.data(),
                            SIGNAL(invalidated(Tp::DBusProxy *,
                                               const QString &, const QString &)),
                            SLOT(expectConnInvalidated())));
            QCOMPARE(mLoop->exec(), 0);
        }
    }

    if (mConnService != 0) {
        g_object_unref(mConnService);
        mConnService = 0;
    }

    cleanupTestCaseImpl();
}

QTEST_MAIN(TestContactsAvatar)
#include "_gen/contacts-avatar.cpp.moc.hpp"
<commit_msg>Don't leak avatar data array in TestContactsAvatar<commit_after>#define TP_QT4_ENABLE_LOWLEVEL_API

#include <TelepathyQt4/AvatarData>
#include <TelepathyQt4/ChannelFactory>
#include <TelepathyQt4/Connection>
#include <TelepathyQt4/ConnectionLowlevel>
#include <TelepathyQt4/Contact>
#include <TelepathyQt4/ContactFactory>
#include <TelepathyQt4/ContactManager>
#include <TelepathyQt4/PendingContacts>
#include <TelepathyQt4/PendingReady>

#include <telepathy-glib/telepathy-glib.h>

#include <tests/lib/glib/contacts-conn.h>
#include <tests/lib/test.h>

using namespace Tp;

class SmartDir : public QDir
{
public:
    SmartDir(const QString &path) : QDir(path) { }
    bool rmdir() { return QDir().rmdir(path()); }
    bool removeDirectory();
};

bool SmartDir::removeDirectory()
{
    bool ret = true;

    QFileInfoList list = entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
    Q_FOREACH (QFileInfo info, list) {
        if (info.isDir()) {
            SmartDir subDir(info.filePath());
            if (!subDir.removeDirectory()) {
                ret = false;
            }
        } else {
            qDebug() << "deleting" << info.filePath();
            if (!QFile(info.filePath()).remove()) {
                ret = false;
            }
        }
    }

    if (ret) {
        qDebug() << "deleting" << path();
        ret = rmdir();
    }

    return ret;
}

class TestContactsAvatar : public Test
{
    Q_OBJECT

public:
    TestContactsAvatar(QObject *parent = 0)
        : Test(parent), mConnService(0)
    { }

protected Q_SLOTS:
    void expectConnInvalidated();
    void expectPendingContactsFinished(Tp::PendingOperation *);
    void onAvatarRetrieved(uint, const QString &, const QByteArray &, const QString &);
    void onAvatarDataChanged(const Tp::AvatarData &);
    void createContactWithFakeAvatar(const char *);

private Q_SLOTS:
    void initTestCase();
    void init();

    void testAvatar();

    void cleanup();
    void cleanupTestCase();

private:
    QString mConnName, mConnPath;
    TpTestsContactsConnection *mConnService;
    ConnectionPtr mConn;
    QList<ContactPtr> mContacts;
    bool mAvatarRetrievedCalled;
};

void TestContactsAvatar::onAvatarRetrieved(uint handle, const QString &token,
    const QByteArray &data, const QString &mimeType)
{
    Q_UNUSED(handle);
    Q_UNUSED(token);
    Q_UNUSED(data);
    Q_UNUSED(mimeType);

    mAvatarRetrievedCalled = true;
}

void TestContactsAvatar::onAvatarDataChanged(const AvatarData &avatar)
{
    Q_UNUSED(avatar);
    mLoop->exit(0);
}

void TestContactsAvatar::createContactWithFakeAvatar(const char *id)
{
    TpHandleRepoIface *serviceRepo = tp_base_connection_get_handles(
        (TpBaseConnection *) mConnService, TP_HANDLE_TYPE_CONTACT);
    const gchar avatarData[] = "fake-avatar-data";
    const gchar avatarToken[] = "fake-avatar-token";
    const gchar avatarMimeType[] = "fake-avatar-mime-type";
    TpHandle handle;
    GArray *array;

    handle = tp_handle_ensure(serviceRepo, id, NULL, NULL);
    array = g_array_new(FALSE, FALSE, sizeof(gchar));
    g_array_append_vals (array, avatarData, strlen(avatarData));

    tp_tests_contacts_connection_change_avatar_data(mConnService, handle,
        array, avatarMimeType, avatarToken);

    Tp::UIntList handles = Tp::UIntList() << handle;
    Features features = Features()
        << Contact::FeatureAvatarToken
        << Contact::FeatureAvatarData;

    PendingContacts *pending = mConn->contactManager()->contactsForHandles(
        handles, features);
    QVERIFY(connect(pending,
                    SIGNAL(finished(Tp::PendingOperation*)),
                    SLOT(expectPendingContactsFinished(Tp::PendingOperation*))));
    QCOMPARE(mLoop->exec(), 0);
    QCOMPARE(mContacts.size(), 1);

    if (mContacts[0]->avatarData().fileName.isEmpty()) {
        QVERIFY(connect(mContacts[0].data(),
                        SIGNAL(avatarDataChanged(const Tp::AvatarData &)),
                        SLOT(onAvatarDataChanged(const Tp::AvatarData &))));
        QCOMPARE(mLoop->exec(), 0);
    }

    AvatarData avatar = mContacts[0]->avatarData();

    qDebug() << "Contact created:";
    qDebug() << "Avatar token:" << mContacts[0]->avatarToken();
    qDebug() << "Avatar file:" << avatar.fileName;
    qDebug() << "Avatar MimeType:" << avatar.mimeType;

    QFile file(avatar.fileName);
    file.open(QIODevice::ReadOnly);
    QByteArray data(file.readAll());
    file.close();

    QCOMPARE(mContacts[0]->avatarToken(), QString(QLatin1String(avatarToken)));
    QCOMPARE(data, QByteArray(avatarData));
    QCOMPARE(avatar.mimeType, QString(QLatin1String(avatarMimeType)));

    g_array_free(array, TRUE);
}

#define RAND_STR_LEN 6

void TestContactsAvatar::testAvatar()
{
    /* Make sure our tests does not mess up user's avatar cache */
    qsrand(time(0));
    static const char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    static const int DirNameLength = 6;
    QString dirName;
    for (int i = 0; i < DirNameLength; ++i) {
        dirName += QLatin1Char(letters[qrand() % qstrlen(letters)]);
    }
    QString tmpDir = QString(QLatin1String("%1/%2")).arg(QDir::tempPath()).arg(dirName);
    QByteArray a = tmpDir.toLatin1();
    setenv ("XDG_CACHE_HOME", a.constData(), true);

    Client::ConnectionInterfaceAvatarsInterface *connAvatarsInterface =
        mConn->optionalInterface<Client::ConnectionInterfaceAvatarsInterface>();

    /* Check if AvatarRetrieved gets called */
    connect(connAvatarsInterface,
            SIGNAL(AvatarRetrieved(uint, const QString &, const QByteArray &, const QString &)),
            SLOT(onAvatarRetrieved(uint, const QString &, const QByteArray &, const QString &)));

    /* First time we create a contact, avatar should not be in cache, so
     * AvatarRetrieved should be called */
    mAvatarRetrievedCalled = false;
    createContactWithFakeAvatar("foo");
    QVERIFY(mAvatarRetrievedCalled);

    /* Second time we create a contact, avatar should be in cache now, so
     * AvatarRetrieved should NOT be called */
    mAvatarRetrievedCalled = false;
    createContactWithFakeAvatar("bar");
    QVERIFY(!mAvatarRetrievedCalled);

    QVERIFY(SmartDir(tmpDir).removeDirectory());
}

void TestContactsAvatar::expectConnInvalidated()
{
    mLoop->exit(0);
}

void TestContactsAvatar::expectPendingContactsFinished(PendingOperation *op)
{
    if (!op->isFinished()) {
        qWarning() << "unfinished";
        mLoop->exit(1);
        return;
    }

    if (op->isError()) {
        qWarning().nospace() << op->errorName()
            << ": " << op->errorMessage();
        mLoop->exit(2);
        return;
    }

    if (!op->isValid()) {
        qWarning() << "inconsistent results";
        mLoop->exit(3);
        return;
    }

    qDebug() << "finished";
    PendingContacts *pending = qobject_cast<PendingContacts *>(op);
    mContacts = pending->contacts();

    mLoop->exit(0);
}

void TestContactsAvatar::initTestCase()
{
    initTestCaseImpl();

    g_type_init();
    g_set_prgname("contacts-avatar");
    tp_debug_set_flags("all");
    dbus_g_bus_get(DBUS_BUS_STARTER, 0);

    gchar *name;
    gchar *connPath;
    GError *error = 0;

    mConnService = TP_TESTS_CONTACTS_CONNECTION(g_object_new(
            TP_TESTS_TYPE_CONTACTS_CONNECTION,
            "account", "me@example.com",
            "protocol", "foo",
            NULL));
    QVERIFY(mConnService != 0);
    QVERIFY(tp_base_connection_register(TP_BASE_CONNECTION(mConnService),
                "foo", &name, &connPath, &error));
    QVERIFY(error == 0);

    QVERIFY(name != 0);
    QVERIFY(connPath != 0);

    mConnName = QLatin1String(name);
    mConnPath = QLatin1String(connPath);

    g_free(name);
    g_free(connPath);

    mConn = Connection::create(mConnName, mConnPath,
            ChannelFactory::create(QDBusConnection::sessionBus()),
            ContactFactory::create());
    QCOMPARE(mConn->isReady(), false);

    QVERIFY(connect(mConn->lowlevel()->requestConnect(),
                    SIGNAL(finished(Tp::PendingOperation*)),
                    SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
    QCOMPARE(mLoop->exec(), 0);
    QCOMPARE(mConn->isReady(), true);

    QCOMPARE(mConn->status(), ConnectionStatusConnected);

    QVERIFY(mConn->contactManager()->supportedFeatures().contains(Contact::FeatureAvatarData));
}

void TestContactsAvatar::init()
{
    initImpl();
}

void TestContactsAvatar::cleanup()
{
    cleanupImpl();
}

void TestContactsAvatar::cleanupTestCase()
{
    if (mConn) {
        // Disconnect and wait for invalidated
        QVERIFY(connect(mConn->lowlevel()->requestDisconnect(),
                        SIGNAL(finished(Tp::PendingOperation*)),
                        SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
        QCOMPARE(mLoop->exec(), 0);

        if (mConn->isValid()) {
            QVERIFY(connect(mConn.data(),
                            SIGNAL(invalidated(Tp::DBusProxy *,
                                               const QString &, const QString &)),
                            SLOT(expectConnInvalidated())));
            QCOMPARE(mLoop->exec(), 0);
        }
    }

    if (mConnService != 0) {
        g_object_unref(mConnService);
        mConnService = 0;
    }

    cleanupTestCaseImpl();
}

QTEST_MAIN(TestContactsAvatar)
#include "_gen/contacts-avatar.cpp.moc.hpp"
<|endoftext|>


<commit_before>// Copyright (c) 2015 Andrew Sutton
// All rights reserved

#ifndef LINGO_NODE_HPP
#define LINGO_NODE_HPP

#include "lingo/utility.hpp"

#include <cassert>
#include <vector>
#include <type_traits>

// This module provides defines a node abstraction for various
// kinds of trees used in different languages and facilities
// for working with those abstractions.

namespace lingo
{

template<typename T>
inline String
get_node_name(T const* t)
{
  return type_str(*t);
}

// -------------------------------------------------------------------------- //
//                            Special values
//
// The following functions define special values of node pointers.

// Returns true if the node is empty.
template<typename T>
inline bool
is_empty_node(T const* t)
{
  return t == nullptr;
}

// Construct a node pointer that acts as an error value.
// The type of the node is explicitly given as a template
// argument.
template<typename T>
inline T*
make_error_node()
{
  return (T*)0x01;
}

// Returns true if `t` is an error node.
template<typename T>
inline bool
is_error_node(T const* t)
{
  return t == make_error_node<T>();
}

// Returns true if `t` is neither null nor an error.
template<typename T>
inline bool
is_valid_node(T const* t)
{
  return t && !is_error_node(t);
}

// -------------------------------------------------------------------------- //
//                        Dynamic type information

// Returns true if the object pointed to by `u` has
// the dynamic type `T`.
template<typename T, typename U>
inline bool
is(U const* u)
{
  return dynamic_cast<T const*>(u);
}

// Statically cast a pointer to a Node of type T to a
// pointer to a Node of type U. This is not a checked
// operation (except in debug mode).
//
// Note that this allows null and error nodes to be
// interpreted as nodes of the given type (as their
// values are considered common to all).
template<typename T, typename U>
inline T*
cast(U* u)
{
  lingo_assert(is_valid_node(u) ? is<T>(u) : true);
  return static_cast<T*>(u);
}

template<typename T, typename U>
inline T const*
cast(U const* u)
{
  assert(is_valid_node(u) ? is<T>(u) : true);
  return static_cast<T const*>(u);
}

// Returns `u` with type `T*` iff the object pointed
// to by `u` has dynamic type `T`.
template<typename T, typename U>
inline T*
as(U* u)
{
  return dynamic_cast<T*>(u);
}

template<typename T, typename U>
inline T const*
as(U const* u)
{
  return dynamic_cast<T const*>(u);
}

// Return a non-const pointer to the term. This is used
// to modify a term post-initializatoin (which should
// be rare).
template<typename T>
inline T*
modify(T const* t)
{
  return const_cast<T*>(t);
}

// -------------------------------------------------------------------------- //
//                              Concepts
//
// There are several concepts describing the kinds of nodes over
// which an algorithm can operate generically. The primary
// characterization // of node types is based on their arity.
//
// ## The Node concept
//
// Every node in an abstract syntax tree must provide a static
// constexpr member, node_kind that statically defines the kind
// of node.
//
// ## Node arity
//
// Nodes with fixed arity have tuple-like member names (e.g.,
// first, second, third). These accessor members correspond to
// the sub-terms of each node. Accessor members may have different
// tpyes, and are not required to be nodes.
//
// A nullary node is a Node is an empty tuple, and has no
// accessor members. A unary node has only accessor member
// `first`. A binary node has only `first` and `second`.
// A ternary node has `first`, second`, and `third`.
//
// Note that more arity nodes can be defined if needed.
//
// A k-ary has k sub-terms, and that range of terms is accessed
// by its `begin()` and `end()` members. The types of these
// sub-terms are the same.

namespace traits
{

// A helper trait used to detect substitution failures.
template<typename T>
struct is_non_void
{
  static constexpr bool value = true;
};

template<>
struct is_non_void<void>
{
  static constexpr bool value = false;
};

// Detect the existince of the member t->first.
template<typename T>
struct first_type
{
  template<typename U> static auto f(U* p) -> decltype(p->first);
  static                      void f(...);

  using type = decltype(f(std::declval<T*>()));
};

// Detect the existence of the member t->second;
template<typename T>
struct second_type
{
  template<typename U> static auto f(U* p) -> decltype(p->second);
  static                      void f(...);

  using type = decltype(f(std::declval<T*>()));
};

// Detect the existence of the member t->third;
template<typename T>
struct third_type
{
  template<typename U> static auto f(U* p) -> decltype(p->third);
  static                      void f(...);

  using type = decltype(f(std::declval<T*>()));
};

// Detect the existence of the member t->begin().
template<typename T>
struct begin_type
{
  template<typename U> static auto f(U* p) -> decltype(p->begin());
  static                      void f(...);

  using type = decltype(f(std::declval<T*>()));
};

// Detect the existence of the member t->end().
template<typename T>
struct end_type
{
  template<typename U> static auto f(U* p) -> decltype(p->end());
  static                      void f(...);

  using type = decltype(f(std::declval<T*>()));
};

// Returns true when `T` has the member `first`.
template<typename T>
constexpr bool
has_first()
{
  return is_non_void<typename first_type<T>::type>::value;
}

// Returns true when `T` has the member `second`.
template<typename T>
constexpr bool
has_second()
{
  return is_non_void<typename second_type<T>::type>::value;
}

// Returns true when `T` has the member `third`.
template<typename T>
constexpr bool
has_third()
{
  return is_non_void<typename third_type<T>::type>::value;
}

// Returns true when `T` has the member `begin`.
template<typename T>
constexpr bool
has_begin()
{
  return is_non_void<typename begin_type<T>::type>::value;
}

// Returns true when `T` has the member `end`.
template<typename T>
constexpr bool
has_end()
{
  return is_non_void<typename end_type<T>::type>::value;
}

} // namesapce traits

// Returns true if T is a Nullary_node.
template<typename T>
constexpr bool
is_nullary_node()
{
  return !traits::has_first<T>();
}

// Returns true if T is a unary node.
template<typename T>
constexpr bool
is_unary_node()
{
  return traits::has_first<T>()
      && !traits::has_second<T>();
}

// Returns true if T is a binary node.
template<typename T>
constexpr bool
is_binary_node()
{
  return traits::has_second<T>()
      && !traits::has_third<T>();
}

// Returns true if T is a ternary node.
template<typename T>
constexpr bool
is_ternary_node()
{
  return traits::has_first<T>()
      && traits::has_second<T>()
      && traits::has_third<T>();
}

// Returns true if T is a k-ary node.
template<typename T>
constexpr bool
is_kary_node()
{
  return traits::has_begin<T>() && traits::has_end<T>();
}

// -------------------------------------------------------------------------- //
//                        Reqiured term

// The Maybe template is typically used to declare node
// pointers within condition declarations.
//
//    if (Required<Var_decl> var = ...)
//
// This class contextually evaluates to `true` whenever
// it is initialized to a non-null, non-error value.
template<typename T>
struct Required
{
  Required()
    : ptr(nullptr)
  { }

  Required(T const* p)
    : ptr(p)
  { }

  // Returns true iff the term is valid.
  explicit operator bool() const { return is_valid_node(ptr); }

  // Returns the underlying term, even if it is an error or
  // empty term.
  T const* operator*() const { return ptr; }
  T const* operator->() const { return ptr; }

  bool is_error() const { return is_error_node(ptr); }
  bool is_empty() const { return is_empty_node(ptr); }

  T const* ptr;
};

// -------------------------------------------------------------------------- //
//                        Optional results

// The Optional template is typically used to declare node
// pointers within condition declarations.
//
//    if (Optional<Var_decl> var = ...)
//
// This class contextually evaluates to `true` whenever
// it is a non-error value. Note that the term may be empty.
template<typename T>
struct Optional
{
  Optional()
    : ptr(nullptr)
  { }

  Optional(T const* p)
    : ptr(p)
  { }

  // Returns true iff the term is valid or empty.
  explicit operator bool() const { return !is_error_node(ptr); }

  // Returns the underlying term, even if it is an error.
  T const* operator*() const { return ptr; }
  T const* operator->() const { return ptr; }

  bool is_error() const { return is_error_node(ptr); }
  bool is_empty() const { return is_empty_node(ptr); }

  T const* ptr;
};

// -------------------------------------------------------------------------- //
//                        Nonempty results

// The Nonempty template is typically used to declare node
// pointers within condition declarations.
//
//    if (Nonempty<Var_decl> var = ...)
//
// This class contextually evaluates to `true` whenever
// is non-empty. Note that error conditions are treated as
// valid results.
template<typename T>
struct Nonempty
{
  Nonempty()
    : ptr(nullptr)
  { }

  Nonempty(T const* p)
    : ptr(p)
  { }

  // Returns true iff the term is non-empty.
  explicit operator bool() const { return !is_empty_node(ptr); }

  // Returns the underlying term, even if it is a empty.
  T const* operator*() const { return ptr; }
  T const* operator->() const { return ptr; }

  bool is_error() const { return is_error_node(ptr); }
  bool is_empty() const { return is_empty_node(ptr); }

  T const* ptr;
};

} // namespace lingo

#endif
<commit_msg>Comments.<commit_after>// Copyright (c) 2015 Andrew Sutton
// All rights reserved

#ifndef LINGO_NODE_HPP
#define LINGO_NODE_HPP

#include "lingo/utility.hpp"

#include <cassert>
#include <vector>
#include <type_traits>

// This module provides defines a node abstraction for various
// kinds of trees used in different languages and facilities
// for working with those abstractions.

namespace lingo
{

// Returns the name of the object pointed to
// by t.
template<typename T>
inline String
get_node_name(T const* t)
{
  return type_str(*t);
}

// -------------------------------------------------------------------------- //
//                            Special values
//
// The following functions define special values of node pointers.

// Returns true if the node is empty.
template<typename T>
inline bool
is_empty_node(T const* t)
{
  return t == nullptr;
}

// Construct a node pointer that acts as an error value.
// The type of the node is explicitly given as a template
// argument.
template<typename T>
inline T*
make_error_node()
{
  return (T*)0x01;
}

// Returns true if `t` is an error node.
template<typename T>
inline bool
is_error_node(T const* t)
{
  return t == make_error_node<T>();
}

// Returns true if `t` is neither null nor an error.
template<typename T>
inline bool
is_valid_node(T const* t)
{
  return t && !is_error_node(t);
}

// -------------------------------------------------------------------------- //
//                        Dynamic type information

// Returns true if the object pointed to by `u` has
// the dynamic type `T`.
template<typename T, typename U>
inline bool
is(U const* u)
{
  return dynamic_cast<T const*>(u);
}

// Statically cast a pointer to a Node of type T to a
// pointer to a Node of type U. This is not a checked
// operation (except in debug mode).
//
// Note that this allows null and error nodes to be
// interpreted as nodes of the given type (as their
// values are considered common to all).
template<typename T, typename U>
inline T*
cast(U* u)
{
  lingo_assert(is_valid_node(u) ? is<T>(u) : true);
  return static_cast<T*>(u);
}

template<typename T, typename U>
inline T const*
cast(U const* u)
{
  assert(is_valid_node(u) ? is<T>(u) : true);
  return static_cast<T const*>(u);
}

// Returns `u` with type `T*` iff the object pointed
// to by `u` has dynamic type `T`.
template<typename T, typename U>
inline T*
as(U* u)
{
  return dynamic_cast<T*>(u);
}

template<typename T, typename U>
inline T const*
as(U const* u)
{
  return dynamic_cast<T const*>(u);
}

// Return a non-const pointer to the term. This is used
// to modify a term post-initializatoin (which should
// be rare).
template<typename T>
inline T*
modify(T const* t)
{
  return const_cast<T*>(t);
}

// -------------------------------------------------------------------------- //
//                              Concepts
//
// There are several concepts describing the kinds of nodes over
// which an algorithm can operate generically. The primary
// characterization of node types is based on their arity.
//
// ## The Node concept
//
// Every node in an abstract syntax tree must provide a static
// constexpr member, node_kind that statically defines the kind
// of node.
//
// ## Node arity
//
// Nodes with fixed arity have tuple-like member names (e.g.,
// first, second, third). These accessor members correspond to
// the sub-terms of each node. Accessor members may have different
// tpyes, and are not required to be nodes.
//
// A nullary node is a Node is an empty tuple, and has no
// accessor members. A unary node has only accessor member
// `first`. A binary node has only `first` and `second`.
// A ternary node has `first`, second`, and `third`.
//
// Note that more arity nodes can be defined if needed.
//
// A k-ary has k sub-terms, and that range of terms is accessed
// by its `begin()` and `end()` members. The types of these
// sub-terms are the same.

namespace traits
{

// A helper trait used to detect substitution failures.
template<typename T>
struct is_non_void
{
  static constexpr bool value = true;
};

template<>
struct is_non_void<void>
{
  static constexpr bool value = false;
};

// Detect the existince of the member t->first.
template<typename T>
struct first_type
{
  template<typename U> static auto f(U* p) -> decltype(p->first);
  static                      void f(...);

  using type = decltype(f(std::declval<T*>()));
};

// Detect the existence of the member t->second;
template<typename T>
struct second_type
{
  template<typename U> static auto f(U* p) -> decltype(p->second);
  static                      void f(...);

  using type = decltype(f(std::declval<T*>()));
};

// Detect the existence of the member t->third;
template<typename T>
struct third_type
{
  template<typename U> static auto f(U* p) -> decltype(p->third);
  static                      void f(...);

  using type = decltype(f(std::declval<T*>()));
};

// Detect the existence of the member t->begin().
template<typename T>
struct begin_type
{
  template<typename U> static auto f(U* p) -> decltype(p->begin());
  static                      void f(...);

  using type = decltype(f(std::declval<T*>()));
};

// Detect the existence of the member t->end().
template<typename T>
struct end_type
{
  template<typename U> static auto f(U* p) -> decltype(p->end());
  static                      void f(...);

  using type = decltype(f(std::declval<T*>()));
};

// Returns true when `T` has the member `first`.
template<typename T>
constexpr bool
has_first()
{
  return is_non_void<typename first_type<T>::type>::value;
}

// Returns true when `T` has the member `second`.
template<typename T>
constexpr bool
has_second()
{
  return is_non_void<typename second_type<T>::type>::value;
}

// Returns true when `T` has the member `third`.
template<typename T>
constexpr bool
has_third()
{
  return is_non_void<typename third_type<T>::type>::value;
}

// Returns true when `T` has the member `begin`.
template<typename T>
constexpr bool
has_begin()
{
  return is_non_void<typename begin_type<T>::type>::value;
}

// Returns true when `T` has the member `end`.
template<typename T>
constexpr bool
has_end()
{
  return is_non_void<typename end_type<T>::type>::value;
}

} // namesapce traits

// Returns true if T is a Nullary_node.
template<typename T>
constexpr bool
is_nullary_node()
{
  return !traits::has_first<T>();
}

// Returns true if T is a unary node.
template<typename T>
constexpr bool
is_unary_node()
{
  return traits::has_first<T>()
      && !traits::has_second<T>();
}

// Returns true if T is a binary node.
template<typename T>
constexpr bool
is_binary_node()
{
  return traits::has_second<T>()
      && !traits::has_third<T>();
}

// Returns true if T is a ternary node.
template<typename T>
constexpr bool
is_ternary_node()
{
  return traits::has_first<T>()
      && traits::has_second<T>()
      && traits::has_third<T>();
}

// Returns true if T is a k-ary node.
template<typename T>
constexpr bool
is_kary_node()
{
  return traits::has_begin<T>() && traits::has_end<T>();
}

// -------------------------------------------------------------------------- //
//                        Reqiured term

// The Maybe template is typically used to declare node
// pointers within condition declarations.
//
//    if (Required<Var_decl> var = ...)
//
// This class contextually evaluates to `true` whenever
// it is initialized to a non-null, non-error value.
template<typename T>
struct Required
{
  Required()
    : ptr(nullptr)
  { }

  Required(T const* p)
    : ptr(p)
  { }

  // Returns true iff the term is valid.
  explicit operator bool() const { return is_valid_node(ptr); }

  // Returns the underlying term, even if it is an error or
  // empty term.
  T const* operator*() const { return ptr; }
  T const* operator->() const { return ptr; }

  bool is_error() const { return is_error_node(ptr); }
  bool is_empty() const { return is_empty_node(ptr); }

  T const* ptr;
};

// -------------------------------------------------------------------------- //
//                        Optional results

// The Optional template is typically used to declare node
// pointers within condition declarations.
//
//    if (Optional<Var_decl> var = ...)
//
// This class contextually evaluates to `true` whenever
// it is a non-error value. Note that the term may be empty.
template<typename T>
struct Optional
{
  Optional()
    : ptr(nullptr)
  { }

  Optional(T const* p)
    : ptr(p)
  { }

  // Returns true iff the term is valid or empty.
  explicit operator bool() const { return !is_error_node(ptr); }

  // Returns the underlying term, even if it is an error.
  T const* operator*() const { return ptr; }
  T const* operator->() const { return ptr; }

  bool is_error() const { return is_error_node(ptr); }
  bool is_empty() const { return is_empty_node(ptr); }

  T const* ptr;
};

// -------------------------------------------------------------------------- //
//                        Nonempty results

// The Nonempty template is typically used to declare node
// pointers within condition declarations.
//
//    if (Nonempty<Var_decl> var = ...)
//
// This class contextually evaluates to `true` whenever
// is non-empty. Note that error conditions are treated as
// valid results.
template<typename T>
struct Nonempty
{
  Nonempty()
    : ptr(nullptr)
  { }

  Nonempty(T const* p)
    : ptr(p)
  { }

  // Returns true iff the term is non-empty.
  explicit operator bool() const { return !is_empty_node(ptr); }

  // Returns the underlying term, even if it is a empty.
  T const* operator*() const { return ptr; }
  T const* operator->() const { return ptr; }

  bool is_error() const { return is_error_node(ptr); }
  bool is_empty() const { return is_empty_node(ptr); }

  T const* ptr;
};

} // namespace lingo

#endif
<|endoftext|>


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

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

// appleseed.renderer headers.
#include "renderer/modeling/environmentedf/environmentedf.h"
#include "renderer/modeling/environmentshader/environmentshader.h"
#include "renderer/modeling/object/object.h"
#include "renderer/modeling/scene/assembly.h"
#include "renderer/modeling/scene/assemblyinstance.h"
#include "renderer/modeling/scene/objectinstance.h"
#include "renderer/modeling/scene/textureinstance.h"
#include "renderer/modeling/scene/visibilityflags.h"
#include "renderer/modeling/surfaceshader/physicalsurfaceshader.h"
#include "renderer/modeling/surfaceshader/surfaceshader.h"
#include "renderer/utility/bbox.h"

// appleseed.foundation headers.
#include "foundation/math/vector.h"
#include "foundation/utility/job/abortswitch.h"
#include "foundation/utility/foreach.h"

// Standard headers.
#include <set>

using namespace foundation;
using namespace std;

namespace renderer
{

//
// Scene class implementation.
//

namespace
{
    const UniqueID g_class_uid = new_guid();
}

UniqueID Scene::get_class_uid()
{
    return g_class_uid;
}

struct Scene::Impl
{
    UniqueID                        m_uid;
    auto_release_ptr<Camera>        m_camera;
    auto_release_ptr<Environment>   m_environment;
    EnvironmentEDFContainer         m_environment_edfs;
    EnvironmentShaderContainer      m_environment_shaders;
    auto_release_ptr<SurfaceShader> m_default_surface_shader;

    explicit Impl(Entity* parent)
      : m_environment_edfs(parent)
      , m_environment_shaders(parent)
      , m_default_surface_shader(
            PhysicalSurfaceShaderFactory().create(
                "default_surface_shader",
                ParamArray()))
    {
    }
};

Scene::Scene()
  : Entity(g_class_uid)
  , BaseGroup(this)
  , impl(new Impl(this))
  , m_has_render_data(false)
{
    set_name("scene");
}

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

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

void Scene::set_camera(auto_release_ptr<Camera> camera)
{
    impl->m_camera = camera;

    if (impl->m_camera.get())
        impl->m_camera->set_parent(this);
}

Camera* Scene::get_camera() const
{
    return impl->m_camera.get();
}

void Scene::set_environment(auto_release_ptr<Environment> environment)
{
    impl->m_environment = environment;

    if (impl->m_environment.get())
        impl->m_environment->set_parent(this);
}

Environment* Scene::get_environment() const
{
    return impl->m_environment.get();
}

EnvironmentEDFContainer& Scene::environment_edfs() const
{
    return impl->m_environment_edfs;
}

EnvironmentShaderContainer& Scene::environment_shaders() const
{
    return impl->m_environment_shaders;
}

SurfaceShader* Scene::get_default_surface_shader() const
{
    return impl->m_default_surface_shader.get();
}

GAABB3 Scene::compute_bbox() const
{
    const AssemblyInstanceContainer& instances = assembly_instances();
    return compute_parent_bbox<GAABB3>(instances.begin(), instances.end());
}

namespace
{
    bool assembly_instances_use_alpha_mapping(
        const AssemblyInstanceContainer&  assembly_instances,
        set<UniqueID>&                    visited_assemblies)
    {
        // Regarding transparency in the Tracer,
        // we only care about camera and shadow rays.
        const uint32 visibility_mask = VisibilityFlags::CameraRay | VisibilityFlags::ShadowRay;

        for (const_each<AssemblyInstanceContainer> i = assembly_instances; i; ++i)
        {
            // Retrieve the assembly instance.
            const AssemblyInstance& assembly_instance = *i;

            // Skip invisible assembly instances.
            if ((assembly_instance.get_vis_flags() & visibility_mask) == 0)
                continue;

            // Retrieve the assembly.
            const Assembly& assembly = assembly_instance.get_assembly();

            if (visited_assemblies.find(assembly.get_uid()) == visited_assemblies.end())
            {
                visited_assemblies.insert(assembly.get_uid());

                // Check the assembly contents.
                for (const_each<ObjectInstanceContainer> i = assembly.object_instances(); i; ++i)
                {
                    // Skip invisible object instances.
                    if ((i->get_vis_flags() & visibility_mask) == 0)
                        continue;

                    if (i->uses_alpha_mapping())
                        return true;
                }

                // Recurse into child assembly instances.
                if (assembly_instances_use_alpha_mapping(assembly.assembly_instances(), visited_assemblies))
                    return true;
            }
        }

        return false;
    }
}

bool Scene::uses_alpha_mapping() const
{
    set<UniqueID> visited_assemblies;
    return assembly_instances_use_alpha_mapping(assembly_instances(), visited_assemblies);
}

namespace
{
    template <typename EntityCollection>
    bool invoke_on_frame_begin(
        const Project&      project,
        EntityCollection&   entities,
        IAbortSwitch*       abort_switch)
    {
        bool success = true;

        for (each<EntityCollection> i = entities; i; ++i)
        {
            if (is_aborted(abort_switch))
                break;

            success = success && i->on_frame_begin(project, abort_switch);
        }

        return success;
    }

    template <typename EntityCollection>
    void invoke_on_frame_end(
        const Project&      project,
        EntityCollection&   entities)
    {
        for (each<EntityCollection> i = entities; i; ++i)
            i->on_frame_end(project);
    }
}

bool Scene::on_frame_begin(
    const Project&          project,
    IAbortSwitch*           abort_switch)
{
    create_render_data();

    bool success = true;

    if (impl->m_camera.get())
        success = success && impl->m_camera->on_frame_begin(project, abort_switch);

    success = success && invoke_on_frame_begin(project, texture_instances(), abort_switch);
    success = success && invoke_on_frame_begin(project, environment_edfs(), abort_switch);
    success = success && invoke_on_frame_begin(project, environment_shaders(), abort_switch);

    if (is_aborted(abort_switch))
        return success;

    if (impl->m_environment.get())
        success = success && impl->m_environment->on_frame_begin(project, abort_switch);

    success = success && invoke_on_frame_begin(project, assemblies(), abort_switch);
    success = success && invoke_on_frame_begin(project, assembly_instances(), abort_switch);

    return success;
}

void Scene::on_frame_end(const Project& project)
{
    invoke_on_frame_end(project, assembly_instances());
    invoke_on_frame_end(project, assemblies());

    if (impl->m_environment.get())
        impl->m_environment->on_frame_end(project);

    invoke_on_frame_end(project, environment_shaders());
    invoke_on_frame_end(project, environment_edfs());
    invoke_on_frame_end(project, texture_instances());

    if (impl->m_camera.get())
        impl->m_camera->on_frame_end(project);

    m_has_render_data = false;
}

void Scene::create_render_data()
{
    assert(!m_has_render_data);

    m_render_data.m_bbox = compute_bbox();

    if (m_render_data.m_bbox.is_valid())
    {
        m_render_data.m_center = m_render_data.m_bbox.center();
        m_render_data.m_radius = m_render_data.m_bbox.radius();
        m_render_data.m_diameter = m_render_data.m_bbox.diameter();
        m_render_data.m_safe_diameter = m_render_data.m_diameter * GScalar(1.01);
    }
    else
    {
        m_render_data.m_center = GVector3(0.0);
        m_render_data.m_radius = GScalar(0.0);
        m_render_data.m_diameter = GScalar(0.0);
        m_render_data.m_safe_diameter = GScalar(0.0);
    }

    m_has_render_data = true;
}

//
// SceneFactory class implementation.
//

auto_release_ptr<Scene> SceneFactory::create()
{
    return auto_release_ptr<Scene>(new Scene());
}

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

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

// appleseed.renderer headers.
#include "renderer/modeling/environmentedf/environmentedf.h"
#include "renderer/modeling/environmentshader/environmentshader.h"
#include "renderer/modeling/object/object.h"
#include "renderer/modeling/scene/assembly.h"
#include "renderer/modeling/scene/assemblyinstance.h"
#include "renderer/modeling/scene/objectinstance.h"
#include "renderer/modeling/scene/textureinstance.h"
#include "renderer/modeling/scene/visibilityflags.h"
#include "renderer/modeling/surfaceshader/physicalsurfaceshader.h"
#include "renderer/modeling/surfaceshader/surfaceshader.h"
#include "renderer/utility/bbox.h"

// appleseed.foundation headers.
#include "foundation/math/vector.h"
#include "foundation/utility/job/abortswitch.h"
#include "foundation/utility/foreach.h"

// Standard headers.
#include <set>

using namespace foundation;
using namespace std;

namespace renderer
{

//
// Scene class implementation.
//

namespace
{
    const UniqueID g_class_uid = new_guid();
}

UniqueID Scene::get_class_uid()
{
    return g_class_uid;
}

struct Scene::Impl
{
    UniqueID                        m_uid;
    auto_release_ptr<Camera>        m_camera;
    auto_release_ptr<Environment>   m_environment;
    EnvironmentEDFContainer         m_environment_edfs;
    EnvironmentShaderContainer      m_environment_shaders;
    auto_release_ptr<SurfaceShader> m_default_surface_shader;

    explicit Impl(Entity* parent)
      : m_environment_edfs(parent)
      , m_environment_shaders(parent)
      , m_default_surface_shader(
            PhysicalSurfaceShaderFactory().create(
                "default_surface_shader",
                ParamArray()))
    {
    }
};

Scene::Scene()
  : Entity(g_class_uid)
  , BaseGroup(this)
  , impl(new Impl(this))
  , m_has_render_data(false)
{
    set_name("scene");
}

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

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

void Scene::set_camera(auto_release_ptr<Camera> camera)
{
    impl->m_camera = camera;

    if (impl->m_camera.get())
        impl->m_camera->set_parent(this);
}

Camera* Scene::get_camera() const
{
    return impl->m_camera.get();
}

void Scene::set_environment(auto_release_ptr<Environment> environment)
{
    impl->m_environment = environment;

    if (impl->m_environment.get())
        impl->m_environment->set_parent(this);
}

Environment* Scene::get_environment() const
{
    return impl->m_environment.get();
}

EnvironmentEDFContainer& Scene::environment_edfs() const
{
    return impl->m_environment_edfs;
}

EnvironmentShaderContainer& Scene::environment_shaders() const
{
    return impl->m_environment_shaders;
}

SurfaceShader* Scene::get_default_surface_shader() const
{
    return impl->m_default_surface_shader.get();
}

GAABB3 Scene::compute_bbox() const
{
    const AssemblyInstanceContainer& instances = assembly_instances();
    return compute_parent_bbox<GAABB3>(instances.begin(), instances.end());
}

namespace
{
    bool assembly_instances_use_alpha_mapping(
        const AssemblyInstanceContainer&  assembly_instances,
        set<UniqueID>&                    visited_assemblies)
    {
        // Regarding transparency in the Tracer,
        // we only care about camera and shadow rays.
        const uint32 visibility_mask = VisibilityFlags::CameraRay | VisibilityFlags::ShadowRay;

        for (const_each<AssemblyInstanceContainer> i = assembly_instances; i; ++i)
        {
            // Retrieve the assembly instance.
            const AssemblyInstance& assembly_instance = *i;

            // Skip invisible assembly instances.
            if ((assembly_instance.get_vis_flags() & visibility_mask) == 0)
                continue;

            // Retrieve the assembly.
            const Assembly& assembly = assembly_instance.get_assembly();

            if (visited_assemblies.find(assembly.get_uid()) == visited_assemblies.end())
            {
                visited_assemblies.insert(assembly.get_uid());

                // Check the assembly contents.
                for (const_each<ObjectInstanceContainer> i = assembly.object_instances(); i; ++i)
                {
                    // Skip invisible object instances.
                    if ((i->get_vis_flags() & visibility_mask) == 0)
                        continue;

                    if (i->uses_alpha_mapping())
                        return true;
                }

                // Recurse into child assembly instances.
                if (assembly_instances_use_alpha_mapping(assembly.assembly_instances(), visited_assemblies))
                    return true;
            }
        }

        return false;
    }
}

bool Scene::uses_alpha_mapping() const
{
    set<UniqueID> visited_assemblies;
    return assembly_instances_use_alpha_mapping(assembly_instances(), visited_assemblies);
}

namespace
{
    template <typename EntityCollection>
    bool invoke_on_frame_begin(
        const Project&      project,
        EntityCollection&   entities,
        IAbortSwitch*       abort_switch)
    {
        bool success = true;

        for (each<EntityCollection> i = entities; i; ++i)
        {
            if (is_aborted(abort_switch))
                break;

            success = success && i->on_frame_begin(project, abort_switch);
        }

        return success;
    }

    template <typename EntityCollection>
    void invoke_on_frame_end(
        const Project&      project,
        EntityCollection&   entities)
    {
        for (each<EntityCollection> i = entities; i; ++i)
            i->on_frame_end(project);
    }
}

bool Scene::on_frame_begin(
    const Project&          project,
    IAbortSwitch*           abort_switch)
{
    create_render_data();

    bool success = true;

    if (impl->m_camera.get())
        success = success && impl->m_camera->on_frame_begin(project, abort_switch);

    success = success && invoke_on_frame_begin(project, texture_instances(), abort_switch);
    success = success && invoke_on_frame_begin(project, environment_edfs(), abort_switch);
    success = success && invoke_on_frame_begin(project, environment_shaders(), abort_switch);

    if (!is_aborted(abort_switch) && impl->m_environment.get())
        success = success && impl->m_environment->on_frame_begin(project, abort_switch);

    success = success && invoke_on_frame_begin(project, assemblies(), abort_switch);
    success = success && invoke_on_frame_begin(project, assembly_instances(), abort_switch);

    return success;
}

void Scene::on_frame_end(const Project& project)
{
    invoke_on_frame_end(project, assembly_instances());
    invoke_on_frame_end(project, assemblies());

    if (impl->m_environment.get())
        impl->m_environment->on_frame_end(project);

    invoke_on_frame_end(project, environment_shaders());
    invoke_on_frame_end(project, environment_edfs());
    invoke_on_frame_end(project, texture_instances());

    if (impl->m_camera.get())
        impl->m_camera->on_frame_end(project);

    m_has_render_data = false;
}

void Scene::create_render_data()
{
    assert(!m_has_render_data);

    m_render_data.m_bbox = compute_bbox();

    if (m_render_data.m_bbox.is_valid())
    {
        m_render_data.m_center = m_render_data.m_bbox.center();
        m_render_data.m_radius = m_render_data.m_bbox.radius();
        m_render_data.m_diameter = m_render_data.m_bbox.diameter();
        m_render_data.m_safe_diameter = m_render_data.m_diameter * GScalar(1.01);
    }
    else
    {
        m_render_data.m_center = GVector3(0.0);
        m_render_data.m_radius = GScalar(0.0);
        m_render_data.m_diameter = GScalar(0.0);
        m_render_data.m_safe_diameter = GScalar(0.0);
    }

    m_has_render_data = true;
}

//
// SceneFactory class implementation.
//

auto_release_ptr<Scene> SceneFactory::create()
{
    return auto_release_ptr<Scene>(new Scene());
}

}   // namespace renderer
<|endoftext|>


<commit_before>#include <iostream>
#include <stdio.h>
#include <limits.h>
#include <cstring>

#define MAX_ARR_SIZE 100000

int arrLength;
// Array containing the input data
int arr[MAX_ARR_SIZE];
// Array containing max
int resultArr[MAX_ARR_SIZE];
int resultLink[MAX_ARR_SIZE];

int findLongestSequence()
{
    int globalMaxIndex = 0;
    for(int i=0; i<arrLength; ++i)
    {
        for(int j=0; j<i; ++j)
        {
            //std::cout << "arr["<<j<<"]="<<arr[j]<<" < arr["<<i<<"]="<<arr[i]<<" && currentMax="<<currentMax<<" < resultArr["<<j<<"]="<<resultArr[j]<<" ";
            if(arr[j] < arr[i] && resultArr[i] <= resultArr[j])
            {
                //std::cout << "best";
                resultArr[i] = resultArr[j] + 1;
                resultLink[i] = j;
            }
            //std::cout << std::endl;
        }
        if(resultArr[globalMaxIndex] < resultArr[i])
            globalMaxIndex = i;
    }

    /*std::cout << "resultArr:  ";
    for(int i=0; i< arrLength; ++i)
        std::cout << resultArr[i] << " ";
    std::cout << std::endl << "resultLink: ";
    for(int i=0; i< arrLength; ++i)
        std::cout << resultLink[i] << " ";
    std::cout << std::endl << "index: " << globalMaxIndex << std::endl;*/
    return globalMaxIndex;
}

int main()
{
    arr[0] = INT_MIN;
    while( std::cin >> arrLength )
    {
        for(int i=0; i<arrLength; ++i)
        {
            scanf("%d", &arr[i]);
            resultArr[i] = 1;
            resultLink[i] = -1;
        }

        int linkIndex = findLongestSequence();

        std::cout << resultArr[linkIndex] << std::endl;
        while(linkIndex >= 0)
        {
            std::cout << linkIndex << " ";
            linkIndex = resultLink[linkIndex];
        }
        std::cout << std::endl;
    }
}<commit_msg>Imprived LIS but still to slow<commit_after>#include <iostream>
#include <stdio.h>
#include <limits.h>
#include <cstring>

#define MAX_ARR_SIZE 100000

int arrLength;
// Array containing the input data
int arr[MAX_ARR_SIZE];
// Array containing max
int resultArr[MAX_ARR_SIZE];
int resultLink[MAX_ARR_SIZE];

int findLongestSequence()
{
    int globalMaxIndex = 0;
    for(int i=arrLength-1; i>=0; --i)
    {
        for(int j=i+1; j<arrLength; ++j)
        {
            if(arr[j] > arr[i] && resultArr[i] <= resultArr[j])
            {
                resultArr[i] = resultArr[j] + 1;
                resultLink[i] = j;
            }
        }
        if(resultArr[globalMaxIndex] < resultArr[i])
            globalMaxIndex = i;
    }

    return globalMaxIndex;
}

int main()
{
    arr[0] = INT_MIN;
    while( std::cin >> arrLength )
    {
        for(int i=0; i<arrLength; ++i)
        {
            scanf("%d", &arr[i]);
            resultArr[i] = 1;
            resultLink[i] = -1;
        }

        int linkIndex = findLongestSequence();

        std::cout << resultArr[linkIndex] << std::endl;
        while(linkIndex >= 0)
        {
            std::cout << linkIndex << " ";
            linkIndex = resultLink[linkIndex];
        }
        std::cout << std::endl;
    }
}<|endoftext|>


<commit_before>#include "ros/ros.h"
#include "geometry_msgs/Pose.h"
#include "geometry_msgs/Twist.h"
#include "geometry_msgs/Wrench.h"
#include "uranus_dp/State.h"

class Controller
{
public:
    Controller()
    {
        outputPub = n.advertise<geometry_msgs::Wrench>("outputTopic", 1);

        // I don't know why, but 'this' is necessary here
        stateSub = n.subscribe("stateTopic", 1, &Controller::stateCallback, this);
        setpointSub = n.subscribe("setpointTopic", 1, &Controller::setpointCallback, this);
    }

    // stateCallback updates the private state variables when a new state message arrives.
    void stateCallback(const uranus_dp::State &state_msg)
    {
        // Copy pose values (position and orientation)
        eta[0] = state_msg.pose.position.x;
        eta[1] = state_msg.pose.position.y;
        eta[2] = state_msg.pose.position.z;
        eta[3] = state_msg.pose.orientation.x;
        eta[4] = state_msg.pose.orientation.y;
        eta[5] = state_msg.pose.orientation.z;
        eta[6] = state_msg.pose.orientation.w;

        // Copy twist values (linear and angular velocity)
        nu[0] = state_msg.twist.linear.x;
        nu[1] = state_msg.twist.linear.y;
        nu[2] = state_msg.twist.linear.z;
        nu[3] = state_msg.twist.angular.x;
        nu[4] = state_msg.twist.angular.y;
        nu[5] = state_msg.twist.angular.z;
    }

    // setpointCallback updates the private setpoint variable when a new setpoint message arrives.
    void setpointCallback(const geometry_msgs::Twist &setpoint_msg)
    {
        // Copy velocity setpoint values
        nu_sp[0] = setpoint_msg.linear.x;
        nu_sp[1] = setpoint_msg.linear.y;
        nu_sp[2] = setpoint_msg.linear.z;
        nu_sp[3] = setpoint_msg.angular.x;
        nu_sp[4] = setpoint_msg.angular.y;
        nu_sp[5] = setpoint_msg.angular.z;
    }

    // calculate contains the control algorithm, and calculates the control output based on the
    // current state and setpoint.
    void calculate(void)
    {
        // Only velocity control for now.

        // geometry_msgs::Twist velocityError = setpoint - state.velocity;

        geometry_msgs::Wrench output;
        output.force.x = 0;
        output.force.y = 0;
        output.force.z = 0;
        output.torque.x = 0;
        output.torque.y = 0;
        output.torque.z = 0;

        outputPub.publish(output);
    }
private:
    ros::NodeHandle n;

    ros::Publisher  outputPub;
    ros::Subscriber stateSub;
    ros::Subscriber setpointSub;

    // State as pose eta = [p q], and twist nu = [v omega]
    double eta [7];
    double nu [6];

    // Velocity setpoints nu_sp = [v_sp omega_sp]
    double nu_sp [6];
}; // End of class Controller

int main(int argc, char **argv)
{
    ros::init(argc, argv, "controller");

    Controller controllerObject;

    // Run the controller at 10 Hz
    ros::Rate loop_rate(10);
    while (ros::ok())
    {
        ros::spinOnce();

        controllerObject.calculate();

        loop_rate.sleep();
    }

    return 0;
}
<commit_msg>Added more necessary variables and some math<commit_after>#include "ros/ros.h"
#include "geometry_msgs/Pose.h"
#include "geometry_msgs/Twist.h"
#include "geometry_msgs/Wrench.h"
#include "uranus_dp/State.h"
#include <Eigen/Dense>
#include <cmath>

class Controller
{
public:
    Controller()
    {
        outputPub = n.advertise<geometry_msgs::Wrench>("outputTopic", 1);

        // I don't know why, but 'this' is necessary here
        stateSub = n.subscribe("stateTopic", 1, &Controller::stateCallback, this);
        setpointSub = n.subscribe("setpointTopic", 1, &Controller::setpointCallback, this);

        // Allocate dynamic member variables
        T = Eigen::MatrixXd(4,3);
        tau = Eigen::VectorXd(3);

        // Initialize controller gains
        K_p = Eigen::Matrix3d::Identity();
        K_d = Eigen::Matrix3d::Identity();
    }

    // stateCallback updates the private state variables when a new state message arrives.
    void stateCallback(const uranus_dp::State &state_msg)
    {
        // Copy position values
        p[0] = state_msg.pose.position.x;
        p[1] = state_msg.pose.position.y;
        p[2] = state_msg.pose.position.z;

        // Copy orientation values (quaternion)
        q.w()      = state_msg.pose.orientation.x;
        q.x() = state_msg.pose.orientation.y;
        q.y() = state_msg.pose.orientation.z;
        q.z() = state_msg.pose.orientation.w;

        // Copy linear velocity values
        v[0] = state_msg.twist.linear.x;
        v[1] = state_msg.twist.linear.y;
        v[2] = state_msg.twist.linear.z;

        // Copy angular velocity values
        omega[0] = state_msg.twist.angular.x;
        omega[1] = state_msg.twist.angular.y;
        omega[2] = state_msg.twist.angular.z;
    }

    // setpointCallback updates the private setpoint variable when a new setpoint message arrives.
    void setpointCallback(const geometry_msgs::Twist &setpoint_msg)
    {
        // Copy linear velocity setpoint values
        v_sp[0] = setpoint_msg.linear.x;
        v_sp[1] = setpoint_msg.linear.y;
        v_sp[2] = setpoint_msg.linear.z;

        // Copy angular velocity setpoint values
        omega_sp[0] = setpoint_msg.angular.x;
        omega_sp[1] = setpoint_msg.angular.y;
        omega_sp[2] = setpoint_msg.angular.z;
    }

    // calculate contains the control algorithm, and calculates the control output based on the
    // current state and setpoint.
    void calculate(void)
    {
        // Only orientation control for now.

        // Orientation error
        // q_err.x() = q_sp.x() - q.x();

        updateTransformationMatrices();

        // Control output
        // tau = - K_d * omega - K_p * T.transpose() * q_err;
        // tau = K_p * T.transpose() * q_err;
        // Eigen::Vector3d temp = T.transpose() * q_err;

        // Build output message
        geometry_msgs::Wrench output;
        output.force.x = 0;
        output.force.y = 0;
        output.force.z = 0;
        output.torque.x = tau(0);
        output.torque.y = tau(1);
        output.torque.z = tau(2);
        outputPub.publish(output);
    }

    void updateTransformationMatrices(void)
    {
        // Linear velocity transformation matrix
        R(0,0) = 1 - 2*(pow(q.y(),2) + pow(q.z(),2));
        R(0,1) = 2*(q.x()*q.y() - q.z()*q.w());
        R(0,2) = 2*(q.x()*q.z() - q.y()*q.w());

        R(1,0) = 2*(q.x()*q.y() + q.z()*q.w());
        R(1,1) = 1 - 2*(pow(q.x(),2) + pow(q.w(),2));
        R(1,2) = 2*(q.y()*q.z() + q.x()*q.w());

        R(2,0) = 2*(q.x()*q.z() + q.y()*q.w());
        R(2,1) = 2*(q.y()*q.z() + q.x()*q.w());
        R(2,2) = 1 - 2*(pow(q.x(),2) + pow(q.y(),2));

        // Angular velocity transformation matrix
        T(0,0) = -q.x();
        T(0,1) = -q.y();
        T(0,2) = -q.z();

        T(1,0) =  q.w();
        T(1,1) = -q.z();
        T(1,2) =  q.y();

        T(2,0) =  q.z();
        T(2,1) =  q.z();
        T(2,2) = -q.x();

        T(3,0) = -q.y();
        T(3,1) =  q.x();
        T(3,2) =  q.w();

        T *= 0.5;
    }
private:
    ros::NodeHandle n;

    ros::Publisher  outputPub;
    ros::Subscriber stateSub;
    ros::Subscriber setpointSub;

    // State
    Eigen::Vector3d p;     // Position
    Eigen::Quaterniond q;  // Orientation
    Eigen::Vector3d v;     // Linear velocity
    Eigen::Vector3d omega; // Angular velocity

    // Setpoints
    Eigen::Vector3d p_sp;     // Position
    Eigen::Quaterniond q_sp;  // Orientation
    Eigen::Vector3d v_sp;     // Linear velocity
    Eigen::Vector3d omega_sp; // Angular velocity

    // Errors
    Eigen::Vector3d p_err;     // Position
    Eigen::Quaterniond q_err;  // Orientation
    Eigen::Vector3d v_err;     // Linear velocity
    Eigen::Vector3d omega_err; // Angular velocity

    // Transformation matrices
    Eigen::Matrix3d R;
    Eigen::MatrixXd T;

    // Control output
    Eigen::VectorXd tau;

    // Controller gains
    Eigen::Matrix3d K_d;
    Eigen::Matrix3d K_p;
}; // End of class Controller

int main(int argc, char **argv)
{
    ros::init(argc, argv, "controller");

    Controller controllerObject;

    // Run the controller at 10 Hz
    ros::Rate loop_rate(10);
    while (ros::ok())
    {
        ros::spinOnce();

        controllerObject.calculate();

        loop_rate.sleep();
    }

    return 0;
}
<|endoftext|>


<commit_before>#include <Python.h>
#include <string>
#include <iostream>
#include <TTree.h>
#include <TFile.h>
#include <TChain.h>
#include <TLeaf.h>
#include <map>
#include <numpy/arrayobject.h>
#include <cassert>
#include <set>
struct TypeInfo{
    PyObject* nptype;
    int size;//in bytes
    TypeInfo(const TypeInfo& t):nptype(t.nptype),size(t.size){Py_INCREF(nptype);}
    TypeInfo(const char* nptype, int size):nptype(PyString_FromString(nptype)),size(size){};
    ~TypeInfo(){Py_DECREF(nptype);}
};

static std::map<std::string, TypeInfo> root_typemap;
//map roottype string to TypeInfo Object
void init_roottypemap(){
    using std::make_pair;
    //TODO: correct this one so it doesn't depend on system
    // from TTree doc
    // - C : a character string terminated by the 0 character
    // - B : an 8 bit signed integer (Char_t)
    // - b : an 8 bit unsigned integer (UChar_t)
    // - S : a 16 bit signed integer (Short_t)
    // - s : a 16 bit unsigned integer (UShort_t)
    // - I : a 32 bit signed integer (Int_t)
    // - i : a 32 bit unsigned integer (UInt_t)
    // - F : a 32 bit floating point (Float_t)
    // - D : a 64 bit floating point (Double_t)
    // - L : a 64 bit signed integer (Long64_t)
    // - l : a 64 bit unsigned integer (ULong64_t)
    // - O : [the letter 'o', not a zero] a boolean (Bool_t)
    // from numericdtype.py
    // # b -> boolean
    // # u -> unsigned integer
    // # i -> signed integer
    // # f -> floating point
    // # c -> complex
    // # M -> datetime
    // # m -> timedelta
    // # S -> string
    // # U -> Unicode string
    // # V -> record
    // # O -> Python object

    root_typemap.insert(make_pair("Char_t",TypeInfo("i1",1)));
    root_typemap.insert(make_pair("UChar_t",TypeInfo("u1",1)));

    root_typemap.insert(make_pair("Short_t",TypeInfo("i2",1)));
    root_typemap.insert(make_pair("UShort_t",TypeInfo("u2",1)));

    root_typemap.insert(make_pair("Int_t",TypeInfo("i4",4)));
    root_typemap.insert(make_pair("UInt_t",TypeInfo("u4",4)));

    root_typemap.insert(make_pair("Float_t",TypeInfo("f4",4)));
    root_typemap.insert(std::make_pair("Double_t",TypeInfo("f8",8)));

    root_typemap.insert(make_pair("Long64_t",TypeInfo("i4",8)));
    root_typemap.insert(make_pair("ULong64_t",TypeInfo("u4",8)));

    //this one is kinda special currently need to read c-api on exacly how numpy and root store bool
    //but int seems to work
    root_typemap.insert(make_pair("Bool_t",TypeInfo("i4",4)));
}

TypeInfo* convert_roottype(const std::string& t){
    std::map<std::string, TypeInfo>::iterator it = root_typemap.find(t);
    if(it==root_typemap.end()){
        std::string msg = "Unknown root type: "+t;
        PyErr_SetString(PyExc_RuntimeError,msg.c_str());
        return NULL;
    }
    return &(it->second);
}

struct LeafInfo{
    std::string name;
    TypeInfo* type;
    std::string root_type;
    char payload[64];//reserve for payload
    LeafInfo():name(),type(){}
    LeafInfo(const std::string& name,const std::string& root_type):name(name),root_type(root_type),type(0){};
    std::string repr(){
        return name + "("+root_type+")";
    }
};
//return all branch name from tree
std::vector<std::string> get_branchnames(TTree& tree){
    TObjArray* branches = (TObjArray*)tree.GetListOfBranches();
    std::vector<std::string> ret;
    for(int i=0;i<branches->GetEntries();++i){
        TBranch* thisBranch = dynamic_cast<TBranch*>(branches->At(i));
        const char* tmp = thisBranch->GetName();
        std::string str(tmp);
        ret.push_back(tmp);
    }
    return ret;
}

//vector unique with order preservation(just get the first one) O(nlogn)
std::vector<std::string> vector_unique(const std::vector<std::string>& org){
    using namespace std;
    set<string> myset;
    myset.insert(org.begin(),org.end());
    vector<string> ret;
    for(int i=0;i<org.size();i++){
        set<string>::iterator it = myset.find(org[i]);
        if(it!=myset.end()){
            myset.erase(it);
            ret.push_back(org[i]);
        }
    }
    return ret;
}

//get list of leafinfo from tree
//if branches is not empty, only the branches specified in branches will be used
//otherwise it will automatically list all the branches of the first tree in chain
std::vector<LeafInfo*> get_leafinfo(TTree& tree,const std::vector<std::string>& branches){

    using namespace std;
    vector<string> branchNames;
    std::vector<LeafInfo*> ret;
    if(branches.size()==0) branchNames = get_branchnames(tree);
    else branchNames = vector_unique(branches);

    for(int i=0;i<branchNames.size();i++){
        TBranch* thisBranch = dynamic_cast<TBranch*>(tree.GetBranch(branchNames[i].c_str()));
        std::string roottype("Float_t");
        if(thisBranch!=0){
            TObjArray* leaves = thisBranch->GetListOfLeaves();
            assert(leaves!=0);
            TLeaf* thisLeaf = dynamic_cast<TLeaf*>(leaves->At(0));
            assert(thisLeaf!=0);
            roottype = thisLeaf->GetTypeName();
        }else{
            std::cout << "Warning: branch not found in the first tree(assume type of Float_t)" << branchNames[i] << std::endl;
        }
        //need to set branch address at tree level because TChain will fail if branch is set at the first tree
        LeafInfo* li = new LeafInfo(thisBranch->GetName(),roottype);
        tree.SetBranchAddress(thisBranch->GetName(),&(li->payload));
        ret.push_back(li);
    }
    return ret;
}
//helper function for building numpy descr
PyObject* build_numpy_descr(const std::vector<LeafInfo*>& lis){

    PyObject* mylist = PyList_New(0);
    for(int i=0;i<lis.size();++i){
        PyObject* pyname = PyString_FromString(lis[i]->name.c_str());
        TypeInfo* ti = convert_roottype(lis[i]->root_type);
        lis[i]->type = ti;
        if(ti==0) return NULL;
        PyObject* pytype = ti->nptype;

        Py_INCREF(pytype);
        PyObject* nt_tuple = PyTuple_New(2);
        PyTuple_SetItem(nt_tuple,0,pyname);
        PyTuple_SetItem(nt_tuple,1,pytype);

        PyList_Append(mylist,nt_tuple);
    }
    return mylist;
}

//convert all leaf specified in lis to numpy structured array
PyObject* read_helper(TTree& chain, std::vector<LeafInfo*>& lis){
    int numEntries = chain.GetEntries();
    PyObject* numpy_descr = build_numpy_descr(lis);
    if(numpy_descr==0){return NULL;}

    //build the array
    PyArray_Descr* descr;
    PyArray_DescrConverter(numpy_descr,&descr);
    Py_DECREF(numpy_descr);

    npy_intp dims[1];
    dims[0]=numEntries;

    PyArrayObject* array = (PyArrayObject*)PyArray_SimpleNewFromDescr(1,dims,descr);

    //assume numpy array is contiguous
    char* current = (char*)PyArray_DATA(array);
    //now put stuff in array
    for(int iEntry=0;iEntry<numEntries;++iEntry){
        chain.GetEntry(iEntry);
        for(int ileaf=0;ileaf<lis.size();++ileaf){
            //cout << *(int*)(lis[ileaf]->payload) << " " << *(float*)(lis[ileaf]->payload) << endl;
            int size = lis[ileaf]->type->size;
            memcpy((void*)current,(void*)lis[ileaf]->payload,size);
            current+=size;
        }
    }
    //return Py_BuildValue("i",numEntries);
    return (PyObject*)array;
}

//convert list of string to vector of string
//if los is just a string vos will be filled with that string
//if los is null or PyNone it do nothing to vos and return OK;
int los2vos(PyObject* los, std::vector<std::string>& vos){
    int ret=1;
    if(los==NULL){
        //do nothing
    }
    else if(los==Py_None){
        //do nothing
    }
    else if(PyString_Check(los)){
        char* tmp = PyString_AsString(los);
        vos.push_back(tmp);
    }else if(PyList_Check(los)){
        int len = PyList_Size(los);
        for(int i=0;i<len;i++){
            PyObject* s = PyList_GetItem(los,i);
            if(!s){return NULL;}
            char* tmp = PyString_AsString(s);
            if(!tmp){return NULL;}
            std::string str(tmp);
            vos.push_back(tmp);
        }
    }else{
        ret=NULL;
    }
    return ret;
}

/**
* loadTTree from PyObject if fnames is list of string then it load every pattern in fnames
* if fnames is just a string then it load just one
* caller is responsible to delete the return value
* null is return if fnames is not a valid python object(either list of string or string)
*/
TTree* loadTree(PyObject* fnames, const char* treename){
    using namespace std;
    vector<string> vs;
    if(!los2vos(fnames,vs)){return NULL;}
    TChain* chain = new TChain(treename);
    for(int i=0;i<vs.size();++i){chain->Add(vs[i].c_str());}
    return dynamic_cast<TTree*>(chain);
}

PyObject* root2array(PyObject *self, PyObject *args, PyObject* keywords){
    using namespace std;
    PyObject* fnames;
    char* treename_;
    PyObject* branches_=NULL;
    PyObject* array=NULL;
    static const char* keywordslist[] = {"fname","treename","branches",NULL};

    if(!PyArg_ParseTupleAndKeywords(args,keywords,"Os|O",const_cast<char **>(keywordslist),&fnames,&treename_,&branches_)){
        return NULL;
    }

    vector<string> branches;
    if(!los2vos(branches_,branches)){return NULL;}

    TTree* chain = loadTree(fnames,treename_);
    if(!chain){return NULL;}

    int numEntries = chain->GetEntries();
    if(numEntries==0){
        PyErr_SetString(PyExc_TypeError,"Empty Tree");
        return NULL;
    }

    vector<LeafInfo*> lis =  get_leafinfo(*chain,branches);

    array = read_helper(*chain, lis);

    //don't switch these two lines because lis[i] contains payload
    delete chain;
    for(int i=0;i<lis.size();i++){delete lis[i];}

    return (PyObject*)array;
}

static PyMethodDef methods[] = {
    {"root2array",  (PyCFunction)root2array, METH_VARARGS|METH_KEYWORDS,
    "root2array(fnames,treename,branches=None)\n"
    "convert tree treename in root files specified in fnames to numpy structured array\n"
    "------------------\n"
    "return numpy array\n"
    "fnames: list of string or string. Root file name patterns. Anything that works with TChain.Add is accepted\n"
    "treename: name of tree to convert to numpy array\n"
    "branches(optional): list of string for branch name to be extracted from tree.\n"
    "\tIf branches is not specified or is none or is empty, all from the first treebranches are extracted\n"
    "\tIf branches contains duplicate branches, only the first one is used.\n"
    "\n"
    "Caveat: This should not matter for most use cases. But, due to the way TChain works, if the trees specified in the input files have different\n"
    "structures, only the branch in the first tree will be automatically extracted. You can work around this by either reordering the input file or\n"
    "specify the branches manually.\n"
    "------------------\n"
    "Ex:\n"
    "root2array('a.root','mytree')#read all branches from tree named mytree from a.root\n\n"
    "root2array('a*.root','mytree')#read all branches from tree named mytree from a*.root\n\n"
    "root2array(['a*.root','b*.root'],'mytree')#read all branches from tree named mytree from a*.root and b*.root\n\n"
    "root2array('a.root','mytree','x')#read branch x from tree named mytree from a.root(useful if memory usage matters)\n\n"
    "root2array('a.root','mytree',['x','y'])#read branch x and y from tree named mytree from a.root\n"
    },
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

void cleanup(){
    //do nothing
}

PyMODINIT_FUNC
initcroot_numpy(void)
{
    import_array();
    init_roottypemap();
    (void) Py_InitModule("croot_numpy", methods);
    //import_array();
}
<commit_msg>fix the wrong type convertion for Long_t and ULong_t<commit_after>#include <Python.h>
#include <string>
#include <iostream>
#include <TTree.h>
#include <TFile.h>
#include <TChain.h>
#include <TLeaf.h>
#include <map>
#include <numpy/arrayobject.h>
#include <cassert>
#include <set>
struct TypeInfo{
    PyObject* nptype;
    int size;//in bytes
    TypeInfo(const TypeInfo& t):nptype(t.nptype),size(t.size){Py_INCREF(nptype);}
    TypeInfo(const char* nptype, int size):nptype(PyString_FromString(nptype)),size(size){};
    ~TypeInfo(){Py_DECREF(nptype);}
};

static std::map<std::string, TypeInfo> root_typemap;
//map roottype string to TypeInfo Object
void init_roottypemap(){
    using std::make_pair;
    //TODO: correct this one so it doesn't depend on system
    // from TTree doc
    // - C : a character string terminated by the 0 character
    // - B : an 8 bit signed integer (Char_t)
    // - b : an 8 bit unsigned integer (UChar_t)
    // - S : a 16 bit signed integer (Short_t)
    // - s : a 16 bit unsigned integer (UShort_t)
    // - I : a 32 bit signed integer (Int_t)
    // - i : a 32 bit unsigned integer (UInt_t)
    // - F : a 32 bit floating point (Float_t)
    // - D : a 64 bit floating point (Double_t)
    // - L : a 64 bit signed integer (Long64_t)
    // - l : a 64 bit unsigned integer (ULong64_t)
    // - O : [the letter 'o', not a zero] a boolean (Bool_t)
    // from numericdtype.py
    // # b -> boolean
    // # u -> unsigned integer
    // # i -> signed integer
    // # f -> floating point
    // # c -> complex
    // # M -> datetime
    // # m -> timedelta
    // # S -> string
    // # U -> Unicode string
    // # V -> record
    // # O -> Python object

    root_typemap.insert(make_pair("Char_t",TypeInfo("i1",1)));
    root_typemap.insert(make_pair("UChar_t",TypeInfo("u1",1)));

    root_typemap.insert(make_pair("Short_t",TypeInfo("i2",1)));
    root_typemap.insert(make_pair("UShort_t",TypeInfo("u2",1)));

    root_typemap.insert(make_pair("Int_t",TypeInfo("i4",4)));
    root_typemap.insert(make_pair("UInt_t",TypeInfo("u4",4)));

    root_typemap.insert(make_pair("Float_t",TypeInfo("f4",4)));
    root_typemap.insert(std::make_pair("Double_t",TypeInfo("f8",8)));

    root_typemap.insert(make_pair("Long64_t",TypeInfo("i8",8)));
    root_typemap.insert(make_pair("ULong64_t",TypeInfo("u8",8)));

    //this one is kinda special currently need to read c-api on exacly how numpy and root store bool
    //but int seems to work
    root_typemap.insert(make_pair("Bool_t",TypeInfo("i4",4)));
}

TypeInfo* convert_roottype(const std::string& t){
    std::map<std::string, TypeInfo>::iterator it = root_typemap.find(t);
    if(it==root_typemap.end()){
        std::string msg = "Unknown root type: "+t;
        PyErr_SetString(PyExc_RuntimeError,msg.c_str());
        return NULL;
    }
    return &(it->second);
}

struct LeafInfo{
    std::string name;
    TypeInfo* type;
    std::string root_type;
    char payload[64];//reserve for payload
    LeafInfo():name(),type(){}
    LeafInfo(const std::string& name,const std::string& root_type):name(name),root_type(root_type),type(0){};
    std::string repr(){
        return name + "("+root_type+")";
    }
};
//return all branch name from tree
std::vector<std::string> get_branchnames(TTree& tree){
    TObjArray* branches = (TObjArray*)tree.GetListOfBranches();
    std::vector<std::string> ret;
    for(int i=0;i<branches->GetEntries();++i){
        TBranch* thisBranch = dynamic_cast<TBranch*>(branches->At(i));
        const char* tmp = thisBranch->GetName();
        std::string str(tmp);
        ret.push_back(tmp);
    }
    return ret;
}

//vector unique with order preservation(just get the first one) O(nlogn)
std::vector<std::string> vector_unique(const std::vector<std::string>& org){
    using namespace std;
    set<string> myset;
    myset.insert(org.begin(),org.end());
    vector<string> ret;
    for(int i=0;i<org.size();i++){
        set<string>::iterator it = myset.find(org[i]);
        if(it!=myset.end()){
            myset.erase(it);
            ret.push_back(org[i]);
        }
    }
    return ret;
}

//get list of leafinfo from tree
//if branches is not empty, only the branches specified in branches will be used
//otherwise it will automatically list all the branches of the first tree in chain
std::vector<LeafInfo*> get_leafinfo(TTree& tree,const std::vector<std::string>& branches){

    using namespace std;
    vector<string> branchNames;
    std::vector<LeafInfo*> ret;
    if(branches.size()==0) branchNames = get_branchnames(tree);
    else branchNames = vector_unique(branches);

    for(int i=0;i<branchNames.size();i++){
        TBranch* thisBranch = dynamic_cast<TBranch*>(tree.GetBranch(branchNames[i].c_str()));
        std::string roottype("Float_t");
        if(thisBranch!=0){
            TObjArray* leaves = thisBranch->GetListOfLeaves();
            assert(leaves!=0);
            TLeaf* thisLeaf = dynamic_cast<TLeaf*>(leaves->At(0));
            assert(thisLeaf!=0);
            roottype = thisLeaf->GetTypeName();
        }else{
            std::cout << "Warning: branch not found in the first tree(assume type of Float_t)" << branchNames[i] << std::endl;
        }
        //need to set branch address at tree level because TChain will fail if branch is set at the first tree
        LeafInfo* li = new LeafInfo(thisBranch->GetName(),roottype);
        tree.SetBranchAddress(thisBranch->GetName(),&(li->payload));
        ret.push_back(li);
    }
    return ret;
}
//helper function for building numpy descr
PyObject* build_numpy_descr(const std::vector<LeafInfo*>& lis){

    PyObject* mylist = PyList_New(0);
    for(int i=0;i<lis.size();++i){
        PyObject* pyname = PyString_FromString(lis[i]->name.c_str());
        TypeInfo* ti = convert_roottype(lis[i]->root_type);
        lis[i]->type = ti;
        if(ti==0) return NULL;
        PyObject* pytype = ti->nptype;

        Py_INCREF(pytype);
        PyObject* nt_tuple = PyTuple_New(2);
        PyTuple_SetItem(nt_tuple,0,pyname);
        PyTuple_SetItem(nt_tuple,1,pytype);

        PyList_Append(mylist,nt_tuple);
    }
    return mylist;
}

//convert all leaf specified in lis to numpy structured array
PyObject* read_helper(TTree& chain, std::vector<LeafInfo*>& lis){
    int numEntries = chain.GetEntries();
    PyObject* numpy_descr = build_numpy_descr(lis);
    if(numpy_descr==0){return NULL;}

    //build the array
    PyArray_Descr* descr;
    PyArray_DescrConverter(numpy_descr,&descr);
    Py_DECREF(numpy_descr);

    npy_intp dims[1];
    dims[0]=numEntries;

    PyArrayObject* array = (PyArrayObject*)PyArray_SimpleNewFromDescr(1,dims,descr);

    //assume numpy array is contiguous
    char* current = (char*)PyArray_DATA(array);
    //now put stuff in array
    for(int iEntry=0;iEntry<numEntries;++iEntry){
        chain.GetEntry(iEntry);
        for(int ileaf=0;ileaf<lis.size();++ileaf){
            //cout << *(int*)(lis[ileaf]->payload) << " " << *(float*)(lis[ileaf]->payload) << endl;
            int size = lis[ileaf]->type->size;
            memcpy((void*)current,(void*)lis[ileaf]->payload,size);
            current+=size;
        }
    }
    //return Py_BuildValue("i",numEntries);
    return (PyObject*)array;
}

//convert list of string to vector of string
//if los is just a string vos will be filled with that string
//if los is null or PyNone it do nothing to vos and return OK;
int los2vos(PyObject* los, std::vector<std::string>& vos){
    int ret=1;
    if(los==NULL){
        //do nothing
    }
    else if(los==Py_None){
        //do nothing
    }
    else if(PyString_Check(los)){
        char* tmp = PyString_AsString(los);
        vos.push_back(tmp);
    }else if(PyList_Check(los)){
        int len = PyList_Size(los);
        for(int i=0;i<len;i++){
            PyObject* s = PyList_GetItem(los,i);
            if(!s){return NULL;}
            char* tmp = PyString_AsString(s);
            if(!tmp){return NULL;}
            std::string str(tmp);
            vos.push_back(tmp);
        }
    }else{
        ret=NULL;
    }
    return ret;
}

/**
* loadTTree from PyObject if fnames is list of string then it load every pattern in fnames
* if fnames is just a string then it load just one
* caller is responsible to delete the return value
* null is return if fnames is not a valid python object(either list of string or string)
*/
TTree* loadTree(PyObject* fnames, const char* treename){
    using namespace std;
    vector<string> vs;
    if(!los2vos(fnames,vs)){return NULL;}
    TChain* chain = new TChain(treename);
    for(int i=0;i<vs.size();++i){chain->Add(vs[i].c_str());}
    return dynamic_cast<TTree*>(chain);
}

PyObject* root2array(PyObject *self, PyObject *args, PyObject* keywords){
    using namespace std;
    PyObject* fnames;
    char* treename_;
    PyObject* branches_=NULL;
    PyObject* array=NULL;
    static const char* keywordslist[] = {"fname","treename","branches",NULL};

    if(!PyArg_ParseTupleAndKeywords(args,keywords,"Os|O",const_cast<char **>(keywordslist),&fnames,&treename_,&branches_)){
        return NULL;
    }

    vector<string> branches;
    if(!los2vos(branches_,branches)){return NULL;}

    TTree* chain = loadTree(fnames,treename_);
    if(!chain){return NULL;}

    int numEntries = chain->GetEntries();
    if(numEntries==0){
        PyErr_SetString(PyExc_TypeError,"Empty Tree");
        return NULL;
    }

    vector<LeafInfo*> lis =  get_leafinfo(*chain,branches);

    array = read_helper(*chain, lis);

    //don't switch these two lines because lis[i] contains payload
    delete chain;
    for(int i=0;i<lis.size();i++){delete lis[i];}

    return (PyObject*)array;
}

static PyMethodDef methods[] = {
    {"root2array",  (PyCFunction)root2array, METH_VARARGS|METH_KEYWORDS,
    "root2array(fnames,treename,branches=None)\n"
    "convert tree treename in root files specified in fnames to numpy structured array\n"
    "------------------\n"
    "return numpy array\n"
    "fnames: list of string or string. Root file name patterns. Anything that works with TChain.Add is accepted\n"
    "treename: name of tree to convert to numpy array\n"
    "branches(optional): list of string for branch name to be extracted from tree.\n"
    "\tIf branches is not specified or is none or is empty, all from the first treebranches are extracted\n"
    "\tIf branches contains duplicate branches, only the first one is used.\n"
    "\n"
    "Caveat: This should not matter for most use cases. But, due to the way TChain works, if the trees specified in the input files have different\n"
    "structures, only the branch in the first tree will be automatically extracted. You can work around this by either reordering the input file or\n"
    "specify the branches manually.\n"
    "------------------\n"
    "Ex:\n"
    "root2array('a.root','mytree')#read all branches from tree named mytree from a.root\n\n"
    "root2array('a*.root','mytree')#read all branches from tree named mytree from a*.root\n\n"
    "root2array(['a*.root','b*.root'],'mytree')#read all branches from tree named mytree from a*.root and b*.root\n\n"
    "root2array('a.root','mytree','x')#read branch x from tree named mytree from a.root(useful if memory usage matters)\n\n"
    "root2array('a.root','mytree',['x','y'])#read branch x and y from tree named mytree from a.root\n"
    },
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

void cleanup(){
    //do nothing
}

PyMODINIT_FUNC
initcroot_numpy(void)
{
    import_array();
    init_roottypemap();
    (void) Py_InitModule("croot_numpy", methods);
    //import_array();
}
<|endoftext|>