<commit_before>/* dtkComposerNodeRemote.cpp ---
 *
 * Author: Nicolas Niclausse, Inria.
 * Created: 2012/04/03 15:19:20
 */

/* Commentary:
 *
 */

/* Change log:
 *
 */

#include "dtkComposerMetatype.h"

#include "dtkComposerNodeRemote.h"
#include "dtkComposerTransmitterEmitter.h"
#include "dtkComposerTransmitterReceiver.h"
#include "dtkComposerTransmitterVariant.h"

#include <dtkDistributed/dtkDistributedController.h>
#include <dtkDistributed/dtkDistributedCommunicator.h>
#include <dtkDistributed/dtkDistributedCommunicatorTcp.h>
#include <dtkDistributed/dtkDistributedSlave.h>

#include <dtkCore/dtkAbstractDataFactory.h>
#include <dtkCore/dtkGlobal.h>

#include <dtkJson>

#include <dtkMath/dtkMath.h>

#include <dtkLog/dtkLog.h>

// /////////////////////////////////////////////////////////////////
// dtkComposerNodeRemotePrivate interface
// /////////////////////////////////////////////////////////////////

class dtkComposerNodeRemotePrivate
{

public:
    dtkComposerTransmitterReceiver<QString> jobid_receiver;

public:
    QDomDocument composition;
    QByteArray current_hash;
    QByteArray last_sent_hash;

public:
    dtkDistributedController *controller;

public:
    dtkDistributedCommunicator *communicator;
    dtkDistributedCommunicatorTcp *server;

public:
    dtkDistributedSlave *slave;

public:
    QString jobid;
    QString last_jobid;

public:
    QString title;

};

// /////////////////////////////////////////////////////////////////
// dtkComposerNodeRemote implementation
// /////////////////////////////////////////////////////////////////

dtkComposerNodeRemote::dtkComposerNodeRemote(void) : QObject(), dtkComposerNodeComposite(), d(new dtkComposerNodeRemotePrivate)
{

    this->appendReceiver(&(d->jobid_receiver));
    this->setInputLabelHint("jobid", 0);

    d->controller = NULL;
    d->slave      = NULL;
    d->server     = NULL;
    d->title      = "Remote";
}

dtkComposerNodeRemote::~dtkComposerNodeRemote(void)
{
    delete d->server;
    delete d;

    d = NULL;
}

QString dtkComposerNodeRemote::type(void)
{
    return "remote";
}

QString dtkComposerNodeRemote::titleHint(void)
{
    return d->title;
}

void dtkComposerNodeRemote::setComposition(QDomDocument document)
{
    d->composition  = document;
    d->current_hash = QCryptographicHash::hash(d->composition.toByteArray(),QCryptographicHash::Md5);
}

void dtkComposerNodeRemote::setController(dtkDistributedController *controller)
{
    if (d->jobid.isEmpty()) {
        dtkWarn() <<  "No job id while setting controller !";
    }

    d->controller = controller;
}

void dtkComposerNodeRemote::setSlave(dtkDistributedSlave *slave)
{
    d->slave = slave;
}

void dtkComposerNodeRemote::setCommunicator(dtkDistributedCommunicator *c)
{
    d->communicator = c;
}

void dtkComposerNodeRemote::setJob(QString jobid)
{
    d->jobid = jobid;
}

bool dtkComposerNodeRemote::isSlave(void)
{
    if (d->slave)
        return true;
    return false;
}

void dtkComposerNodeRemote::onJobStarted(QString jobid)
{
    if (jobid == d->jobid) {
        QObject::disconnect( dtkDistributedController::instance(), SIGNAL(jobStarted(QString)), this, SLOT(onJobStarted(QString)));
    } else {
        dtkDebug() << "A job has started, but it's not ours, keep waiting " << d->jobid << jobid ;
    }
}

void dtkComposerNodeRemote::begin(void)
{
    if (!d->slave && !d->jobid_receiver.isEmpty()) {
        // we are running on the controller but controller and job was
        // not drag&dropped, get job from transmitter and main
        // controller instance
        d->jobid = *d->jobid_receiver.data();
        d->controller = dtkDistributedController::instance();
        if (!d->controller->is_running(d->jobid)) {
            dtkDebug() << " Wait for job to start, jobid is " << d->jobid;
            QEventLoop loop;
            this->connect(d->controller, SIGNAL(jobStarted(QString)), this, SLOT(onJobStarted(QString)),Qt::DirectConnection);
            loop.connect(d->controller, SIGNAL(jobStarted(QString)), &loop, SLOT(quit()));
            loop.connect(qApp, SIGNAL(aboutToQuit()), &loop, SLOT(quit()));
            loop.exec();
            dtkTrace() << "waiting event loop ended, job has started" << d->jobid;
        } else
            dtkDebug() << " Job already running, go " << d->jobid;
    }

    if (d->controller) {
        if (!d->server) {
            d->server = new dtkDistributedCommunicatorTcp;
            d->server->connectToHost(d->controller->socket(d->jobid)->peerAddress().toString(),d->controller->socket(d->jobid)->peerPort());
            if (d->server->socket()->waitForConnected()) {
                dtkDebug() << "Connected to server";
            } else {
                dtkError() << "Can't connect to server";
                return;
            }
        }
        dtkDistributedMessage *msg;
        if (d->last_jobid != d->jobid) {
            msg = new dtkDistributedMessage(dtkDistributedMessage::SETRANK,d->jobid,dtkDistributedMessage::CONTROLLER_RUN_RANK );
            d->server->socket()->sendRequest(msg);
            delete msg;
            d->last_jobid=d->jobid;
            // the job has changed, so we must send the composition even if it has not changed
            d->last_sent_hash.clear();
        }
        if (d->current_hash != d->last_sent_hash){
            // send sub-composition to rank 0 on remote node
            QByteArray compo = d->composition.toByteArray();
            dtkDebug() << "running node remote begin statement on controller, send composition of size " << compo.size();
            msg = new dtkDistributedMessage(dtkDistributedMessage::DATA,d->jobid,0,compo.size(), "xml", compo );
            d->last_sent_hash=d->current_hash;
        } else {
            dtkDebug() << "composition hash hasn't changed, send 'not-modified' to slave";
            msg = new dtkDistributedMessage(dtkDistributedMessage::DATA,d->jobid,0,d->current_hash.size(), "not-modified", d->current_hash );
        }
        d->server->socket()->sendRequest(msg);
        delete msg;
        dtkDebug() << "composition sent";
        // then send transmitters data
        int max  = dtkComposerNodeComposite::receivers().count();
        for (int i = 1; i < max; i++) {
            dtkComposerTransmitterVariant *t = dynamic_cast<dtkComposerTransmitterVariant *>(dtkComposerNodeComposite::receivers().at(i));
            // FIXME: use our own transmitter variant list (see control nodes)
            QByteArray array = t->dataToByteArray();
            dtkDebug() << "sending transmitter" << i << "of size" << array.size();
            msg = new dtkDistributedMessage(dtkDistributedMessage::DATA, d->jobid, 0, array.size(), "qvariant", array);
            d->server->socket()->sendRequest(msg);
            delete msg;
        }
        d->server->socket()->waitForBytesWritten();
    } else {
        // running on the slave, receive data and set transmitters
        int max  = dtkComposerNodeComposite::receivers().count();
        int size = d->communicator->size();
        for (int i = 1; i < max; i++) {
            dtkComposerTransmitterVariant *t = dynamic_cast<dtkComposerTransmitterVariant *>(dtkComposerNodeComposite::receivers().at(i));
            if (d->communicator->rank() == 0) {

                if (d->slave->communicator()->socket()->bytesAvailable()) {
                    dtkDebug() << "data already available, parse" ;

                } else {
                    if (!(d->slave->communicator()->socket()->waitForReadyRead(60000))) {
                        dtkError() << "No data received from server after 1mn, abort " ;
                        return;
                    } else
                        dtkDebug() << "Ok, data received, parse" ;
                }
                dtkDistributedMessage *msg = d->slave->communicator()->socket()->parseRequest();
                t->setTwinned(false);
                t->setDataFrom(msg->content());
                t->setTwinned(true);

                dtkDebug() << "send data to slaves";
                for (int j=1; j< size; j++)
                    d->communicator->send(msg->content(),j,0);

                delete msg;
            } else {
                QByteArray array;
                dtkDebug() << "receive data from rank 0";
                d->communicator->receive(array, 0, 0);
                dtkDebug() << "data received, set";
                t->setTwinned(false);
                t->setDataFrom(array);
                t->setTwinned(true);
            }
        }
    }
}

void dtkComposerNodeRemote::end(void)
{
    if (d->controller) {
        dtkDebug() << "running node remote end statement on controller";
        int max  = this->emitters().count();
        for (int i = 0; i < max; i++) {
            dtkComposerTransmitterVariant *t = dynamic_cast<dtkComposerTransmitterVariant *>(this->emitters().at(i));

            if (d->server->socket()->bytesAvailable()) {
                dtkDebug() << "data already available, parse" ;
            } else {
                if (!(d->server->socket()->waitForReadyRead(60000))) {
                    dtkError() << "No data received from slave after 1mn, abort " ;
                    return;
                } else
                    dtkDebug() << "Ok, data received, parse" ;
            }
            dtkDistributedMessage *msg = d->server->socket()->parseRequest();
            t->setTwinned(false);
            t->setDataFrom(msg->content());
            t->setTwinned(true);
            delete msg;

        }
    } else {
        // running on the slave, send data and set transmitters
        dtkDebug() << "running node remote end statement on slave" << d->communicator->rank() ;

        int max  = this->emitters().count();
        int size = d->communicator->size();
        Q_UNUSED(size);

        for (int i = 0; i < max; i++) {
            dtkComposerTransmitterVariant *t = dynamic_cast<dtkComposerTransmitterVariant *>(this->emitters().at(i));
            // FIXME: use our own transmitter variant list (see control nodes)
            if (d->communicator->rank() == 0) {
                dtkDebug() << "end, send transmitter data (we are rank 0)";
                QByteArray array = t->dataToByteArray();
                if (!array.isEmpty()) {
                    dtkDistributedMessage *req = new dtkDistributedMessage(dtkDistributedMessage::DATA, d->jobid, dtkDistributedMessage::CONTROLLER_RUN_RANK, array.size(), "qvariant", array);
                    d->slave->communicator()->socket()->sendRequest(req);
                    delete req;
                } else {
                    dtkError() << "serialization failed in transmitter";
                }

            } else {
                //TODO rank >0
            }
        }
        if (d->communicator->rank() == 0)
            d->slave->communicator()->socket()->waitForBytesWritten();
    }
}

// /////////////////////////////////////////////////////////////////
// Submit
// /////////////////////////////////////////////////////////////////

class dtkComposerNodeRemoteSubmitPrivate
{
public:
    dtkComposerTransmitterEmitter<QString> id;

    dtkComposerTransmitterReceiver<QString> cluster;
    dtkComposerTransmitterReceiver<qlonglong> nodes;
    dtkComposerTransmitterReceiver<qlonglong> cores;
    dtkComposerTransmitterReceiver<QString> walltime;
    dtkComposerTransmitterReceiver<QString> queuename;
    dtkComposerTransmitterReceiver<QString> application;

    QMutex mutex;

public:
    QString job_id;
};

dtkComposerNodeRemoteSubmit::dtkComposerNodeRemoteSubmit(void) : dtkComposerNodeLeaf(), d(new dtkComposerNodeRemoteSubmitPrivate)
{
    this->appendReceiver(&(d->cluster));
    this->appendReceiver(&(d->nodes));
    this->appendReceiver(&(d->cores));
    this->appendReceiver(&(d->walltime));
    this->appendReceiver(&(d->queuename));

    d->job_id = QString();
    d->id.setData(&d->job_id);
    this->appendEmitter(&(d->id));

    d->mutex.lock();

}

dtkComposerNodeRemoteSubmit::~dtkComposerNodeRemoteSubmit(void)
{
    delete d;

    d = NULL;
}

void dtkComposerNodeRemoteSubmit::run(void)
{
    QVariantMap resources;

    if (d->cluster.isEmpty()) {
        dtkError() << "Empty server in remote submit, can't submit job";
        return;
    }

    QVariantMap job;

    if (d->cores.isEmpty())
        resources.insert("cores", 1);
    else
        resources.insert("cores", *d->cores.data());

    if (d->nodes.isEmpty())
        resources.insert("nodes", 1);
    else
        resources.insert("nodes", *d->nodes.data());

    job.insert("resources", resources);

    if (d->walltime.isEmpty())
        job.insert("walltime", "00:15:00");
    else
        job.insert("walltime", *d->walltime.data());

    if (!d->queuename.isEmpty())
        job.insert("queue", *d->queuename.data());

    job.insert("properties", QVariantMap());
    job.insert("application", "dtkComposerEvaluatorSlave "+*d->cluster.data());

    QByteArray job_data = dtkJson::serialize(job);

    dtkTrace() << " submit job with parameters: "<< job_data;

    dtkDistributedController *controller = dtkDistributedController::instance();
    if (controller->submit(QUrl(*d->cluster.data()), job_data)) {
        QEventLoop loop;
        this->connect(controller, SIGNAL(jobQueued(QString)), this, SLOT(onJobQueued(QString)),Qt::DirectConnection);
        loop.connect(controller, SIGNAL(jobQueued(QString)), &loop, SLOT(quit()));
        loop.connect(qApp, SIGNAL(aboutToQuit()), &loop, SLOT(quit()));

        loop.exec();
        dtkTrace() <<  "event loop ended, job is queued";

    } else
        dtkWarn() <<  "failed to submit ";
}

void dtkComposerNodeRemoteSubmit::onJobQueued(const QString& job_id)
{
    d->job_id = job_id;
    QObject::disconnect( dtkDistributedController::instance(), SIGNAL(jobQueued(QString)), this, SLOT(onJobQueued(QString)));
}
<commit_msg>avoid crashing in remote node when no controller is set<commit_after>/* dtkComposerNodeRemote.cpp ---
 *
 * Author: Nicolas Niclausse, Inria.
 * Created: 2012/04/03 15:19:20
 */

/* Commentary:
 *
 */

/* Change log:
 *
 */

#include "dtkComposerMetatype.h"

#include "dtkComposerNodeRemote.h"
#include "dtkComposerTransmitterEmitter.h"
#include "dtkComposerTransmitterReceiver.h"
#include "dtkComposerTransmitterVariant.h"

#include <dtkDistributed/dtkDistributedController.h>
#include <dtkDistributed/dtkDistributedCommunicator.h>
#include <dtkDistributed/dtkDistributedCommunicatorTcp.h>
#include <dtkDistributed/dtkDistributedSlave.h>

#include <dtkCore/dtkAbstractDataFactory.h>
#include <dtkCore/dtkGlobal.h>

#include <dtkJson>

#include <dtkMath/dtkMath.h>

#include <dtkLog/dtkLog.h>

// /////////////////////////////////////////////////////////////////
// dtkComposerNodeRemotePrivate interface
// /////////////////////////////////////////////////////////////////

class dtkComposerNodeRemotePrivate
{

public:
    dtkComposerTransmitterReceiver<QString> jobid_receiver;

public:
    QDomDocument composition;
    QByteArray current_hash;
    QByteArray last_sent_hash;

public:
    dtkDistributedController *controller;

public:
    dtkDistributedCommunicator *communicator;
    dtkDistributedCommunicatorTcp *server;

public:
    dtkDistributedSlave *slave;

public:
    QString jobid;
    QString last_jobid;

public:
    QString title;

};

// /////////////////////////////////////////////////////////////////
// dtkComposerNodeRemote implementation
// /////////////////////////////////////////////////////////////////

dtkComposerNodeRemote::dtkComposerNodeRemote(void) : QObject(), dtkComposerNodeComposite(), d(new dtkComposerNodeRemotePrivate)
{

    this->appendReceiver(&(d->jobid_receiver));
    this->setInputLabelHint("jobid", 0);

    d->controller = NULL;
    d->slave      = NULL;
    d->server     = NULL;
    d->title      = "Remote";
}

dtkComposerNodeRemote::~dtkComposerNodeRemote(void)
{
    delete d->server;
    delete d;

    d = NULL;
}

QString dtkComposerNodeRemote::type(void)
{
    return "remote";
}

QString dtkComposerNodeRemote::titleHint(void)
{
    return d->title;
}

void dtkComposerNodeRemote::setComposition(QDomDocument document)
{
    d->composition  = document;
    d->current_hash = QCryptographicHash::hash(d->composition.toByteArray(),QCryptographicHash::Md5);
}

void dtkComposerNodeRemote::setController(dtkDistributedController *controller)
{
    if (d->jobid.isEmpty()) {
        dtkWarn() <<  "No job id while setting controller !";
    }

    d->controller = controller;
}

void dtkComposerNodeRemote::setSlave(dtkDistributedSlave *slave)
{
    d->slave = slave;
}

void dtkComposerNodeRemote::setCommunicator(dtkDistributedCommunicator *c)
{
    d->communicator = c;
}

void dtkComposerNodeRemote::setJob(QString jobid)
{
    d->jobid = jobid;
}

bool dtkComposerNodeRemote::isSlave(void)
{
    if (d->slave)
        return true;
    return false;
}

void dtkComposerNodeRemote::onJobStarted(QString jobid)
{
    if (jobid == d->jobid) {
        QObject::disconnect( dtkDistributedController::instance(), SIGNAL(jobStarted(QString)), this, SLOT(onJobStarted(QString)));
    } else {
        dtkDebug() << "A job has started, but it's not ours, keep waiting " << d->jobid << jobid ;
    }
}

void dtkComposerNodeRemote::begin(void)
{
    if (!d->slave && !d->jobid_receiver.isEmpty()) {
        // we are running on the controller but controller and job was
        // not drag&dropped, get job from transmitter and main
        // controller instance
        d->jobid = *d->jobid_receiver.data();
        d->controller = dtkDistributedController::instance();
        if (!d->controller->is_running(d->jobid)) {
            dtkDebug() << " Wait for job to start, jobid is " << d->jobid;
            QEventLoop loop;
            this->connect(d->controller, SIGNAL(jobStarted(QString)), this, SLOT(onJobStarted(QString)),Qt::DirectConnection);
            loop.connect(d->controller, SIGNAL(jobStarted(QString)), &loop, SLOT(quit()));
            loop.connect(qApp, SIGNAL(aboutToQuit()), &loop, SLOT(quit()));
            loop.exec();
            dtkTrace() << "waiting event loop ended, job has started" << d->jobid;
        } else
            dtkDebug() << " Job already running, go " << d->jobid;
    }

    if (d->controller) {
        if (!d->server) {
            d->server = new dtkDistributedCommunicatorTcp;
            d->server->connectToHost(d->controller->socket(d->jobid)->peerAddress().toString(),d->controller->socket(d->jobid)->peerPort());
            if (d->server->socket()->waitForConnected()) {
                dtkDebug() << "Connected to server";
            } else {
                dtkError() << "Can't connect to server";
                return;
            }
        }
        dtkDistributedMessage *msg;
        if (d->last_jobid != d->jobid) {
            msg = new dtkDistributedMessage(dtkDistributedMessage::SETRANK,d->jobid,dtkDistributedMessage::CONTROLLER_RUN_RANK );
            d->server->socket()->sendRequest(msg);
            delete msg;
            d->last_jobid=d->jobid;
            // the job has changed, so we must send the composition even if it has not changed
            d->last_sent_hash.clear();
        }
        if (d->current_hash != d->last_sent_hash){
            // send sub-composition to rank 0 on remote node
            QByteArray compo = d->composition.toByteArray();
            dtkDebug() << "running node remote begin statement on controller, send composition of size " << compo.size();
            msg = new dtkDistributedMessage(dtkDistributedMessage::DATA,d->jobid,0,compo.size(), "xml", compo );
            d->last_sent_hash=d->current_hash;
        } else {
            dtkDebug() << "composition hash hasn't changed, send 'not-modified' to slave";
            msg = new dtkDistributedMessage(dtkDistributedMessage::DATA,d->jobid,0,d->current_hash.size(), "not-modified", d->current_hash );
        }
        d->server->socket()->sendRequest(msg);
        delete msg;
        dtkDebug() << "composition sent";
        // then send transmitters data
        int max  = dtkComposerNodeComposite::receivers().count();
        for (int i = 1; i < max; i++) {
            dtkComposerTransmitterVariant *t = dynamic_cast<dtkComposerTransmitterVariant *>(dtkComposerNodeComposite::receivers().at(i));
            // FIXME: use our own transmitter variant list (see control nodes)
            QByteArray array = t->dataToByteArray();
            dtkDebug() << "sending transmitter" << i << "of size" << array.size();
            msg = new dtkDistributedMessage(dtkDistributedMessage::DATA, d->jobid, 0, array.size(), "qvariant", array);
            d->server->socket()->sendRequest(msg);
            delete msg;
        }
        d->server->socket()->waitForBytesWritten();
    } else if (d->communicator) {
        // running on the slave, receive data and set transmitters
        int max  = dtkComposerNodeComposite::receivers().count();
        int size = d->communicator->size();
        for (int i = 1; i < max; i++) {
            dtkComposerTransmitterVariant *t = dynamic_cast<dtkComposerTransmitterVariant *>(dtkComposerNodeComposite::receivers().at(i));
            if (d->communicator->rank() == 0) {

                if (d->slave->communicator()->socket()->bytesAvailable()) {
                    dtkDebug() << "data already available, parse" ;

                } else {
                    if (!(d->slave->communicator()->socket()->waitForReadyRead(60000))) {
                        dtkError() << "No data received from server after 1mn, abort " ;
                        return;
                    } else
                        dtkDebug() << "Ok, data received, parse" ;
                }
                dtkDistributedMessage *msg = d->slave->communicator()->socket()->parseRequest();
                t->setTwinned(false);
                t->setDataFrom(msg->content());
                t->setTwinned(true);

                dtkDebug() << "send data to slaves";
                for (int j=1; j< size; j++)
                    d->communicator->send(msg->content(),j,0);

                delete msg;
            } else {
                QByteArray array;
                dtkDebug() << "receive data from rank 0";
                d->communicator->receive(array, 0, 0);
                dtkDebug() << "data received, set";
                t->setTwinned(false);
                t->setDataFrom(array);
                t->setTwinned(true);
            }
        }
    } else {
        dtkError() << "No communicator and no controller on remote node: can't run begin node";
    }
}

void dtkComposerNodeRemote::end(void)
{
    if (d->controller) {
        dtkDebug() << "running node remote end statement on controller";
        int max  = this->emitters().count();
        for (int i = 0; i < max; i++) {
            dtkComposerTransmitterVariant *t = dynamic_cast<dtkComposerTransmitterVariant *>(this->emitters().at(i));

            if (d->server->socket()->bytesAvailable()) {
                dtkDebug() << "data already available, parse" ;
            } else {
                if (!(d->server->socket()->waitForReadyRead(60000))) {
                    dtkError() << "No data received from slave after 1mn, abort " ;
                    return;
                } else
                    dtkDebug() << "Ok, data received, parse" ;
            }
            dtkDistributedMessage *msg = d->server->socket()->parseRequest();
            t->setTwinned(false);
            t->setDataFrom(msg->content());
            t->setTwinned(true);
            delete msg;

        }
    } else if (d->communicator) {
        // running on the slave, send data and set transmitters
        dtkDebug() << "running node remote end statement on slave" << d->communicator->rank() ;

        int max  = this->emitters().count();
        int size = d->communicator->size();
        Q_UNUSED(size);

        for (int i = 0; i < max; i++) {
            dtkComposerTransmitterVariant *t = dynamic_cast<dtkComposerTransmitterVariant *>(this->emitters().at(i));
            // FIXME: use our own transmitter variant list (see control nodes)
            if (d->communicator->rank() == 0) {
                dtkDebug() << "end, send transmitter data (we are rank 0)";
                QByteArray array = t->dataToByteArray();
                if (!array.isEmpty()) {
                    dtkDistributedMessage *req = new dtkDistributedMessage(dtkDistributedMessage::DATA, d->jobid, dtkDistributedMessage::CONTROLLER_RUN_RANK, array.size(), "qvariant", array);
                    d->slave->communicator()->socket()->sendRequest(req);
                    delete req;
                } else {
                    dtkError() << "serialization failed in transmitter";
                }

            } else {
                //TODO rank >0
            }
        }
        if (d->communicator->rank() == 0)
            d->slave->communicator()->socket()->waitForBytesWritten();
    } else {
        dtkError() << "No communicator and no controller on remote node: can't run end node";
    }

}

// /////////////////////////////////////////////////////////////////
// Submit
// /////////////////////////////////////////////////////////////////

class dtkComposerNodeRemoteSubmitPrivate
{
public:
    dtkComposerTransmitterEmitter<QString> id;

    dtkComposerTransmitterReceiver<QString> cluster;
    dtkComposerTransmitterReceiver<qlonglong> nodes;
    dtkComposerTransmitterReceiver<qlonglong> cores;
    dtkComposerTransmitterReceiver<QString> walltime;
    dtkComposerTransmitterReceiver<QString> queuename;
    dtkComposerTransmitterReceiver<QString> application;

    QMutex mutex;

public:
    QString job_id;
};

dtkComposerNodeRemoteSubmit::dtkComposerNodeRemoteSubmit(void) : dtkComposerNodeLeaf(), d(new dtkComposerNodeRemoteSubmitPrivate)
{
    this->appendReceiver(&(d->cluster));
    this->appendReceiver(&(d->nodes));
    this->appendReceiver(&(d->cores));
    this->appendReceiver(&(d->walltime));
    this->appendReceiver(&(d->queuename));

    d->job_id = QString();
    d->id.setData(&d->job_id);
    this->appendEmitter(&(d->id));

    d->mutex.lock();

}

dtkComposerNodeRemoteSubmit::~dtkComposerNodeRemoteSubmit(void)
{
    delete d;

    d = NULL;
}

void dtkComposerNodeRemoteSubmit::run(void)
{
    QVariantMap resources;

    if (d->cluster.isEmpty()) {
        dtkError() << "Empty server in remote submit, can't submit job";
        return;
    }

    QVariantMap job;

    if (d->cores.isEmpty())
        resources.insert("cores", 1);
    else
        resources.insert("cores", *d->cores.data());

    if (d->nodes.isEmpty())
        resources.insert("nodes", 1);
    else
        resources.insert("nodes", *d->nodes.data());

    job.insert("resources", resources);

    if (d->walltime.isEmpty())
        job.insert("walltime", "00:15:00");
    else
        job.insert("walltime", *d->walltime.data());

    if (!d->queuename.isEmpty())
        job.insert("queue", *d->queuename.data());

    job.insert("properties", QVariantMap());
    job.insert("application", "dtkComposerEvaluatorSlave "+*d->cluster.data());

    QByteArray job_data = dtkJson::serialize(job);

    dtkTrace() << " submit job with parameters: "<< job_data;

    dtkDistributedController *controller = dtkDistributedController::instance();
    if (controller->submit(QUrl(*d->cluster.data()), job_data)) {
        QEventLoop loop;
        this->connect(controller, SIGNAL(jobQueued(QString)), this, SLOT(onJobQueued(QString)),Qt::DirectConnection);
        loop.connect(controller, SIGNAL(jobQueued(QString)), &loop, SLOT(quit()));
        loop.connect(qApp, SIGNAL(aboutToQuit()), &loop, SLOT(quit()));

        loop.exec();
        dtkTrace() <<  "event loop ended, job is queued";

    } else
        dtkWarn() <<  "failed to submit ";
}

void dtkComposerNodeRemoteSubmit::onJobQueued(const QString& job_id)
{
    d->job_id = job_id;
    QObject::disconnect( dtkDistributedController::instance(), SIGNAL(jobQueued(QString)), this, SLOT(onJobQueued(QString)));
}
<|endoftext|>


<commit_before>#include "treemodel.h"

QVariant tin::Tree_model::headerData(int section, Qt::Orientation orientation, int role) const
{
  if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
    return column_headers_[section];
  }

  return QVariant{};
}

QModelIndex tin::Tree_model::index(int row, int column, const QModelIndex& parent) const
{
  if (!hasIndex(row, column, parent))
    return QModelIndex();

  Tree_item* item;
  if (!parent.isValid())
    item = root_item_.get();
  else
    item = static_cast<Tree_item*>(parent.internalPointer());

  item = item->child(row);
  if (item)
    return createIndex(row, column, item);

  return QModelIndex{};
}

QModelIndex tin::Tree_model::parent(const QModelIndex& index) const
{
  if (!index.isValid())
    return QModelIndex{};

  auto* item = static_cast<Tree_item*>(index.internalPointer())->parent();

  if (item == root_item_.get() || item == nullptr)
    return QModelIndex{};

  return createIndex(item->row(), 0, item);
}

int tin::Tree_model::rowCount(const QModelIndex& parent) const
{
  if (parent.column() > 0)
    return 0;

  Tree_item* item;
  if (!parent.isValid())
    item = root_item_.get();
  else
    item = static_cast<Tree_item*>(parent.internalPointer());

  return item->child_count();
}

int tin::Tree_model::columnCount(const QModelIndex& parent) const
{
  Q_UNUSED(parent)
  return static_cast<int>(column_headers_.size());
}

QVariant tin::Tree_model::data(const QModelIndex& index, int role) const
{
  if (!index.isValid())
    return QVariant{};
  if (role != Qt::DisplayRole)
    return QVariant{};

  Tree_item* item = static_cast<Tree_item*>(index.internalPointer());
  return item->data(index.column());
}
<commit_msg>Use auto<commit_after>#include "treemodel.h"

QVariant tin::Tree_model::headerData(int section, Qt::Orientation orientation, int role) const
{
  if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
    return column_headers_[section];
  }

  return QVariant{};
}

QModelIndex tin::Tree_model::index(int row, int column, const QModelIndex& parent) const
{
  if (!hasIndex(row, column, parent))
    return QModelIndex();

  Tree_item* item;
  if (!parent.isValid())
    item = root_item_.get();
  else
    item = static_cast<Tree_item*>(parent.internalPointer());

  item = item->child(row);
  if (item)
    return createIndex(row, column, item);

  return QModelIndex{};
}

QModelIndex tin::Tree_model::parent(const QModelIndex& index) const
{
  if (!index.isValid())
    return QModelIndex{};

  auto* item = static_cast<Tree_item*>(index.internalPointer())->parent();

  if (item == root_item_.get() || item == nullptr)
    return QModelIndex{};

  return createIndex(item->row(), 0, item);
}

int tin::Tree_model::rowCount(const QModelIndex& parent) const
{
  if (parent.column() > 0)
    return 0;

  Tree_item* item;
  if (!parent.isValid())
    item = root_item_.get();
  else
    item = static_cast<Tree_item*>(parent.internalPointer());

  return item->child_count();
}

int tin::Tree_model::columnCount(const QModelIndex& parent) const
{
  Q_UNUSED(parent)
  return static_cast<int>(column_headers_.size());
}

QVariant tin::Tree_model::data(const QModelIndex& index, int role) const
{
  if (!index.isValid())
    return QVariant{};
  if (role != Qt::DisplayRole)
    return QVariant{};

  auto* item = static_cast<Tree_item*>(index.internalPointer());
  return item->data(index.column());
}
<|endoftext|>


<commit_before>#include "widgets/ui_image.h"

using namespace Halley;

UIImage::UIImage(Sprite s, Maybe<UISizer> sizer, Vector4f innerBorder)
	: UIWidget("", {}, sizer, innerBorder)
{
	setSprite(s);
}

void UIImage::draw(UIPainter& painter) const
{
	if (sprite.hasMaterial()) {
		painter.draw(sprite);
	}
}

void UIImage::update(Time t, bool moved)
{
	if (moved || dirty) {
		sprite
			.setPos(getPosition())
			.setScale(getSize() / (sprite.getRawSize().abs() + topLeftBorder + bottomRightBorder));
		dirty = false;
	}
}

void UIImage::setSprite(Sprite s)
{
	sprite = s;

	auto b = sprite.getOuterBorder();
	topLeftBorder = Vector2f(float(b.x), float(b.y));
	bottomRightBorder = Vector2f(float(b.z), float(b.w));
	auto c = s.getClip();

	auto spriteSize = (s.getRawSize().abs() + topLeftBorder + bottomRightBorder) * s.getScale();

	if (c) {
		setMinSize(Vector2f::min(spriteSize, c.get().getSize()));
	} else {
		setMinSize(spriteSize);
	}
	dirty = true;
}

Sprite& UIImage::getSprite()
{
	return sprite;
}

const Sprite& UIImage::getSprite() const
{
	return sprite;
}
<commit_msg>Change alignment of UIImages.<commit_after>#include "widgets/ui_image.h"

using namespace Halley;

UIImage::UIImage(Sprite s, Maybe<UISizer> sizer, Vector4f innerBorder)
	: UIWidget("", {}, sizer, innerBorder)
{
	setSprite(s);
}

void UIImage::draw(UIPainter& painter) const
{
	if (sprite.hasMaterial()) {
		painter.draw(sprite);
	}
}

void UIImage::update(Time t, bool moved)
{
	if (moved || dirty) {
		sprite
			.setPos(getPosition())
			.setScale(getSize() / (sprite.getRawSize().abs() + topLeftBorder + bottomRightBorder));
		dirty = false;
	}
}

void UIImage::setSprite(Sprite s)
{
	sprite = s;

	auto b = sprite.getOuterBorder();
	topLeftBorder = Vector2f(float(b.x), float(b.y));
	bottomRightBorder = Vector2f(float(b.z), float(b.w));
	auto c = s.getClip();

	auto spriteSize = (s.getRawSize().abs() + topLeftBorder + bottomRightBorder) * s.getScale();
	sprite.setAbsolutePivot(-topLeftBorder);

	if (c) {
		setMinSize(Vector2f::min(spriteSize, c.get().getSize()));
	} else {
		setMinSize(spriteSize);
	}
	dirty = true;
}

Sprite& UIImage::getSprite()
{
	return sprite;
}

const Sprite& UIImage::getSprite() const
{
	return sprite;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2010, British Broadcasting Corporation
 * All Rights Reserved.
 *
 * Author: Philip de Nier
 *
 * 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 British Broadcasting Corporation 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 OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "EssenceParserUtils.h"
#include <bmx/BMXException.h>
#include <bmx/Logging.h>

using namespace bmx;

uint32_t bmx::get_bits(const unsigned char *data, uint32_t data_size, uint32_t bit_offset, uint8_t num_bits)
{
    BMX_ASSERT(num_bits <= 32);
    BMX_CHECK((bit_offset + num_bits + 7) / 8 <= data_size);

    const unsigned char *byte = data + bit_offset / 8;
    uint32_t num_bytes = ((bit_offset % 8) + num_bits + 7) / 8;
    uint64_t buffer = 0;
    uint32_t i;
    for (i = 0; i < num_bytes; i++) {
        buffer = (buffer << 8) | (*byte);
        byte++;
    }

    buffer >>= (7 - ((bit_offset + num_bits - 1) % 8));
    buffer &=  (1UL << num_bits) - 1;

    return (uint32_t)buffer;
}

<commit_msg>ess parser: fix num_bits=64 case in get_bits()<commit_after>/*
 * Copyright (C) 2010, British Broadcasting Corporation
 * All Rights Reserved.
 *
 * Author: Philip de Nier
 *
 * 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 British Broadcasting Corporation 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 OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define __STDC_LIMIT_MACROS

#include "EssenceParserUtils.h"
#include <bmx/BMXException.h>
#include <bmx/Logging.h>

using namespace bmx;

uint32_t bmx::get_bits(const unsigned char *data, uint32_t data_size, uint32_t bit_offset, uint8_t num_bits)
{
    BMX_ASSERT(num_bits <= 32);
    BMX_CHECK((bit_offset + num_bits + 7) / 8 <= data_size);

    const unsigned char *byte = data + bit_offset / 8;
    uint32_t num_bytes = ((bit_offset % 8) + num_bits + 7) / 8;
    uint64_t buffer = 0;
    uint32_t i;
    for (i = 0; i < num_bytes; i++) {
        buffer = (buffer << 8) | (*byte);
        byte++;
    }

    buffer >>= (7 - ((bit_offset + num_bits - 1) % 8));
    buffer &=  UINT64_MAX >> (64 - num_bits);

    return (uint32_t)buffer;
}

<|endoftext|>


<commit_before>#include "mugen/storyboard.h"

#include "util/bitmap.h"
#include "init.h"
#include "resource.h"
#include "util/funcs.h"
#include "globals.h"
#include "factory/font_render.h"

#include "mugen_animation.h"
#include "mugen/background.h"
#include "mugen_sound.h"
#include "mugen_reader.h"
#include "mugen_sprite.h"
#include "mugen_util.h"
#include "mugen_font.h"

#include "util/timedifference.h"
#include "ast/all.h"
#include "parser/all.h"
#include "input/input-map.h"
#include "input/input-manager.h"

namespace PaintownUtil = ::Util;

using namespace std;
using namespace Mugen;

static const int DEFAULT_WIDTH = 320;
static const int DEFAULT_HEIGHT = 240;
static const int DEFAULT_SCREEN_X_AXIS = 160;
static const int DEFAULT_SCREEN_Y_AXIS = 0;

Layer::Layer():
startTime(0),
enabled(false),
animation(0){
}

Layer::~Layer(){
}

void Layer::act(int currentTime){
    if (startTime >= currentTime && !enabled){
        enabled = true;
    }
    if (enabled){
        animation->logic();
    }
}

void Layer::render(int x, int y, const Bitmap &bmp){
    if (enabled){
        animation->render(x + offset.x, y + offset.y, bmp);
    }
}

void Layer::reset(){
    enabled = false;
    animation->reset();
}

Scene::Scene(Ast::Section * data, const std::string & storyBoardFile, SpriteMap & sprites):
clearColor(-2),
ticker(0),
endTime(0),
backgroundName(""),
background(0){
#if 0
    for (Ast::AstParse::section_iterator section_it = parsed.getSections()->begin(); section_it != parsed.getSections()->end(); section_it++){
        Ast::Section * section = *section_it;
	std::string head = section->getName();

        head = Util::fixCase(head);
        if (PaintownUtil::matchRegex(head, "^scene")){
            class SceneWalker: public Ast::Walker(Scene & scene, SpriteMap & sprites){
            public:
                SceneWalker():
                scene(scene){
                }

                Scene & scene;

                virtual void onAttributeSimple(const Ast::AttributeSimple & simple){
                    if (simple == "fadein.time"){
                        int time;
                        simple >> time;
                        scene.fader.setFadeInTime(time);
                    } else if (simple == "fadein.col"){
                        int r=0,g=0,b=0;
                        try{
                            simple >> r >> g >> b;
                        } catch (const Ast::Exception & e){
                        }
                        scene.fader.setFadeInColor(Bitmap::makeColor(r,g,b));
                    } else if (simple == "fadeout.time"){
                        int time;
                        simple >> time;
                        scene.fader.setFadeOutTime(time);
                    } else if (simple == "fadeout.col"){
                        int r=0,g=0,b=0;
                        try {
                            simple >> r >> g >> b;
                        } catch (const Ast::Exception & e){
                        }
                        scene.fader.setFadeOutColor(Bitmap::makeColor(r,g,b));
                    } else if (simple == "bg.name"){
                        simple >> scene.backgroundName;
                    } else if (simple == "clearcolor"){
                        int r=0,g=0,b=0;
                        try {
                            simple >> r >> g >> b;
                        } catch (const Ast::Exception & e){
                        }
                        scene.clearColor = (r == -1 ? r : Bitmap::makeColor(r,g,b));
                    } else if (simple == "end.time"){
                        simple >> scene.endTime;
                    } else if (simple == "layerall.pos"){
                        try{
                            simple >> scene.defaultAxis.x;
                            simple >> scene.defaultAxis.y;
                        } catch (const Ast::Exception & e){
                        }
                    } else if (PaintownUtil::matchRegex(simple.idString(), "layer[0-9]\\.anim")){
                        int num = atoi(PaintownUtil::captureRegex(simple.idString(), "layer([0-9])\\.anim", 0).c_str());
                        if (num >= 0 && num < 10){
                            simple >> scene->layers[num]->actionno;
                        }
                    } else if (PaintownUtil::matchRegex(simple.idString(), "layer[0-9]\\.offset")){
                        int num = atoi(PaintownUtil::captureRegex(simple.idString(), "layer([0-9])\\.offset", 0).c_str());
                        if (num >= 0 && num < 10){
                            simple >> scene->layers[num]->offset.x;
                            simple >> scene->layers[num]->offset.y;
                        }
                    } else if (PaintownUtil::matchRegex(simple.idString(), "layer[0-9]\\.starttime")){
                        int num = atoi(PaintownUtil::captureRegex(simple.idString(), "layer([0-9])\\.starttime", 0).c_str());
                        if (num >= 0 && num < 10){
                            simple >> scene->layers[num]->startTime;
                        }
                    } else if (simple == "bgm"){
                        // do nothing
                    } else if (simple == "bgm.loop"){
                        // do nothing
                    } else {
                            Global::debug(0) << "Unhandled option in Scene Section: " << simple.toString();
                    }
                }
            };

            SceneWalker walker;
            section->walk(walker);
            scenes.push_back(walker.scene);
            bgname = walker.scene->backgroundName;
            bgname.erase(std::remove(bgname.begin(), bgname.end(), ' '), bgname.end());
            bgname = Util::fixCase(bgname);
            Global::debug(1) << "Got Scene number: '" << scenes.size() - 1 << "' bgname is '" << bgname << "'" << endl;

        } else if (PaintownUtil::matchRegex(head, "begin action")){
            //int h = atoi(PaintownUtil::captureRegex(head, "begin action *([0-9]+)", 0).c_str());
            //animations[h] = Util::getAnimation(section, sprites);
        } else if (PaintownUtil::matchRegex(head, bgname + "def$")){
            Global::debug(1) << "Checking def!" << endl;
            if (scenes.back()){
                Scene *scene = scenes.back();
                /*
                std::string name = collection[i]->getHeader();
                name = Util::fixCase(name);
                Global::debug(1) << "Checking for background: " << scene->backgroundName << " in Head: " << name << endl;
                */
                //Background *manager = new Background(ourDefFile, bgname);
                //scenes.back()->background = manager;
            }
        } //else throw MugenException( "Unhandled Section in '" + ourDefFile + "': " + head );
    }

    // set initial fade state
    fader.setState(FADEIN);
#endif
}

Scene::~Scene(){

    if (background){
        delete background;
    }

    // layers
    for ( std::vector< Layer *>::iterator i = layers.begin(); i != layers.end(); ++i ){
        if ((*i))delete (*i);
    }
}

void Scene::act(){
#if 0
    // backgrounds
    if (background){
        background->act();
    }
    // layers
    for ( std::vector< Layer *>::iterator i = layers.begin(); i != layers.end(); ++i ){
        Layer *layer = *i;
        if(ticker >= layer->startTime)layer->act();
    }
    // Fader
    fader.act();
    if (ticker == endTime - fader.getFadeOutTime()){
        fader.setState(FADEOUT);
    }
    // tick tick
    ticker++;
#endif
}
void Scene::render(const Bitmap & bmp){
#if 0
    if (clearColor != -1 && clearColor != -2){
        bmp.fill(clearColor);
    }
    // backgrounds
    if (background){
        background->renderBackground(0,0, bmp);
    }
    // layers
    for (std::vector< Layer *>::iterator i = layers.begin(); i != layers.end(); ++i ){
        Layer *layer = *i;
        layer->render(defaultAxis.x,defaultAxis.y,bmp);
    }
    // foregrounds
    if (background){
        background->renderForeground(0,0, bmp);
    }
    // fader
    fader.draw(bmp);
#endif
}

bool Scene::isDone(){
    return (ticker >= endTime);
}

void Scene::reset(){
#if 0
    ticker = 0;
    fader.setState(FADEIN);
    // layers
    for (std::vector< Layer *>::iterator i = layers.begin(); i != layers.end(); ++i ){
	Layer *layer = *i;
	layer->reset();
    }
#endif
}

Storyboard::Storyboard( const string & s ):
storyBoardFile(s),
startscene(0){
}

Storyboard::~Storyboard(){
    // Get rid of animation lists;
    for( std::map< int, MugenAnimation * >::iterator i = animations.begin() ; i != animations.end() ; ++i ){
        if( i->second )delete i->second;
    }

    // Get rid of scene lists;
    for( std::vector< Scene * >::iterator i = scenes.begin() ; i != scenes.end() ; ++i ){
        if( (*i) )delete (*i);
    }
    // sprites
    for( SpriteMap::iterator i = sprites.begin() ; i != sprites.end() ; ++i ){
        for( std::map< unsigned int, MugenSprite * >::iterator j = i->second.begin() ; j != i->second.end() ; ++j ){
            if( j->second )delete j->second;
        }
    }
}

void Storyboard::load() throw (MugenException){
#if 0
    // Lets look for our def since some people think that all file systems are case insensitive
    std::string baseDir = Util::getFileDir(storyBoardFile);
    const std::string ourDefFile = Util::fixFileName( baseDir, Util::stripDir(storyBoardFile) );
    // get real basedir
    //baseDir = Util::getFileDir( ourDefFile );
    Global::debug(1) << baseDir << endl;

    if (ourDefFile.empty()){
        throw MugenException("Cannot locate storyboard definition file for: " + storyBoardFile);
    }

    std::string filesdir = "";

    Global::debug(1) << "Got subdir: " << filesdir << endl;

    TimeDifference diff;
    diff.startTime();
    Ast::AstParse parsed((list<Ast::Section*>*) Def::main(ourDefFile));
    diff.endTime();
    Global::debug(1) << "Parsed mugen file " + ourDefFile + " in" + diff.printTime("") << endl;

    /* set by bg.name = "foo" */
    string bgname;

    for (Ast::AstParse::section_iterator section_it = parsed.getSections()->begin(); section_it != parsed.getSections()->end(); section_it++){
        Ast::Section * section = *section_it;
	std::string head = section->getName();
        /* this should really be head = Util::fixCase(head) */
	head = Util::fixCase(head);

        // Global::debug(1) << "Name: " << head << endl;
        if (head == "info"){
            class InfoWalk: public Ast::Walker{
            public:
                virtual void onAttributeSimple(const Ast::AttributeSimple & simple){
                    if (simple == "name"){
                        string name;
                        simple >> name;
                        Global::debug(1) << "Read name '" << name << "'" << endl;
                    } else if (simple == "author"){
                        string name;
                        simple >> name;
                        Global::debug(1) << "Made by: '" << name << "'" << endl;
                    } else {
                        Global::debug(0) << "Warning: ignored attribute: " << simple.toString() << endl;
                    }
                }
            };

            InfoWalk walk;
            section->walk(walk);

        } else if (head == "scenedef"){
            class SceneWalk: public Ast::Walker{
            public:
                SceneWalk(const string & baseDir, Storyboard & board):
                    baseDir(baseDir),
                    board(board){
                }

                const string & baseDir;
                Storyboard & board;

                virtual void onAttributeSimple(const Ast::AttributeSimple & simple){
                    if (simple == "spr"){
                        simple >> board.spriteFile;
                        Util::readSprites(Util::getCorrectFileLocation(this->baseDir, board.spriteFile), "", board.sprites);
                    } else if (simple == "startscene"){
                        simple >> board.startscene;
                        Global::debug(1) << "Starting storyboard at: '" << board.startscene << "'" << endl;
                    } else {
                        Global::debug(0) << "Warning: ignored attribute: " << simple.toString() << endl;
                    }
                }
            };

            SceneWalk walk(baseDir, *this);
            section->walk(walk);
        }
    }
#endif
}

void Storyboard::run(const Bitmap &bmp, bool repeat){
#if 0
    double gameSpeed = 1.0;
    double runCounter = 0;
    bool quit = false;

    Bitmap work( 320, 240 );

    for( std::vector< Scene * >::iterator i = scenes.begin() ; i != scenes.end() ; ++i ){
        if( (*i) ){
            (*i)->reset();
        }
    }

    std::vector< Scene * >::iterator sceneIterator = scenes.begin() + startscene;

    while( !quit ){
        bool draw = false;

        Scene *scene = *sceneIterator;
        if ( Global::speed_counter > 0 ){

            runCounter += Global::speed_counter * gameSpeed * Global::LOGIC_MULTIPLIER;//(double) 90 / (double) 60;
            while (runCounter > 1){
                runCounter -= 1;
                draw = true;
                // Key handler
		InputManager::poll();

                InputMap<CharacterKeys>::Output out = InputManager::getMap(input);

                if (out[Up]){
	        }
	        if (out[Down]){
	        }
	        if (out[Left]){
	        }
	        if (out[Right]){
	        }
	        if (out[A]){
                    quit = true;
                    return;
	        }
	        if (out[B]){
                    quit = true;
                    return;
	        }
	        if (out[C]){
                    quit = true;
                    return;
	        }
	        if (out[X]){
                    quit = true;
                    return;
	        }
	        if (out[Y]){
                    quit = true;
                    return;
	        }
	        if (out[Z]){
                    quit = true;
                    return;
	        }
	        if (out[Start]){
                    quit = true;
                    return;
	        }
                scene->act();

                if (scene->isDone()){
                    sceneIterator++;
                    if (sceneIterator == scenes.end()){
                        if (repeat){
                            sceneIterator = scenes.begin();
                        } else {
                            return;
                        }
                    }
                    scene = *sceneIterator;
                    scene->reset();
                }
            }
            Global::speed_counter = 0;
        }

        if (draw){
            scene->render(work);
            work.Stretch(bmp);
            if (Global::getDebug() > 0){
                Font::getDefaultFont().printf( 15, 310, Bitmap::makeColor(0,255,128), bmp, "Scene: Time(%i) : EndTime(%i) : Fade in(%i) : Fade out(%i)",0, scene->ticker,scene->endTime,scene->fader.getFadeInTime(),scene->fader.getFadeOutTime() );
            }
            bmp.BlitToScreen();
        }

        while (Global::speed_counter == 0){
            PaintownUtil::rest(1);
        }
    }
#endif
}

<commit_msg>Fill out more info for Scene.<commit_after>#include "mugen/storyboard.h"

#include "util/bitmap.h"
#include "init.h"
#include "resource.h"
#include "util/funcs.h"
#include "globals.h"
#include "factory/font_render.h"

#include "mugen_animation.h"
#include "mugen/background.h"
#include "mugen_sound.h"
#include "mugen_reader.h"
#include "mugen_sprite.h"
#include "mugen_util.h"
#include "mugen_font.h"

#include "util/timedifference.h"
#include "ast/all.h"
#include "parser/all.h"
#include "input/input-map.h"
#include "input/input-manager.h"

namespace PaintownUtil = ::Util;

using namespace std;
using namespace Mugen;

static const int DEFAULT_WIDTH = 320;
static const int DEFAULT_HEIGHT = 240;
static const int DEFAULT_SCREEN_X_AXIS = 160;
static const int DEFAULT_SCREEN_Y_AXIS = 0;

Layer::Layer():
startTime(0),
enabled(false),
animation(0){
}

Layer::~Layer(){
}

void Layer::act(int currentTime){
    if (startTime >= currentTime && !enabled){
        enabled = true;
    }
    if (enabled){
        animation->logic();
    }
}

void Layer::render(int x, int y, const Bitmap &bmp){
    if (enabled){
        animation->render(x + offset.x, y + offset.y, bmp);
    }
}

void Layer::reset(){
    enabled = false;
    animation->reset();
}

Scene::Scene(Ast::Section * data, const std::string & storyBoardFile, SpriteMap & sprites):
clearColor(-2),
ticker(0),
endTime(0),
backgroundName(""),
background(0){
    class SceneWalker: public Ast::Walker {
	public:
	    SceneWalker(Scene & scene, SpriteMap & sprites):
	    scene(scene),
	    sprites(sprites){
	    }
	    ~SceneWalker(){
	    }

	    Scene & scene;
	    SpriteMap & sprites;

	    virtual void onAttributeSimple(const Ast::AttributeSimple & simple){
		if (simple == "fadein.time"){
		    int time;
		    simple >> time;
		    scene.fader.setFadeInTime(time);
		} else if (simple == "fadein.col"){
		    int r=0,g=0,b=0;
		    try{
			simple >> r >> g >> b;
		    } catch (const Ast::Exception & e){
		    }
		    scene.fader.setFadeInColor(Bitmap::makeColor(r,g,b));
		} else if (simple == "fadeout.time"){
		    int time;
		    simple >> time;
		    scene.fader.setFadeOutTime(time);
		} else if (simple == "fadeout.col"){
		    int r=0,g=0,b=0;
		    try {
			simple >> r >> g >> b;
		    } catch (const Ast::Exception & e){
		    }
		    scene.fader.setFadeOutColor(Bitmap::makeColor(r,g,b));
		} else if (simple == "bg.name"){
		    simple >> scene.backgroundName;
		} else if (simple == "clearcolor"){
		    int r=0,g=0,b=0;
		    try {
			simple >> r >> g >> b;
		    } catch (const Ast::Exception & e){
		    }
		    scene.clearColor = (r == -1 ? r : Bitmap::makeColor(r,g,b));
		} else if (simple == "end.time"){
		    simple >> scene.endTime;
		} else if (simple == "layerall.pos"){
		    try{
			simple >> scene.defaultPosition.x;
			simple >> scene.defaultPosition.y;
		    } catch (const Ast::Exception & e){
		    }
		} else if (PaintownUtil::matchRegex(simple.idString(), "layer[0-9]\\.anim")){
		    int num = atoi(PaintownUtil::captureRegex(simple.idString(), "layer([0-9])\\.anim", 0).c_str());
		    if (num >= 0 && num < 10){
			//simple >> scene.layers[num]->actionno;
		    }
		} else if (PaintownUtil::matchRegex(simple.idString(), "layer[0-9]\\.offset")){
		    int num = atoi(PaintownUtil::captureRegex(simple.idString(), "layer([0-9])\\.offset", 0).c_str());
		    if (num >= 0 && num < 10){
			//simple >> scene.layers[num]->offset.x;
			//simple >> scene.layers[num]->offset.y;
		    }
		} else if (PaintownUtil::matchRegex(simple.idString(), "layer[0-9]\\.starttime")){
		    int num = atoi(PaintownUtil::captureRegex(simple.idString(), "layer([0-9])\\.starttime", 0).c_str());
		    if (num >= 0 && num < 10){
			//simple >> scene.layers[num]->startTime;
		    }
		} else if (simple == "bgm"){
		    // do nothing
		} else if (simple == "bgm.loop"){
		    // do nothing
		} else {
			Global::debug(0) << "Unhandled option in Scene Section: " << simple.toString();
		}
	    }
    };

    SceneWalker walker(*this,sprites);
    data->walk(walker);

#if 0
	scenes.push_back(walker.scene);
	bgname = walker.scene->backgroundName;
	bgname.erase(std::remove(bgname.begin(), bgname.end(), ' '), bgname.end());
	bgname = Util::fixCase(bgname);
	Global::debug(1) << "Got Scene number: '" << scenes.size() - 1 << "' bgname is '" << bgname << "'" << endl;

        if (PaintownUtil::matchRegex(head, "^scene")){

        } else if (PaintownUtil::matchRegex(head, "begin action")){
            //int h = atoi(PaintownUtil::captureRegex(head, "begin action *([0-9]+)", 0).c_str());
            //animations[h] = Util::getAnimation(section, sprites);
        } else if (PaintownUtil::matchRegex(head, bgname + "def$")){
            Global::debug(1) << "Checking def!" << endl;
            if (scenes.back()){
                Scene *scene = scenes.back();
                /*
                std::string name = collection[i]->getHeader();
                name = Util::fixCase(name);
                Global::debug(1) << "Checking for background: " << scene->backgroundName << " in Head: " << name << endl;
                */
                //Background *manager = new Background(ourDefFile, bgname);
                //scenes.back()->background = manager;
            }
        } //else throw MugenException( "Unhandled Section in '" + ourDefFile + "': " + head );
    }
#endif
    // set initial fade state
    fader.setState(FADEIN);
}

Scene::~Scene(){

    if (background){
        delete background;
    }

    // layers
    for ( std::vector< Layer *>::iterator i = layers.begin(); i != layers.end(); ++i ){
        if ((*i))delete (*i);
    }
}

void Scene::act(){
#if 0
    // backgrounds
    if (background){
        background->act();
    }
    // layers
    for ( std::vector< Layer *>::iterator i = layers.begin(); i != layers.end(); ++i ){
        Layer *layer = *i;
        if(ticker >= layer->startTime)layer->act();
    }
    // Fader
    fader.act();
    if (ticker == endTime - fader.getFadeOutTime()){
        fader.setState(FADEOUT);
    }
    // tick tick
    ticker++;
#endif
}
void Scene::render(const Bitmap & bmp){
#if 0
    if (clearColor != -1 && clearColor != -2){
        bmp.fill(clearColor);
    }
    // backgrounds
    if (background){
        background->renderBackground(0,0, bmp);
    }
    // layers
    for (std::vector< Layer *>::iterator i = layers.begin(); i != layers.end(); ++i ){
        Layer *layer = *i;
        layer->render(defaultAxis.x,defaultAxis.y,bmp);
    }
    // foregrounds
    if (background){
        background->renderForeground(0,0, bmp);
    }
    // fader
    fader.draw(bmp);
#endif
}

bool Scene::isDone(){
    return (ticker >= endTime);
}

void Scene::reset(){
#if 0
    ticker = 0;
    fader.setState(FADEIN);
    // layers
    for (std::vector< Layer *>::iterator i = layers.begin(); i != layers.end(); ++i ){
	Layer *layer = *i;
	layer->reset();
    }
#endif
}

Storyboard::Storyboard( const string & s ):
storyBoardFile(s),
startscene(0){
}

Storyboard::~Storyboard(){
    // Get rid of animation lists;
    for( std::map< int, MugenAnimation * >::iterator i = animations.begin() ; i != animations.end() ; ++i ){
        if( i->second )delete i->second;
    }

    // Get rid of scene lists;
    for( std::vector< Scene * >::iterator i = scenes.begin() ; i != scenes.end() ; ++i ){
        if( (*i) )delete (*i);
    }
    // sprites
    for( SpriteMap::iterator i = sprites.begin() ; i != sprites.end() ; ++i ){
        for( std::map< unsigned int, MugenSprite * >::iterator j = i->second.begin() ; j != i->second.end() ; ++j ){
            if( j->second )delete j->second;
        }
    }
}

void Storyboard::load() throw (MugenException){
    // Lets look for our def since some people think that all file systems are case insensitive
    std::string baseDir = Util::getFileDir(storyBoardFile);
    const std::string ourDefFile = Util::fixFileName( baseDir, Util::stripDir(storyBoardFile) );
    // get real basedir
    //baseDir = Util::getFileDir( ourDefFile );
    Global::debug(1) << baseDir << endl;

    if (ourDefFile.empty()){
        throw MugenException("Cannot locate storyboard definition file for: " + storyBoardFile);
    }

    std::string filesdir = "";

    Global::debug(1) << "Got subdir: " << filesdir << endl;

    TimeDifference diff;
    diff.startTime();
    Ast::AstParse parsed((list<Ast::Section*>*) Def::main(ourDefFile));
    diff.endTime();
    Global::debug(1) << "Parsed mugen file " + ourDefFile + " in" + diff.printTime("") << endl;

    /* set by bg.name = "foo" */
    string bgname;

    for (Ast::AstParse::section_iterator section_it = parsed.getSections()->begin(); section_it != parsed.getSections()->end(); section_it++){
        Ast::Section * section = *section_it;
	std::string head = section->getName();
        /* this should really be head = Util::fixCase(head) */
	head = Util::fixCase(head);

        // Global::debug(1) << "Name: " << head << endl;
        if (head == "info"){
            class InfoWalk: public Ast::Walker{
            public:
                virtual void onAttributeSimple(const Ast::AttributeSimple & simple){
                    if (simple == "name"){
                        string name;
                        simple >> name;
                        Global::debug(1) << "Read name '" << name << "'" << endl;
                    } else if (simple == "author"){
                        string name;
                        simple >> name;
                        Global::debug(1) << "Made by: '" << name << "'" << endl;
                    } else {
                        Global::debug(0) << "Warning: ignored attribute: " << simple.toString() << endl;
                    }
                }
            };

            InfoWalk walk;
            section->walk(walk);

        } else if (head == "scenedef"){
            class SceneWalk: public Ast::Walker{
            public:
                SceneWalk(const string & baseDir, Storyboard & board):
                    baseDir(baseDir),
                    board(board){
                }

                const string & baseDir;
                Storyboard & board;

                virtual void onAttributeSimple(const Ast::AttributeSimple & simple){
                    if (simple == "spr"){
			std::string temp;
                        simple >> temp;
                        Util::readSprites(Util::getCorrectFileLocation(this->baseDir, temp), "", board.sprites);
                    } else if (simple == "startscene"){
                        simple >> board.startscene;
                        Global::debug(1) << "Starting storyboard at: '" << board.startscene << "'" << endl;
                    } else {
                        Global::debug(0) << "Warning: ignored attribute: " << simple.toString() << endl;
                    }
                }
            };

            SceneWalk walk(baseDir, *this);
            section->walk(walk);
        } else if (PaintownUtil::matchRegex(head, "^scene")){
	    Scene *scene = new Scene(section,storyBoardFile,sprites);
	    scenes.push_back(scene);
	}
    }
}

void Storyboard::run(const Bitmap &bmp, bool repeat){
#if 0
    double gameSpeed = 1.0;
    double runCounter = 0;
    bool quit = false;

    Bitmap work( 320, 240 );

    for( std::vector< Scene * >::iterator i = scenes.begin() ; i != scenes.end() ; ++i ){
        if( (*i) ){
            (*i)->reset();
        }
    }

    std::vector< Scene * >::iterator sceneIterator = scenes.begin() + startscene;

    while( !quit ){
        bool draw = false;

        Scene *scene = *sceneIterator;
        if ( Global::speed_counter > 0 ){

            runCounter += Global::speed_counter * gameSpeed * Global::LOGIC_MULTIPLIER;//(double) 90 / (double) 60;
            while (runCounter > 1){
                runCounter -= 1;
                draw = true;
                // Key handler
		InputManager::poll();

                InputMap<CharacterKeys>::Output out = InputManager::getMap(input);

                if (out[Up]){
	        }
	        if (out[Down]){
	        }
	        if (out[Left]){
	        }
	        if (out[Right]){
	        }
	        if (out[A]){
                    quit = true;
                    return;
	        }
	        if (out[B]){
                    quit = true;
                    return;
	        }
	        if (out[C]){
                    quit = true;
                    return;
	        }
	        if (out[X]){
                    quit = true;
                    return;
	        }
	        if (out[Y]){
                    quit = true;
                    return;
	        }
	        if (out[Z]){
                    quit = true;
                    return;
	        }
	        if (out[Start]){
                    quit = true;
                    return;
	        }
                scene->act();

                if (scene->isDone()){
                    sceneIterator++;
                    if (sceneIterator == scenes.end()){
                        if (repeat){
                            sceneIterator = scenes.begin();
                        } else {
                            return;
                        }
                    }
                    scene = *sceneIterator;
                    scene->reset();
                }
            }
            Global::speed_counter = 0;
        }

        if (draw){
            scene->render(work);
            work.Stretch(bmp);
            if (Global::getDebug() > 0){
                Font::getDefaultFont().printf( 15, 310, Bitmap::makeColor(0,255,128), bmp, "Scene: Time(%i) : EndTime(%i) : Fade in(%i) : Fade out(%i)",0, scene->ticker,scene->endTime,scene->fader.getFadeInTime(),scene->fader.getFadeOutTime() );
            }
            bmp.BlitToScreen();
        }

        while (Global::speed_counter == 0){
            PaintownUtil::rest(1);
        }
    }
#endif
}

<|endoftext|>


<commit_before>
#include "game/general/optionsmenu.h"
#include "game/base/basescreen.h"
#include "game/ufopaedia/ufopaedia.h"
#include "game/debugtools/debugmenu.h"
#include "framework/framework.h"

namespace OpenApoc
{

OptionsMenu::OptionsMenu(Framework &fw) : Stage(fw)
{
	menuform = fw.gamecore->GetForm("FORM_OPTIONSMENU");
}

OptionsMenu::~OptionsMenu() {}

void OptionsMenu::Begin() {}

void OptionsMenu::Pause() {}

void OptionsMenu::Resume() {}

void OptionsMenu::Finish() {}

void OptionsMenu::EventOccurred(Event *e)
{
	menuform->EventOccured(e);
	fw.gamecore->MouseCursor->EventOccured(e);

	if (e->Type == EVENT_KEY_DOWN)
	{
		if (e->Data.Keyboard.KeyCode == ALLEGRO_KEY_ESCAPE)
		{
			stageCmd.cmd = StageCmd::Command::POP;
			return;
		}
	}

	if (e->Type == EVENT_FORM_INTERACTION && e->Data.Forms.EventFlag == FormEventType::ButtonClick)
	{
		if (e->Data.Forms.RaisedBy->Name == "BUTTON_TEST_XCOMBASE")
		{
			stageCmd.cmd = StageCmd::Command::PUSH;
			stageCmd.nextStage = std::make_shared<BaseScreen>(fw);
			return;
		}
		if (e->Data.Forms.RaisedBy->Name == "BUTTON_TEST_UFOPAEDIA")
		{
			stageCmd.cmd = StageCmd::Command::PUSH;
			stageCmd.nextStage = std::make_shared<Ufopaedia>(fw);
			return;
		}
		if (e->Data.Forms.RaisedBy->Name == "BUTTON_DEBUGGING")
		{
			stageCmd.cmd = StageCmd::Command::PUSH;
			stageCmd.nextStage = std::make_shared<DebugMenu>(fw);
			return;
		}
		if (e->Data.Forms.RaisedBy->Name == "BUTTON_QUIT")
		{
			stageCmd.cmd = StageCmd::Command::POP;
			return;
		}
	}
}

void OptionsMenu::Update(StageCmd *const cmd)
{
	menuform->Update();
	*cmd = this->stageCmd;
	// Reset the command to default
	this->stageCmd = StageCmd();
}

void OptionsMenu::Render()
{
	fw.Stage_GetPrevious(this->shared_from_this())->Render();
	fw.renderer->drawFilledRect({0, 0}, fw.Display_GetSize(), Colour{0, 0, 0, 128});
	menuform->Render();
	fw.gamecore->MouseCursor->Render();
}

bool OptionsMenu::IsTransition() { return false; }

}; // namespace OpenApoc
<commit_msg>Ditched Basescreen from Options menu -- need to start rebuilding this properly soon<commit_after>
#include "game/general/optionsmenu.h"
#include "game/ufopaedia/ufopaedia.h"
#include "game/debugtools/debugmenu.h"
#include "framework/framework.h"

namespace OpenApoc
{

OptionsMenu::OptionsMenu(Framework &fw) : Stage(fw)
{
	menuform = fw.gamecore->GetForm("FORM_OPTIONSMENU");
}

OptionsMenu::~OptionsMenu() {}

void OptionsMenu::Begin() {}

void OptionsMenu::Pause() {}

void OptionsMenu::Resume() {}

void OptionsMenu::Finish() {}

void OptionsMenu::EventOccurred(Event *e)
{
	menuform->EventOccured(e);
	fw.gamecore->MouseCursor->EventOccured(e);

	if (e->Type == EVENT_KEY_DOWN)
	{
		if (e->Data.Keyboard.KeyCode == ALLEGRO_KEY_ESCAPE)
		{
			stageCmd.cmd = StageCmd::Command::POP;
			return;
		}
	}

	if (e->Type == EVENT_FORM_INTERACTION && e->Data.Forms.EventFlag == FormEventType::ButtonClick)
	{
		if (e->Data.Forms.RaisedBy->Name == "BUTTON_TEST_XCOMBASE")
		{
			return;
		}
		if (e->Data.Forms.RaisedBy->Name == "BUTTON_TEST_UFOPAEDIA")
		{
			stageCmd.cmd = StageCmd::Command::PUSH;
			stageCmd.nextStage = std::make_shared<Ufopaedia>(fw);
			return;
		}
		if (e->Data.Forms.RaisedBy->Name == "BUTTON_DEBUGGING")
		{
			stageCmd.cmd = StageCmd::Command::PUSH;
			stageCmd.nextStage = std::make_shared<DebugMenu>(fw);
			return;
		}
		if (e->Data.Forms.RaisedBy->Name == "BUTTON_QUIT")
		{
			stageCmd.cmd = StageCmd::Command::POP;
			return;
		}
	}
}

void OptionsMenu::Update(StageCmd *const cmd)
{
	menuform->Update();
	*cmd = this->stageCmd;
	// Reset the command to default
	this->stageCmd = StageCmd();
}

void OptionsMenu::Render()
{
	fw.Stage_GetPrevious(this->shared_from_this())->Render();
	fw.renderer->drawFilledRect({0, 0}, fw.Display_GetSize(), Colour{0, 0, 0, 128});
	menuform->Render();
	fw.gamecore->MouseCursor->Render();
}

bool OptionsMenu::IsTransition() { return false; }

}; // namespace OpenApoc
<|endoftext|>


<commit_before>#include "Base.h"
#include "HeightField.h"
#include "Image.h"
#include "FileSystem.h"

namespace gameplay
{

HeightField::HeightField(unsigned int columns, unsigned int rows)
    : _array(NULL), _cols(columns), _rows(rows)
{
    _array = new float[columns * rows];
}

HeightField::~HeightField()
{
    SAFE_DELETE_ARRAY(_array);
}

HeightField* HeightField::create(unsigned int columns, unsigned int rows)
{
    return new HeightField(columns, rows);
}

/**
 * @script{ignore}
 */
float normalizedHeightPacked(float r, float g, float b)
{
    // This formula is intended for 24-bit packed heightmap images (that are generated
    // with gameplay-encoder. However, it is also compatible with normal grayscale
    // heightmap images, with an error of approximately 0.4%. This can be seen by
    // setting r=g=b=x and comparing the grayscale height expression to the packed
    // height expression: the error is 2^-8 + 2^-16 which is just under 0.4%.
    return (256.0f*r + g + 0.00390625f*b) / 65536.0f;
}

HeightField* HeightField::createFromImage(const char* path, float heightMin, float heightMax)
{
    return create(path, 0, 0, heightMin, heightMax);
}

HeightField* HeightField::createFromRAW(const char* path, unsigned int width, unsigned int height, float heightMin, float heightMax)
{
    return create(path, width, height, heightMin, heightMax);
}

HeightField* HeightField::create(const char* path, unsigned int width, unsigned int height, float heightMin, float heightMax)
{
    GP_ASSERT(path);
    GP_ASSERT(heightMax >= heightMin);

    float heightScale = heightMax - heightMin;

    HeightField* heightfield = NULL;

    // Load height data from image
    std::string ext = FileSystem::getExtension(path);
    if (ext == ".PNG")
    {
        // Normal image
        Image* image = Image::create(path);
        if (!image)
            return NULL;

        unsigned int pixelSize = 0;
        switch (image->getFormat())
        {
            case Image::RGB:
                pixelSize = 3;
                break;
            case Image::RGBA:
                pixelSize = 4;
                break;
            default:
                SAFE_RELEASE(image);
                GP_WARN("Unsupported pixel format for heightfield image: %s.", path);
                return NULL;
        }

        // Calculate the heights for each pixel.
        heightfield = HeightField::create(image->getWidth(), image->getHeight());
        float* heights = heightfield->getArray();
        unsigned char* data = image->getData();
        int idx;
        for (unsigned int y = 0, i = 0; y < image->getHeight(); ++y)
        {
            for (unsigned int x = 0, w = image->getWidth(); x < w; ++x)
            {
                idx = (y*w + x) * pixelSize;
                heights[i++] = heightMin + normalizedHeightPacked(data[idx], data[idx + 1], data[idx + 2]) * heightScale;
            }
        }

        SAFE_RELEASE(image);
    }
    else if (ext == ".RAW" || ext == ".R16")
    {
        // RAW image (headerless)
        if (width < 2 || height < 2 || heightMax < 0)
        {
            GP_WARN("Invalid 'width', 'height' or 'heightMax' parameter for RAW heightfield image: %s.", path);
            return NULL;
        }

        // Load raw bytes
        int fileSize = 0;
        unsigned char* bytes = (unsigned char*)FileSystem::readAll(path, &fileSize);
        if (bytes == NULL)
        {
            GP_WARN("Falied to read bytes from RAW heightfield image: %s.", path);
            return NULL;
        }

        // Determine if the RAW file is 8-bit or 16-bit based on file size.
        int bits = (fileSize / (width * height)) * 8;
        if (bits != 8 && bits != 16)
        {
            GP_WARN("Invalid RAW file - must be 8-bit or 16-bit, but found neither: %s.", path);
            SAFE_DELETE_ARRAY(bytes);
            return NULL;
        }

        heightfield = HeightField::create(width, height);
        float* heights = heightfield->getArray();

        if (bits == 16)
        {
            // 16-bit (0-65535)
            int idx;
            for (unsigned int y = 0, i = 0; y < height; ++y)
            {
                for (unsigned int x = 0; x < width; ++x, ++i)
                {
                    idx = (y * width + x) << 1;
                    heights[i] = heightMin + ((bytes[idx] | (int)bytes[idx+1] << 8) / 65535.0f) * heightScale;
                }
            }
        }
        else
        {
            // 8-bit (0-255)
            for (unsigned int y = 0, i = 0; y < height; ++y)
            {
                for (unsigned int x = 0; x < width; ++x, ++i)
                {
                    heights[i] = heightMin + (bytes[y * width + x] / 255.0f) * heightScale;
                }
            }
        }

        SAFE_DELETE_ARRAY(bytes);
    }
    else
    {
        GP_WARN("Unsupported heightfield image format: %s.", path);
    }

    return heightfield;
}

float* HeightField::getArray() const
{
    return _array;
}

float HeightField::getHeight(float column, float row) const
{
    // Clamp to heightfield boundaries
    column = column < 0 ? 0 : (column > (_cols-1) ? (_cols-1) : column);
    row = row < 0 ? 0 : (row > (_rows-1) ? (_rows-1) : row);

    unsigned int x1 = column;
    unsigned int y1 = row;
    unsigned int x2 = x1 + 1;
    unsigned int y2 = y1 + 1;
    float tmp;
    float xFactor = modf(column, &tmp);
    float yFactor = modf(row, &tmp);
    float xFactorI = 1.0f - xFactor;
    float yFactorI = 1.0f - yFactor;

    if (x2 >= _cols && y2 >= _rows)
    {
        return _array[x1 + y1 * _cols];
    }
    else if (x2 >= _cols)
    {
        return _array[x1 + y1 * _cols] * yFactorI + _array[x1 + y2 * _cols] * yFactor;
    }
    else if (y2 >= _rows)
    {
        return _array[x1 + y1 * _cols] * xFactorI + _array[x2 + y1 * _cols] * xFactor;
    }
    else
    {
        float a = xFactorI * yFactorI;
        float b = xFactorI * yFactor;
        float c = xFactor * yFactor;
        float d = xFactor * yFactorI;
        return _array[x1 + y1 * _cols] * a + _array[x1 + y2 * _cols] * b +
            _array[x2 + y2 * _cols] * c + _array[x2 + y1 * _cols] * d;
    }
}

unsigned int HeightField::getColumnCount() const
{
    return _cols;
}

unsigned int HeightField::getRowCount() const
{
    return _rows;
}

}
<commit_msg>reverted changes made previously to HeightField<commit_after>#include "Base.h"
#include "HeightField.h"
#include "Image.h"
#include "FileSystem.h"

namespace gameplay
{

HeightField::HeightField(unsigned int columns, unsigned int rows)
    : _array(NULL), _cols(columns), _rows(rows)
{
    _array = new float[columns * rows];
}

HeightField::~HeightField()
{
    SAFE_DELETE_ARRAY(_array);
}

HeightField* HeightField::create(unsigned int columns, unsigned int rows)
{
    return new HeightField(columns, rows);
}

/**
 * @script{ignore}
 */
float normalizedHeightPacked(float r, float g, float b)
{
    // This formula is intended for 24-bit packed heightmap images (that are generated
    // with gameplay-encoder. However, it is also compatible with normal grayscale
    // heightmap images, with an error of approximately 0.4%. This can be seen by
    // setting r=g=b=x and comparing the grayscale height expression to the packed
    // height expression: the error is 2^-8 + 2^-16 which is just under 0.4%.
    return (256.0f*r + g + 0.00390625f*b) / 65536.0f;
}

HeightField* HeightField::createFromImage(const char* path, float heightMin, float heightMax)
{
    return create(path, 0, 0, heightMin, heightMax);
}

HeightField* HeightField::createFromRAW(const char* path, unsigned int width, unsigned int height, float heightMin, float heightMax)
{
    return create(path, width, height, heightMin, heightMax);
}

HeightField* HeightField::create(const char* path, unsigned int width, unsigned int height, float heightMin, float heightMax)
{
    GP_ASSERT(path);
    GP_ASSERT(heightMax >= heightMin);

    float heightScale = heightMax - heightMin;

    HeightField* heightfield = NULL;

    // Load height data from image
    std::string ext = FileSystem::getExtension(path);
    if (ext == ".PNG")
    {
        // Normal image
        Image* image = Image::create(path);
        if (!image)
            return NULL;

        unsigned int pixelSize = 0;
        switch (image->getFormat())
        {
            case Image::RGB:
                pixelSize = 3;
                break;
            case Image::RGBA:
                pixelSize = 4;
                break;
            default:
                SAFE_RELEASE(image);
                GP_WARN("Unsupported pixel format for heightfield image: %s.", path);
                return NULL;
        }

        // Calculate the heights for each pixel.
        heightfield = HeightField::create(image->getWidth(), image->getHeight());
        float* heights = heightfield->getArray();
        unsigned char* data = image->getData();
        int idx;
        for (int y = image->getHeight()-1, i = 0; y >= 0; --y)
        {
            for (unsigned int x = 0, w = image->getWidth(); x < w; ++x)
            {
                idx = (y*w + x) * pixelSize;
                heights[i++] = heightMin + normalizedHeightPacked(data[idx], data[idx + 1], data[idx + 2]) * heightScale;
            }
        }

        SAFE_RELEASE(image);
    }
    else if (ext == ".RAW" || ext == ".R16")
    {
        // RAW image (headerless)
        if (width < 2 || height < 2 || heightMax < 0)
        {
            GP_WARN("Invalid 'width', 'height' or 'heightMax' parameter for RAW heightfield image: %s.", path);
            return NULL;
        }

        // Load raw bytes
        int fileSize = 0;
        unsigned char* bytes = (unsigned char*)FileSystem::readAll(path, &fileSize);
        if (bytes == NULL)
        {
            GP_WARN("Falied to read bytes from RAW heightfield image: %s.", path);
            return NULL;
        }

        // Determine if the RAW file is 8-bit or 16-bit based on file size.
        int bits = (fileSize / (width * height)) * 8;
        if (bits != 8 && bits != 16)
        {
            GP_WARN("Invalid RAW file - must be 8-bit or 16-bit, but found neither: %s.", path);
            SAFE_DELETE_ARRAY(bytes);
            return NULL;
        }

        heightfield = HeightField::create(width, height);
        float* heights = heightfield->getArray();

        if (bits == 16)
        {
            // 16-bit (0-65535)
            int idx;
            for (unsigned int y = 0, i = 0; y < height; ++y)
            {
                for (unsigned int x = 0; x < width; ++x, ++i)
                {
                    idx = (y * width + x) << 1;
                    heights[i] = heightMin + ((bytes[idx] | (int)bytes[idx+1] << 8) / 65535.0f) * heightScale;
                }
            }
        }
        else
        {
            // 8-bit (0-255)
            for (unsigned int y = 0, i = 0; y < height; ++y)
            {
                for (unsigned int x = 0; x < width; ++x, ++i)
                {
                    heights[i] = heightMin + (bytes[y * width + x] / 255.0f) * heightScale;
                }
            }
        }

        SAFE_DELETE_ARRAY(bytes);
    }
    else
    {
        GP_WARN("Unsupported heightfield image format: %s.", path);
    }

    return heightfield;
}

float* HeightField::getArray() const
{
    return _array;
}

float HeightField::getHeight(float column, float row) const
{
    // Clamp to heightfield boundaries
    column = column < 0 ? 0 : (column > (_cols-1) ? (_cols-1) : column);
    row = row < 0 ? 0 : (row > (_rows-1) ? (_rows-1) : row);

    unsigned int x1 = column;
    unsigned int y1 = row;
    unsigned int x2 = x1 + 1;
    unsigned int y2 = y1 + 1;
    float tmp;
    float xFactor = modf(column, &tmp);
    float yFactor = modf(row, &tmp);
    float xFactorI = 1.0f - xFactor;
    float yFactorI = 1.0f - yFactor;

    if (x2 >= _cols && y2 >= _rows)
    {
        return _array[x1 + y1 * _cols];
    }
    else if (x2 >= _cols)
    {
        return _array[x1 + y1 * _cols] * yFactorI + _array[x1 + y2 * _cols] * yFactor;
    }
    else if (y2 >= _rows)
    {
        return _array[x1 + y1 * _cols] * xFactorI + _array[x2 + y1 * _cols] * xFactor;
    }
    else
    {
        float a = xFactorI * yFactorI;
        float b = xFactorI * yFactor;
        float c = xFactor * yFactor;
        float d = xFactor * yFactorI;
        return _array[x1 + y1 * _cols] * a + _array[x1 + y2 * _cols] * b +
            _array[x2 + y2 * _cols] * c + _array[x2 + y1 * _cols] * d;
    }
}

unsigned int HeightField::getColumnCount() const
{
    return _cols;
}

unsigned int HeightField::getRowCount() const
{
    return _rows;
}

}
<|endoftext|>