<commit_before>/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * Flacon - audio File Encoder
 * https://github.com/flacon/flacon
 *
 * Copyright: 2017
 *   Alexander Sokoloff <sokoloff.a@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include "diskpipline.h"
#include "splitter.h"
#include "encoder.h"
#include "gain.h"
#include "cuecreator.h"
#include "copycover.h"
#include "project.h"
#include "settings.h"
#include "outformat.h"

#include <QThread>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QCoreApplication>

/************************************************
 *
 ************************************************/
class WorkerThread: public QThread
{
public:
    explicit WorkerThread(Worker *worker, QObject *parent = nullptr):
        QThread(parent),
        mWorker(worker)
    {
        worker->moveToThread(this);
    }

    virtual ~WorkerThread()
    {
        quit();
        if (!wait(3000))
        {
            qWarning() << "Can't quit from thread" << mWorker;
            terminate();
            if (!wait(3000))
                qWarning() << "Can't terminate from thread" << mWorker;
        }
        mWorker->deleteLater();
    }

    void run()
    {
        mWorker->run();
    }

private:
    Worker *mWorker;
};

/************************************************
 *
 ************************************************/
class DiskPipeline::Data
{
public:
    Data():
        pipeline(nullptr),
        disk(nullptr),
        needStartSplitter(true),
        interrupted(false),
        preGapType(PreGapType::Skip)
    {
    }

    DiskPipeline *pipeline;
    const Disk *disk;
    QList<const Track*> tracks;
    bool needStartSplitter;
    QHash<const Track*, Track::Status> trackStatuses;
    QList<WorkerRequest> encoderRequests;
    QList<WorkerRequest> gainRequests;
    bool interrupted;
    QString workDir;
    PreGapType preGapType;

    void interrupt(Track::Status status);
    void startSplitterThread();
    void startEncoderThread(const WorkerRequest &req);
    void startTrackGainThread(const WorkerRequest &req);
    void startAlbumGainThread(QList<WorkerRequest> &reqs);
    bool createDir(const QString &dirName) const;
    bool createCue() const;
    bool copyCoverImage() const;
};

/************************************************
 *
 ************************************************/
bool DiskPipeline::Data::createDir(const QString &dirName) const
{
    QDir dir(dirName);

    if (! dir.mkpath("."))
    {
        Project::error(QObject::tr("I can't create directory \"%1\".").arg(dir.path()));
        return false;
    }

    if (!QFileInfo(dir.path()).isWritable())
    {
        Project::error(QObject::tr("I can't write to directory \"%1\".").arg(dir.path()));
        return false;
    }

    return true;
}

/************************************************
 *
 ************************************************/
DiskPipeline::DiskPipeline(const Disk *disk, QObject *parent) :
    QObject(parent),
    mData(new Data()),
    mTmpDir(nullptr)
{
    mData->pipeline = this;
    mData->disk = disk;
    mData->preGapType =  settings->createCue() ? settings->preGapType() : PreGapType::Skip;

    if (!settings->tmpDir().isEmpty())
    {
        mTmpDir = new QTemporaryDir(QString("%1/flacon.").arg(settings->tmpDir()));
        mTmpDir->setAutoRemove(true);
        mData->workDir = mTmpDir->path(); //QDir(QString("%1/flacon.%2").arg(settings->tmpDir()).arg(QCoreApplication::applicationPid())).absolutePath();
    }
    else
        mData->workDir = QFileInfo(disk->track(0)->resultFilePath()).dir().absolutePath();

    Splitter splitter(mData->disk, mData->workDir, mData->preGapType);
    mData->tracks = splitter.tracks();

    foreach (const Track *track, mData->tracks)
    {
        mData->trackStatuses.insert(track, Track::NotRunning);
    }

}

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

 ************************************************/
DiskPipeline::~DiskPipeline()
{
    delete mData;
    delete mTmpDir;
}

/************************************************
 *
 ************************************************/
bool DiskPipeline::init()
{
    if (!mData->createDir(mData->workDir))
        return false;

    foreach (const Track *track, mData->tracks)
    {
        QString dir = QFileInfo(track->resultFilePath()).absoluteDir().path();
        if (!mData->createDir(dir))
            return false;
    }

    return true;
}

/************************************************
 CREATE WORKER CHAINS
 ************************************************
              +--> Encoder ---> Track gain -->+
   Splitter ->+            ...                +-> Album gain --> this
              +--> Encoder ---> Track gain -->+

                                optional step    optional step

 ************************************************/
void DiskPipeline::startWorker(int *splitterCount, int *count)
{
    if (mData->interrupted)
        return;

    if (*count <= 0)
        return;

    if (*splitterCount > 0 && mData->needStartSplitter)
    {
        mData->startSplitterThread();
        --(*splitterCount);
        --(*count);
        return;
    }

    if (settings->outFormat()->gainType() == GainType::Track)
    {
        while (*count > 0 && !mData->gainRequests.isEmpty())
        {
            mData->startTrackGainThread(mData->gainRequests.takeFirst());
            --(*count);
        }
    }
    else if (settings->outFormat()->gainType() == GainType::Album)
    {
        if (*count > 0 && mData->gainRequests.count() == mData->tracks.count())
        {
            mData->startAlbumGainThread(mData->gainRequests);
            mData->gainRequests.clear();
            --(*count);
        }
    }

    while (*count > 0 && !mData->encoderRequests.isEmpty())
    {
        mData->startEncoderThread(mData->encoderRequests.takeFirst());
        --(*count);
    }
}

/************************************************
 *
 ************************************************/
bool DiskPipeline::Data::createCue() const
{
    if (!settings->createCue())
        return true;

    CueCreator cue(disk, preGapType);
    if (!cue.write())
    {
        pipeline->trackError(tracks.first(), cue.errorString());
        return false;
    }

    return true;
}

/************************************************
 *
 ************************************************/
bool DiskPipeline::Data::copyCoverImage() const
{
    CoverMode mode = settings->coverMode();

    if (mode == CoverMode::Disable)
        return true;

    int size = 0;
    if (mode == CoverMode::Scale)
        size = settings->coverImageSize();

    QString dir = QFileInfo(disk->track(0)->resultFilePath()).dir().absolutePath();

    CopyCover copyCover(disk, dir, "cover", size);
    bool res = copyCover.run();

    if (!res)
        Project::error(copyCover.errorString());

    return res;
}

/************************************************
 *
 ************************************************/
void DiskPipeline::Data::startSplitterThread()
{
    Splitter *worker = new Splitter(disk, workDir, preGapType);

    WorkerThread *thread = new WorkerThread(worker, pipeline);

    connect(pipeline, SIGNAL(threadQuit()),
            thread,   SLOT(terminate()));

    connect(worker,   SIGNAL(trackProgress(const Track*,Track::Status,int)),
            pipeline, SLOT(trackProgress(const Track*,Track::Status,int)));

    connect(worker,   SIGNAL(error(const Track*,QString)),
            pipeline, SLOT(trackError(const Track*,QString)));

    connect(worker,   SIGNAL(trackReady(const Track*,QString)),
            pipeline, SLOT(addEncoderRequest(const Track*,QString)));

    connect(thread,   SIGNAL(finished()),
            pipeline, SIGNAL(threadFinished()));

    pipeline->mThreads << thread;
    thread->start();

    needStartSplitter = false;
    trackStatuses.insert(disk->track(0), Track::Splitting);

    createCue();
    copyCoverImage();
}

/************************************************
 *
 ************************************************/
void DiskPipeline::Data::startEncoderThread(const WorkerRequest &req)
{
    Encoder *worker = new Encoder(req, settings->outFormat());
    WorkerThread *thread = new WorkerThread(worker, pipeline);

    connect(pipeline, SIGNAL(threadQuit()),
            thread,   SLOT(terminate()));

    connect(worker,   SIGNAL(trackProgress(const Track*,Track::Status,int)),
            pipeline, SLOT(trackProgress(const Track*,Track::Status,int)));

    connect(worker, SIGNAL(error(const Track*,QString)),
            pipeline, SLOT(trackError(const Track*,QString)));

    if (settings->outFormat()->gainType() == GainType::Disable)
    {
        connect(worker, SIGNAL(trackReady(const Track*,QString)),
                pipeline, SLOT(trackDone(const Track*,QString)));
    }
    else
    {
        connect(worker, SIGNAL(trackReady(const Track*,QString)),
                pipeline, SLOT(addGainRequest(const Track*,QString)));
    }

    connect(thread,   SIGNAL(finished()),
            pipeline, SIGNAL(threadFinished()));

    pipeline->mThreads << thread;
    thread->start();
}

/************************************************
 *
 ************************************************/
void DiskPipeline::Data::startTrackGainThread(const WorkerRequest &req)
{
    Gain *worker = new Gain(req, settings->outFormat());
    WorkerThread *thread = new WorkerThread(worker, pipeline);

    connect(pipeline, SIGNAL(threadQuit()),
            thread,   SLOT(terminate()));

    connect(worker,   SIGNAL(trackProgress(const Track*,Track::Status,int)),
            pipeline, SLOT(trackProgress(const Track*,Track::Status,int)));

    connect(worker,   SIGNAL(error(const Track*,QString)),
            pipeline, SLOT(trackError(const Track*,QString)));

    connect(worker,   SIGNAL(trackReady(const Track*,QString)),
            pipeline, SLOT(trackDone(const Track*,QString)));

    connect(thread,   SIGNAL(finished()),
            pipeline, SIGNAL(threadFinished()));

    pipeline->mThreads << thread;
    thread->start();
}

/************************************************
 *
 ************************************************/
void DiskPipeline::Data::startAlbumGainThread(QList<WorkerRequest> &reqs)
{
    Gain *worker = new Gain(reqs, settings->outFormat());
    WorkerThread *thread = new WorkerThread(worker, pipeline);

    connect(pipeline, SIGNAL(threadQuit()),
            thread,   SLOT(terminate()));

    connect(worker,   SIGNAL(trackProgress(const Track*,Track::Status,int)),
            pipeline, SLOT(trackProgress(const Track*,Track::Status,int)));

    connect(worker,   SIGNAL(error(const Track*,QString)),
            pipeline, SLOT(trackError(const Track*,QString)));

    connect(worker,   SIGNAL(trackReady(const Track*,QString)),
            pipeline, SLOT(trackDone(const Track*,QString)));

    connect(thread,   SIGNAL(finished()),
            pipeline, SIGNAL(threadFinished()));

    pipeline->mThreads << thread;
    thread->start();
}

/************************************************
 *
 ************************************************/
void DiskPipeline::addEncoderRequest(const Track *track, const QString &inputFile)
{
    trackProgress(track, Track::Queued, 0);
    QFileInfo trackFile(track->resultFilePath());
    QString outFile = trackFile.dir().filePath(
                QFileInfo(inputFile).baseName() +
                ".encoded." +
                trackFile.suffix());

    mData->encoderRequests << WorkerRequest(track, inputFile, outFile);
    emit readyStart();
}

/************************************************
 *
 ************************************************/
void DiskPipeline::addGainRequest(const Track *track, const QString &fileName)
{
    if (settings->outFormat()->gainType() == GainType::Album)
        trackProgress(track, Track::WaitGain, 0);
    else
        trackProgress(track, Track::Queued, 0);

    mData->gainRequests << WorkerRequest(track, fileName, fileName);
    emit readyStart();
}

/************************************************
 *
 ************************************************/
void DiskPipeline::trackDone(const Track *track, const QString &outFileName)
{
    // Track is ready, rename the file to the final name.
    QFile::remove(track->resultFilePath());
    QFile(outFileName).rename(track->resultFilePath());

    mData->trackStatuses.insert(track, Track::OK);
    const_cast<Track*>(track)->setProgress(Track::OK);

    emit threadFinished();

    if (!isRunning())
        emit finished();
}

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

 ************************************************/
void DiskPipeline::Data::interrupt(Track::Status status)
{
    interrupted = true;
    encoderRequests.clear();

    QHash<const Track*, Track::Status>::iterator it;
    for (it = trackStatuses.begin(); it != trackStatuses.end(); ++it)
    {
        switch (it.value())
        {
        case Track::Splitting:
        case Track::Encoding:
        case Track::Queued:
        case Track::WaitGain:
        case Track::CalcGain:
        case Track::WriteGain:
        case Track::NotRunning:
            it.value() = status;
            const_cast<Track*>(it.key())->setProgress(status);
            break;

        case Track::Canceled:
        case Track::Error:
        case Track::Aborted:
        case Track::OK:
            break;
        }
    }
}

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

 ************************************************/
void DiskPipeline::stop()
{
    mData->interrupt(Track::Aborted);
    emit threadQuit();
    emit threadFinished();

    emit finished();
}

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

 ************************************************/
void DiskPipeline::trackError(const Track *track, const QString &message)
{
    mData->trackStatuses.insert(track, Track::Error);
    const_cast<Track*>(track)->setProgress(Track::Error);
    mData->interrupt(Track::Aborted);
    emit threadQuit();
    emit threadFinished();

    emit finished();
    Project::error(message);
}

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

 ************************************************/
bool DiskPipeline::isRunning() const
{
    QHash<const Track*, Track::Status>::const_iterator it;
    for (it = mData->trackStatuses.begin(); it != mData->trackStatuses.end(); ++it)
    {
        switch (it.value())
        {
        case Track::Splitting:
        case Track::Encoding:
        case Track::Queued:
        case Track::WaitGain:
        case Track::CalcGain:
        case Track::WriteGain:
            return true;

        case Track::NotRunning:
        case Track::Canceled:
        case Track::Error:
        case Track::Aborted:
        case Track::OK:
            break;
        }
    }

    return false;
}

/************************************************
 *
 ************************************************/
int DiskPipeline::runningThreadCount() const
{
    int res = 0;
    foreach (WorkerThread *thread, mThreads)
    {
        if (thread->isRunning())
            ++res;
    }
    return res;
}

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

 ************************************************/
void DiskPipeline::trackProgress(const Track *track, Track::Status status, int percent)
{
    if (mData->interrupted)
        return;

    if (!track)
        return;

    mData->trackStatuses.insert(track, status);
    const_cast<Track*>(track)->setProgress(status, percent);
}
<commit_msg>Create temporary path<commit_after>/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * Flacon - audio File Encoder
 * https://github.com/flacon/flacon
 *
 * Copyright: 2017
 *   Alexander Sokoloff <sokoloff.a@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include "diskpipline.h"
#include "splitter.h"
#include "encoder.h"
#include "gain.h"
#include "cuecreator.h"
#include "copycover.h"
#include "project.h"
#include "settings.h"
#include "outformat.h"

#include <QThread>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QCoreApplication>

/************************************************
 *
 ************************************************/
class WorkerThread: public QThread
{
public:
    explicit WorkerThread(Worker *worker, QObject *parent = nullptr):
        QThread(parent),
        mWorker(worker)
    {
        worker->moveToThread(this);
    }

    virtual ~WorkerThread()
    {
        quit();
        if (!wait(3000))
        {
            qWarning() << "Can't quit from thread" << mWorker;
            terminate();
            if (!wait(3000))
                qWarning() << "Can't terminate from thread" << mWorker;
        }
        mWorker->deleteLater();
    }

    void run()
    {
        mWorker->run();
    }

private:
    Worker *mWorker;
};

/************************************************
 *
 ************************************************/
class DiskPipeline::Data
{
public:
    Data():
        pipeline(nullptr),
        disk(nullptr),
        needStartSplitter(true),
        interrupted(false),
        preGapType(PreGapType::Skip)
    {
    }

    DiskPipeline *pipeline;
    const Disk *disk;
    QList<const Track*> tracks;
    bool needStartSplitter;
    QHash<const Track*, Track::Status> trackStatuses;
    QList<WorkerRequest> encoderRequests;
    QList<WorkerRequest> gainRequests;
    bool interrupted;
    QString workDir;
    PreGapType preGapType;

    void interrupt(Track::Status status);
    void startSplitterThread();
    void startEncoderThread(const WorkerRequest &req);
    void startTrackGainThread(const WorkerRequest &req);
    void startAlbumGainThread(QList<WorkerRequest> &reqs);
    bool createDir(const QString &dirName) const;
    bool createCue() const;
    bool copyCoverImage() const;
};

/************************************************
 *
 ************************************************/
bool DiskPipeline::Data::createDir(const QString &dirName) const
{
    QDir dir(dirName);

    if (! dir.mkpath("."))
    {
        Project::error(QObject::tr("I can't create directory \"%1\".").arg(dir.path()));
        return false;
    }

    if (!QFileInfo(dir.path()).isWritable())
    {
        Project::error(QObject::tr("I can't write to directory \"%1\".").arg(dir.path()));
        return false;
    }

    return true;
}

/************************************************
 *
 ************************************************/
DiskPipeline::DiskPipeline(const Disk *disk, QObject *parent) :
    QObject(parent),
    mData(new Data()),
    mTmpDir(nullptr)
{
    mData->pipeline = this;
    mData->disk = disk;
    mData->preGapType =  settings->createCue() ? settings->preGapType() : PreGapType::Skip;

}

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

 ************************************************/
DiskPipeline::~DiskPipeline()
{
    delete mData;
    delete mTmpDir;
}

/************************************************
 *
 ************************************************/
bool DiskPipeline::init()
{
    if (!settings->tmpDir().isEmpty())
    {
        if (!mData->createDir(settings->tmpDir()))
            return false;
        mTmpDir = new QTemporaryDir(QString("%1/flacon.").arg(settings->tmpDir()));
        mTmpDir->setAutoRemove(true);
        mData->workDir = mTmpDir->path();
    }
    else
        mData->workDir = QFileInfo(mData->disk->track(0)->resultFilePath()).dir().absolutePath();

    Splitter splitter(mData->disk, mData->workDir, mData->preGapType);
    mData->tracks = splitter.tracks();

    foreach (const Track *track, mData->tracks)
    {
        mData->trackStatuses.insert(track, Track::NotRunning);
    }

    if (!mData->createDir(mData->workDir))
        return false;

    foreach (const Track *track, mData->tracks)
    {
        QString dir = QFileInfo(track->resultFilePath()).absoluteDir().path();
        if (!mData->createDir(dir))
            return false;
    }

    return true;
}

/************************************************
 CREATE WORKER CHAINS
 ************************************************
              +--> Encoder ---> Track gain -->+
   Splitter ->+            ...                +-> Album gain --> this
              +--> Encoder ---> Track gain -->+

                                optional step    optional step

 ************************************************/
void DiskPipeline::startWorker(int *splitterCount, int *count)
{
    if (mData->interrupted)
        return;

    if (*count <= 0)
        return;

    if (*splitterCount > 0 && mData->needStartSplitter)
    {
        mData->startSplitterThread();
        --(*splitterCount);
        --(*count);
        return;
    }

    if (settings->outFormat()->gainType() == GainType::Track)
    {
        while (*count > 0 && !mData->gainRequests.isEmpty())
        {
            mData->startTrackGainThread(mData->gainRequests.takeFirst());
            --(*count);
        }
    }
    else if (settings->outFormat()->gainType() == GainType::Album)
    {
        if (*count > 0 && mData->gainRequests.count() == mData->tracks.count())
        {
            mData->startAlbumGainThread(mData->gainRequests);
            mData->gainRequests.clear();
            --(*count);
        }
    }

    while (*count > 0 && !mData->encoderRequests.isEmpty())
    {
        mData->startEncoderThread(mData->encoderRequests.takeFirst());
        --(*count);
    }
}

/************************************************
 *
 ************************************************/
bool DiskPipeline::Data::createCue() const
{
    if (!settings->createCue())
        return true;

    CueCreator cue(disk, preGapType);
    if (!cue.write())
    {
        pipeline->trackError(tracks.first(), cue.errorString());
        return false;
    }

    return true;
}

/************************************************
 *
 ************************************************/
bool DiskPipeline::Data::copyCoverImage() const
{
    CoverMode mode = settings->coverMode();

    if (mode == CoverMode::Disable)
        return true;

    int size = 0;
    if (mode == CoverMode::Scale)
        size = settings->coverImageSize();

    QString dir = QFileInfo(disk->track(0)->resultFilePath()).dir().absolutePath();

    CopyCover copyCover(disk, dir, "cover", size);
    bool res = copyCover.run();

    if (!res)
        Project::error(copyCover.errorString());

    return res;
}

/************************************************
 *
 ************************************************/
void DiskPipeline::Data::startSplitterThread()
{
    Splitter *worker = new Splitter(disk, workDir, preGapType);

    WorkerThread *thread = new WorkerThread(worker, pipeline);

    connect(pipeline, SIGNAL(threadQuit()),
            thread,   SLOT(terminate()));

    connect(worker,   SIGNAL(trackProgress(const Track*,Track::Status,int)),
            pipeline, SLOT(trackProgress(const Track*,Track::Status,int)));

    connect(worker,   SIGNAL(error(const Track*,QString)),
            pipeline, SLOT(trackError(const Track*,QString)));

    connect(worker,   SIGNAL(trackReady(const Track*,QString)),
            pipeline, SLOT(addEncoderRequest(const Track*,QString)));

    connect(thread,   SIGNAL(finished()),
            pipeline, SIGNAL(threadFinished()));

    pipeline->mThreads << thread;
    thread->start();

    needStartSplitter = false;
    trackStatuses.insert(disk->track(0), Track::Splitting);

    createCue();
    copyCoverImage();
}

/************************************************
 *
 ************************************************/
void DiskPipeline::Data::startEncoderThread(const WorkerRequest &req)
{
    Encoder *worker = new Encoder(req, settings->outFormat());
    WorkerThread *thread = new WorkerThread(worker, pipeline);

    connect(pipeline, SIGNAL(threadQuit()),
            thread,   SLOT(terminate()));

    connect(worker,   SIGNAL(trackProgress(const Track*,Track::Status,int)),
            pipeline, SLOT(trackProgress(const Track*,Track::Status,int)));

    connect(worker, SIGNAL(error(const Track*,QString)),
            pipeline, SLOT(trackError(const Track*,QString)));

    if (settings->outFormat()->gainType() == GainType::Disable)
    {
        connect(worker, SIGNAL(trackReady(const Track*,QString)),
                pipeline, SLOT(trackDone(const Track*,QString)));
    }
    else
    {
        connect(worker, SIGNAL(trackReady(const Track*,QString)),
                pipeline, SLOT(addGainRequest(const Track*,QString)));
    }

    connect(thread,   SIGNAL(finished()),
            pipeline, SIGNAL(threadFinished()));

    pipeline->mThreads << thread;
    thread->start();
}

/************************************************
 *
 ************************************************/
void DiskPipeline::Data::startTrackGainThread(const WorkerRequest &req)
{
    Gain *worker = new Gain(req, settings->outFormat());
    WorkerThread *thread = new WorkerThread(worker, pipeline);

    connect(pipeline, SIGNAL(threadQuit()),
            thread,   SLOT(terminate()));

    connect(worker,   SIGNAL(trackProgress(const Track*,Track::Status,int)),
            pipeline, SLOT(trackProgress(const Track*,Track::Status,int)));

    connect(worker,   SIGNAL(error(const Track*,QString)),
            pipeline, SLOT(trackError(const Track*,QString)));

    connect(worker,   SIGNAL(trackReady(const Track*,QString)),
            pipeline, SLOT(trackDone(const Track*,QString)));

    connect(thread,   SIGNAL(finished()),
            pipeline, SIGNAL(threadFinished()));

    pipeline->mThreads << thread;
    thread->start();
}

/************************************************
 *
 ************************************************/
void DiskPipeline::Data::startAlbumGainThread(QList<WorkerRequest> &reqs)
{
    Gain *worker = new Gain(reqs, settings->outFormat());
    WorkerThread *thread = new WorkerThread(worker, pipeline);

    connect(pipeline, SIGNAL(threadQuit()),
            thread,   SLOT(terminate()));

    connect(worker,   SIGNAL(trackProgress(const Track*,Track::Status,int)),
            pipeline, SLOT(trackProgress(const Track*,Track::Status,int)));

    connect(worker,   SIGNAL(error(const Track*,QString)),
            pipeline, SLOT(trackError(const Track*,QString)));

    connect(worker,   SIGNAL(trackReady(const Track*,QString)),
            pipeline, SLOT(trackDone(const Track*,QString)));

    connect(thread,   SIGNAL(finished()),
            pipeline, SIGNAL(threadFinished()));

    pipeline->mThreads << thread;
    thread->start();
}

/************************************************
 *
 ************************************************/
void DiskPipeline::addEncoderRequest(const Track *track, const QString &inputFile)
{
    trackProgress(track, Track::Queued, 0);
    QFileInfo trackFile(track->resultFilePath());
    QString outFile = trackFile.dir().filePath(
                QFileInfo(inputFile).baseName() +
                ".encoded." +
                trackFile.suffix());

    mData->encoderRequests << WorkerRequest(track, inputFile, outFile);
    emit readyStart();
}

/************************************************
 *
 ************************************************/
void DiskPipeline::addGainRequest(const Track *track, const QString &fileName)
{
    if (settings->outFormat()->gainType() == GainType::Album)
        trackProgress(track, Track::WaitGain, 0);
    else
        trackProgress(track, Track::Queued, 0);

    mData->gainRequests << WorkerRequest(track, fileName, fileName);
    emit readyStart();
}

/************************************************
 *
 ************************************************/
void DiskPipeline::trackDone(const Track *track, const QString &outFileName)
{
    // Track is ready, rename the file to the final name.
    QFile::remove(track->resultFilePath());
    QFile(outFileName).rename(track->resultFilePath());

    mData->trackStatuses.insert(track, Track::OK);
    const_cast<Track*>(track)->setProgress(Track::OK);

    emit threadFinished();

    if (!isRunning())
        emit finished();
}

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

 ************************************************/
void DiskPipeline::Data::interrupt(Track::Status status)
{
    interrupted = true;
    encoderRequests.clear();

    QHash<const Track*, Track::Status>::iterator it;
    for (it = trackStatuses.begin(); it != trackStatuses.end(); ++it)
    {
        switch (it.value())
        {
        case Track::Splitting:
        case Track::Encoding:
        case Track::Queued:
        case Track::WaitGain:
        case Track::CalcGain:
        case Track::WriteGain:
        case Track::NotRunning:
            it.value() = status;
            const_cast<Track*>(it.key())->setProgress(status);
            break;

        case Track::Canceled:
        case Track::Error:
        case Track::Aborted:
        case Track::OK:
            break;
        }
    }
}

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

 ************************************************/
void DiskPipeline::stop()
{
    mData->interrupt(Track::Aborted);
    emit threadQuit();
    emit threadFinished();

    emit finished();
}

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

 ************************************************/
void DiskPipeline::trackError(const Track *track, const QString &message)
{
    mData->trackStatuses.insert(track, Track::Error);
    const_cast<Track*>(track)->setProgress(Track::Error);
    mData->interrupt(Track::Aborted);
    emit threadQuit();
    emit threadFinished();

    emit finished();
    Project::error(message);
}

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

 ************************************************/
bool DiskPipeline::isRunning() const
{
    QHash<const Track*, Track::Status>::const_iterator it;
    for (it = mData->trackStatuses.begin(); it != mData->trackStatuses.end(); ++it)
    {
        switch (it.value())
        {
        case Track::Splitting:
        case Track::Encoding:
        case Track::Queued:
        case Track::WaitGain:
        case Track::CalcGain:
        case Track::WriteGain:
            return true;

        case Track::NotRunning:
        case Track::Canceled:
        case Track::Error:
        case Track::Aborted:
        case Track::OK:
            break;
        }
    }

    return false;
}

/************************************************
 *
 ************************************************/
int DiskPipeline::runningThreadCount() const
{
    int res = 0;
    foreach (WorkerThread *thread, mThreads)
    {
        if (thread->isRunning())
            ++res;
    }
    return res;
}

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

 ************************************************/
void DiskPipeline::trackProgress(const Track *track, Track::Status status, int percent)
{
    if (mData->interrupted)
        return;

    if (!track)
        return;

    mData->trackStatuses.insert(track, status);
    const_cast<Track*>(track)->setProgress(status, percent);
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2009, Willow Garage, Inc.
 *
 * 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 names of Stanford University or Willow Garage, Inc. 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.
 */

#include "ros/debug.h"

#if defined(HAVE_EXECINFO_H)
#include <execinfo.h>
#endif

#if defined(HAVE_CXXABI_H)
#include <cxxabi.h>
#endif

#include <cstdlib>
#include <cstdio>
#include <sstream>

namespace ros
{
namespace debug
{

void getBacktrace(V_void& addresses)
{
#if HAVE_GLIBC_BACKTRACE
  void *array[64];

  size_t size = backtrace(array, 64);
  for (size_t i = 1; i < size; i++)
  {
    addresses.push_back(array[i]);
  }
#endif
}

void translateAddresses(const V_void& addresses, V_string& lines)
{
#if HAVE_GLIBC_BACKTRACE
  if (addresses.empty())
  {
    return;
  }

  size_t size = addresses.size();
  char **strings = backtrace_symbols(&addresses.front(), size);

  for (size_t i = 0; i < size; ++i)
  {
    lines.push_back(strings[i]);
  }

  free(strings);
#endif
}

std::string demangleName(const std::string& name)
{
#if HAVE_CXXABI_H
  int status;
  char* demangled = abi::__cxa_demangle(name.c_str(), 0, 0, &status);
  std::string out;
  if (demangled)
  {
    out = demangled;
    free(demangled);
  }
  else
  {
    out = name;
  }

  printf("name: %s, demangled: %s\n", name.c_str(), out.c_str());
  return out;
#else
  return name;
#endif
}

std::string demangleBacktraceLine(const std::string& line)
{
  // backtrace_symbols outputs in the form:
  // executable(function+offset) [address]
  // We want everything between ( and + to send to demangleName()
  size_t paren_pos = line.find('(');
  size_t plus_pos = line.find('+');
  if (paren_pos == std::string::npos || plus_pos == std::string::npos)
  {
    return line;
  }

  std::string name(line, paren_pos + 1, plus_pos - paren_pos - 1);
  return line.substr(0, paren_pos + 1) + demangleName(name) + line.substr(plus_pos);
}

void demangleBacktrace(const V_string& lines, V_string& demangled)
{
  V_string::const_iterator it = lines.begin();
  V_string::const_iterator end = lines.end();
  for (; it != end; ++it)
  {
    demangled.push_back(demangleBacktraceLine(*it));
  }
}

std::string backtraceToString(const V_void& addresses)
{
  V_string lines, demangled;
  translateAddresses(addresses, lines);
  demangleBacktrace(lines, demangled);

  std::stringstream ss;
  V_string::const_iterator it = demangled.begin();
  V_string::const_iterator end = demangled.end();
  for (; it != end; ++it)
  {
    ss << *it << std::endl;
  }

  return ss.str();
}

std::string getBacktrace()
{
  V_void addresses;
  getBacktrace(addresses);
  return backtraceToString(addresses);
}

}

}
<commit_msg>didn't mean to leave that printf in<commit_after>/*
 * Copyright (C) 2009, Willow Garage, Inc.
 *
 * 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 names of Stanford University or Willow Garage, Inc. 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.
 */

#include "ros/debug.h"

#if defined(HAVE_EXECINFO_H)
#include <execinfo.h>
#endif

#if defined(HAVE_CXXABI_H)
#include <cxxabi.h>
#endif

#include <cstdlib>
#include <cstdio>
#include <sstream>

namespace ros
{
namespace debug
{

void getBacktrace(V_void& addresses)
{
#if HAVE_GLIBC_BACKTRACE
  void *array[64];

  size_t size = backtrace(array, 64);
  for (size_t i = 1; i < size; i++)
  {
    addresses.push_back(array[i]);
  }
#endif
}

void translateAddresses(const V_void& addresses, V_string& lines)
{
#if HAVE_GLIBC_BACKTRACE
  if (addresses.empty())
  {
    return;
  }

  size_t size = addresses.size();
  char **strings = backtrace_symbols(&addresses.front(), size);

  for (size_t i = 0; i < size; ++i)
  {
    lines.push_back(strings[i]);
  }

  free(strings);
#endif
}

std::string demangleName(const std::string& name)
{
#if HAVE_CXXABI_H
  int status;
  char* demangled = abi::__cxa_demangle(name.c_str(), 0, 0, &status);
  std::string out;
  if (demangled)
  {
    out = demangled;
    free(demangled);
  }
  else
  {
    out = name;
  }

  return out;
#else
  return name;
#endif
}

std::string demangleBacktraceLine(const std::string& line)
{
  // backtrace_symbols outputs in the form:
  // executable(function+offset) [address]
  // We want everything between ( and + to send to demangleName()
  size_t paren_pos = line.find('(');
  size_t plus_pos = line.find('+');
  if (paren_pos == std::string::npos || plus_pos == std::string::npos)
  {
    return line;
  }

  std::string name(line, paren_pos + 1, plus_pos - paren_pos - 1);
  return line.substr(0, paren_pos + 1) + demangleName(name) + line.substr(plus_pos);
}

void demangleBacktrace(const V_string& lines, V_string& demangled)
{
  V_string::const_iterator it = lines.begin();
  V_string::const_iterator end = lines.end();
  for (; it != end; ++it)
  {
    demangled.push_back(demangleBacktraceLine(*it));
  }
}

std::string backtraceToString(const V_void& addresses)
{
  V_string lines, demangled;
  translateAddresses(addresses, lines);
  demangleBacktrace(lines, demangled);

  std::stringstream ss;
  V_string::const_iterator it = demangled.begin();
  V_string::const_iterator end = demangled.end();
  for (; it != end; ++it)
  {
    ss << *it << std::endl;
  }

  return ss.str();
}

std::string getBacktrace()
{
  V_void addresses;
  getBacktrace(addresses);
  return backtraceToString(addresses);
}

}

}
<|endoftext|>


<commit_before>#include "label.h"

#include "util/geom.h"
#include "glm/gtx/rotate_vector.hpp"
#include "tangram.h"

namespace Tangram {

Label::Label(Label::Transform _transform, glm::vec2 _size, Type _type, Options _options, LabelProperty::Anchor _anchor)
    : m_type(_type),
      m_transform(_transform),
      m_dim(_size),
      m_options(_options),
      m_anchorType(_anchor) {

    if (!m_options.collide || m_type == Type::debug){
        enterState(State::visible, 1.0);
    } else {
        m_state = State::wait_occ;
        m_transform.state.alpha = 0.0;
    }

    m_occludedLastFrame = false;
    m_updateMeshVisibility = true;
    m_dirty = true;
    m_proxy = false;
    m_xAxis = glm::vec2(1.0, 0.0);
    m_yAxis = glm::vec2(0.0, 1.0);
    m_occluded = false;
    m_parent = nullptr;
}

Label::~Label() {}

void Label::setProxy(bool _proxy) {
    m_proxy = _proxy;
}

bool Label::updateScreenTransform(const glm::mat4& _mvp, const glm::vec2& _screenSize, bool _testVisibility) {

    glm::vec2 screenPosition;
    float rot = 0;

    glm::vec2 ap1, ap2;

    switch (m_type) {
        case Type::debug:
        case Type::point:
        {
            glm::vec4 v1 = worldToClipSpace(_mvp, glm::vec4(m_transform.modelPosition1, 0.0, 1.0));

            if (_testVisibility && (v1.w <= 0)) {
                return false;
            }

            screenPosition = clipToScreenSpace(v1, _screenSize);

            ap1 = ap2 = screenPosition;

            break;
        }
        case Type::line:
        {
            // project label position from mercator world space to clip
            // coordinates
            glm::vec4 v1 = worldToClipSpace(_mvp, glm::vec4(m_transform.modelPosition1, 0.0, 1.0));
            glm::vec4 v2 = worldToClipSpace(_mvp, glm::vec4(m_transform.modelPosition2, 0.0, 1.0));

            // check whether the label is behind the camera using the
            // perspective division factor
            if (_testVisibility && (v1.w <= 0 || v2.w <= 0)) {
                return false;
            }

            // project to screen space
            glm::vec2 p1 = clipToScreenSpace(v1, _screenSize);
            glm::vec2 p2 = clipToScreenSpace(v2, _screenSize);

            rot = angleBetweenPoints(p1, p2) + M_PI_2;

            if (rot > M_PI_2 || rot < -M_PI_2) { // un-readable labels
                rot += M_PI;
            } else {
                std::swap(p1, p2);
            }

            float length = glm::length(p2 - p1);

            float exceedHeuristic = 30; // default heuristic : 30%

            if (_testVisibility && (m_dim.x > length)) {
                float exceed = (1 - (length / m_dim.x)) * 100;
                if (exceed > exceedHeuristic) {
                    return false;
                }
            }

            ap1 = p1;
            ap2 = p2;

            break;
        }
    }

    align(screenPosition, ap1, ap2);

    // update screen position
    glm::vec2 offset = m_options.offset;

    if (m_transform.state.rotation != 0.f) {
        offset = glm::rotate(offset, m_transform.state.rotation);
    }

    glm::vec2 newScreenPos = screenPosition + offset;
    if (newScreenPos != m_transform.state.screenPos) {
        m_transform.state.screenPos = newScreenPos;
        m_dirty = true;
    }

    // update screen rotation
    if (m_transform.state.rotation != rot) {
        m_transform.state.rotation = rot;
        m_dirty = true;
    }

    return true;
}

void Label::setParent(const Label& _parent, bool _definePriority) {
    m_parent = &_parent;

    glm::vec2 anchorDir = LabelProperty::anchorDirection(_parent.anchorType());
    glm::vec2 anchorOrigin = anchorDir * _parent.dimension() * 0.5f;
    applyAnchor(m_dim + _parent.dimension(), anchorOrigin, m_anchorType);

    if (_definePriority) {
        m_options.priority = _parent.options().priority + 0.5f;
    }

    m_options.offset += _parent.options().offset;
}

bool Label::offViewport(const glm::vec2& _screenSize) {
    const auto& quad = m_obb.getQuad();

    for (int i = 0; i < 4; ++i) {
        const auto& p = quad[i];
        if (p.x < _screenSize.x && p.y < _screenSize.y && p.x > 0 && p.y > 0) {
            return false;
        }
    }

    return true;
}

void Label::occlude(bool _occlusion) {
    m_occluded = _occlusion;
}

bool Label::canOcclude() {
    if (!m_options.collide) {
        return false;
    }

    int occludeFlags = (State::visible |
                        State::wait_occ |
                        State::skip_transition |
                        State::fading_in |
                        State::sleep |
                        State::out_of_screen |
                        State::dead);

    return (occludeFlags & m_state) && !(m_type == Type::debug);
}

bool Label::visibleState() const {
    int visibleFlags = (State::visible |
                        State::fading_in |
                        State::fading_out |
                        State::skip_transition);

    return (visibleFlags & m_state);
}

void Label::skipTransitions() {
    enterState(State::skip_transition, 0.0);
}

glm::vec2 Label::center() const {
    return m_obb.getCentroid();
}

void Label::enterState(const State& _state, float _alpha) {
    if (m_state == State::dead) { return; }

    m_state = _state;
    setAlpha(_alpha);
}

void Label::setAlpha(float _alpha) {
    float alpha = CLAMP(_alpha, 0.0, 1.0);
    if (m_transform.state.alpha != alpha) {
        m_transform.state.alpha = alpha;
        m_dirty = true;
    }

    if (alpha == 0.f) {
        m_updateMeshVisibility = true;
    }
}

void Label::resetState() {
    if (m_state == State::dead) { return; }

    m_occludedLastFrame = false;
    m_occluded = false;
    m_updateMeshVisibility = true;
    m_dirty = true;
    m_proxy = false;
    enterState(State::wait_occ, 0.0);
}

bool Label::update(const glm::mat4& _mvp, const glm::vec2& _screenSize, float _zoomFract, bool _allLabels) {

    m_occludedLastFrame = m_occluded;
    if (m_state != State::fading_out) {
        m_occluded = false;
    }

    if (m_state == State::dead) {
        if (!_allLabels) {
            return false;
        } else {
            m_occluded = true;
        }
    }

    bool ruleSatisfied = updateScreenTransform(_mvp, _screenSize, !_allLabels);

    // one of the label rules has not been satisfied
    if (!ruleSatisfied) {
        enterState(State::sleep, 0.0);
        return false;
    }

    // update the view-space bouding box
    updateBBoxes(_zoomFract);

    // checks whether the label is out of the viewport
    if (offViewport(_screenSize)) {
        enterState(State::out_of_screen, 0.0);
    }

    return true;
}

bool Label::evalState(const glm::vec2& _screenSize, float _dt) {

    // if (Tangram::getDebugFlag(DebugFlags::all_labels)) {
    //     enterState(State::visible, 1.0);
    //     return false;
    // }

    bool animate = false;

    switch (m_state) {
        case State::visible:
            if (m_occluded) {
                m_fade = FadeEffect(false, m_options.hideTransition.ease,
                                    m_options.hideTransition.time);
                enterState(State::fading_out, 1.0);
                animate = true;
            }
            break;
        case State::fading_in:
            if (m_occluded) {
                enterState(State::sleep, 0.0);
                // enterState(State::fading_out, m_transform.state.alpha);
                // animate = true;
                break;
            }
            setAlpha(m_fade.update(_dt));
            animate = true;
            if (m_fade.isFinished()) {
                enterState(State::visible, 1.0);
            }
            break;
        case State::fading_out:
            // if (m_occluded) {
            //     enterState(State::fading_in, m_transform.state.alpha);
            //     animate = true;
            //     break;
            // }
            setAlpha(m_fade.update(_dt));
            animate = true;
            if (m_fade.isFinished()) {
                enterState(State::sleep, 0.0);
            }
            break;
        case State::out_of_screen:
            if (!offViewport(_screenSize)) {
                enterState(State::wait_occ, 0.0);
            }
            break;
        case State::wait_occ:
            if (m_occluded) {
                // if (m_parent) {
                enterState(State::sleep, 0.0);
                // } else {
                //     enterState(State::dead, 0.0);
                // }
            } else {
                m_fade = FadeEffect(true, m_options.showTransition.ease,
                                    m_options.showTransition.time);
                enterState(State::fading_in, 0.0);
                animate = true;
            }
            break;
        case State::skip_transition:
            if (m_occluded) {
                enterState(State::sleep, 0.0);
                // enterState(State::dead, 0.0);
            } else {
                enterState(State::visible, 1.0);
            }
            break;
        case State::sleep:
            if (!m_occluded) {
                m_fade = FadeEffect(true, m_options.showTransition.ease,
                                    m_options.showTransition.time);
                enterState(State::fading_in, 0.0);
                animate = true;
            }
            break;
        case State::dead:
            break;
    }

    return animate;
}

}
<commit_msg>let non-occluded labels fade-in again while in fade-out state<commit_after>#include "label.h"

#include "util/geom.h"
#include "glm/gtx/rotate_vector.hpp"
#include "tangram.h"

namespace Tangram {

Label::Label(Label::Transform _transform, glm::vec2 _size, Type _type, Options _options, LabelProperty::Anchor _anchor)
    : m_type(_type),
      m_transform(_transform),
      m_dim(_size),
      m_options(_options),
      m_anchorType(_anchor) {

    if (!m_options.collide || m_type == Type::debug){
        enterState(State::visible, 1.0);
    } else {
        m_state = State::wait_occ;
        m_transform.state.alpha = 0.0;
    }

    m_occludedLastFrame = false;
    m_updateMeshVisibility = true;
    m_dirty = true;
    m_proxy = false;
    m_xAxis = glm::vec2(1.0, 0.0);
    m_yAxis = glm::vec2(0.0, 1.0);
    m_occluded = false;
    m_parent = nullptr;
}

Label::~Label() {}

void Label::setProxy(bool _proxy) {
    m_proxy = _proxy;
}

bool Label::updateScreenTransform(const glm::mat4& _mvp, const glm::vec2& _screenSize, bool _testVisibility) {

    glm::vec2 screenPosition;
    float rot = 0;

    glm::vec2 ap1, ap2;

    switch (m_type) {
        case Type::debug:
        case Type::point:
        {
            glm::vec4 v1 = worldToClipSpace(_mvp, glm::vec4(m_transform.modelPosition1, 0.0, 1.0));

            if (_testVisibility && (v1.w <= 0)) {
                return false;
            }

            screenPosition = clipToScreenSpace(v1, _screenSize);

            ap1 = ap2 = screenPosition;

            break;
        }
        case Type::line:
        {
            // project label position from mercator world space to clip
            // coordinates
            glm::vec4 v1 = worldToClipSpace(_mvp, glm::vec4(m_transform.modelPosition1, 0.0, 1.0));
            glm::vec4 v2 = worldToClipSpace(_mvp, glm::vec4(m_transform.modelPosition2, 0.0, 1.0));

            // check whether the label is behind the camera using the
            // perspective division factor
            if (_testVisibility && (v1.w <= 0 || v2.w <= 0)) {
                return false;
            }

            // project to screen space
            glm::vec2 p1 = clipToScreenSpace(v1, _screenSize);
            glm::vec2 p2 = clipToScreenSpace(v2, _screenSize);

            rot = angleBetweenPoints(p1, p2) + M_PI_2;

            if (rot > M_PI_2 || rot < -M_PI_2) { // un-readable labels
                rot += M_PI;
            } else {
                std::swap(p1, p2);
            }

            float length = glm::length(p2 - p1);

            float exceedHeuristic = 30; // default heuristic : 30%

            if (_testVisibility && (m_dim.x > length)) {
                float exceed = (1 - (length / m_dim.x)) * 100;
                if (exceed > exceedHeuristic) {
                    return false;
                }
            }

            ap1 = p1;
            ap2 = p2;

            break;
        }
    }

    align(screenPosition, ap1, ap2);

    // update screen position
    glm::vec2 offset = m_options.offset;

    if (m_transform.state.rotation != 0.f) {
        offset = glm::rotate(offset, m_transform.state.rotation);
    }

    glm::vec2 newScreenPos = screenPosition + offset;
    if (newScreenPos != m_transform.state.screenPos) {
        m_transform.state.screenPos = newScreenPos;
        m_dirty = true;
    }

    // update screen rotation
    if (m_transform.state.rotation != rot) {
        m_transform.state.rotation = rot;
        m_dirty = true;
    }

    return true;
}

void Label::setParent(const Label& _parent, bool _definePriority) {
    m_parent = &_parent;

    glm::vec2 anchorDir = LabelProperty::anchorDirection(_parent.anchorType());
    glm::vec2 anchorOrigin = anchorDir * _parent.dimension() * 0.5f;
    applyAnchor(m_dim + _parent.dimension(), anchorOrigin, m_anchorType);

    if (_definePriority) {
        m_options.priority = _parent.options().priority + 0.5f;
    }

    m_options.offset += _parent.options().offset;
}

bool Label::offViewport(const glm::vec2& _screenSize) {
    const auto& quad = m_obb.getQuad();

    for (int i = 0; i < 4; ++i) {
        const auto& p = quad[i];
        if (p.x < _screenSize.x && p.y < _screenSize.y && p.x > 0 && p.y > 0) {
            return false;
        }
    }

    return true;
}

void Label::occlude(bool _occlusion) {
    m_occluded = _occlusion;
}

bool Label::canOcclude() {
    if (!m_options.collide) {
        return false;
    }

    int occludeFlags = (State::visible |
                        State::wait_occ |
                        State::skip_transition |
                        State::fading_in |
                        State::sleep |
                        State::out_of_screen |
                        State::dead);

    return (occludeFlags & m_state) && !(m_type == Type::debug);
}

bool Label::visibleState() const {
    int visibleFlags = (State::visible |
                        State::fading_in |
                        State::fading_out |
                        State::skip_transition);

    return (visibleFlags & m_state);
}

void Label::skipTransitions() {
    enterState(State::skip_transition, 0.0);
}

glm::vec2 Label::center() const {
    return m_obb.getCentroid();
}

void Label::enterState(const State& _state, float _alpha) {
    if (m_state == State::dead) { return; }

    m_state = _state;
    setAlpha(_alpha);
}

void Label::setAlpha(float _alpha) {
    float alpha = CLAMP(_alpha, 0.0, 1.0);
    if (m_transform.state.alpha != alpha) {
        m_transform.state.alpha = alpha;
        m_dirty = true;
    }

    if (alpha == 0.f) {
        m_updateMeshVisibility = true;
    }
}

void Label::resetState() {
    if (m_state == State::dead) { return; }

    m_occludedLastFrame = false;
    m_occluded = false;
    m_updateMeshVisibility = true;
    m_dirty = true;
    m_proxy = false;
    enterState(State::wait_occ, 0.0);
}

bool Label::update(const glm::mat4& _mvp, const glm::vec2& _screenSize, float _zoomFract, bool _allLabels) {

    m_occludedLastFrame = m_occluded;
    if (m_state != State::fading_out) {
        m_occluded = false;
    }

    if (m_state == State::dead) {
        if (!_allLabels) {
            return false;
        } else {
            m_occluded = true;
        }
    }

    bool ruleSatisfied = updateScreenTransform(_mvp, _screenSize, !_allLabels);

    // one of the label rules has not been satisfied
    if (!ruleSatisfied) {
        enterState(State::sleep, 0.0);
        return false;
    }

    // update the view-space bouding box
    updateBBoxes(_zoomFract);

    // checks whether the label is out of the viewport
    if (offViewport(_screenSize)) {
        enterState(State::out_of_screen, 0.0);
    }

    return true;
}

bool Label::evalState(const glm::vec2& _screenSize, float _dt) {

    // if (Tangram::getDebugFlag(DebugFlags::all_labels)) {
    //     enterState(State::visible, 1.0);
    //     return false;
    // }

    bool animate = false;

    switch (m_state) {
        case State::visible:
            if (m_occluded) {
                m_fade = FadeEffect(false, m_options.hideTransition.ease,
                                    m_options.hideTransition.time);
                enterState(State::fading_out, 1.0);
                animate = true;
            }
            break;
        case State::fading_in:
            if (m_occluded) {
                enterState(State::sleep, 0.0);
                // enterState(State::fading_out, m_transform.state.alpha);
                // animate = true;
                break;
            }
            setAlpha(m_fade.update(_dt));
            animate = true;
            if (m_fade.isFinished()) {
                enterState(State::visible, 1.0);
            }
            break;
        case State::fading_out:
            if (!m_occluded) {
                enterState(State::fading_in, m_transform.state.alpha);
                animate = true;
                break;
            }
            setAlpha(m_fade.update(_dt));
            animate = true;
            if (m_fade.isFinished()) {
                enterState(State::sleep, 0.0);
            }
            break;
        case State::out_of_screen:
            if (!offViewport(_screenSize)) {
                enterState(State::wait_occ, 0.0);
            }
            break;
        case State::wait_occ:
            if (m_occluded) {
                enterState(State::sleep, 0.0);
            } else {
                m_fade = FadeEffect(true, m_options.showTransition.ease,
                                    m_options.showTransition.time);
                enterState(State::fading_in, 0.0);
                animate = true;
            }
            break;
        case State::skip_transition:
            if (m_occluded) {
                enterState(State::sleep, 0.0);
            } else {
                enterState(State::visible, 1.0);
            }
            break;
        case State::sleep:
            if (!m_occluded) {
                m_fade = FadeEffect(true, m_options.showTransition.ease,
                                    m_options.showTransition.time);
                enterState(State::fading_in, 0.0);
                animate = true;
            }
            break;
        case State::dead:
            break;
    }

    return animate;
}

}
<|endoftext|>


<commit_before>#include "vboMesh.h"
#include "platform.h"

#define MAX_INDEX_VALUE 65535 // Maximum value of GLushort

std::unordered_set<VboMesh*> VboMesh::s_managedVBOs;

VboMesh::VboMesh(std::shared_ptr<VertexLayout> _vertexLayout, GLenum _drawMode) : m_vertexLayout(_vertexLayout) {

    m_glVertexBuffer = 0;
    m_glIndexBuffer = 0;
    m_nVertices = 0;
    m_nIndices = 0;

    m_isUploaded = false;

    setDrawMode(_drawMode);

    addManagedVBO(this);
}

VboMesh::VboMesh() {
    m_glVertexBuffer = 0;
    m_glIndexBuffer = 0;
    m_nVertices = 0;
    m_nIndices = 0;

    m_isUploaded = false;

    addManagedVBO(this);
}

VboMesh::~VboMesh() {

    glDeleteBuffers(1, &m_glVertexBuffer);
    glDeleteBuffers(1, &m_glIndexBuffer);

    removeManagedVBO(this);

}

void VboMesh::setVertexLayout(std::shared_ptr<VertexLayout> _vertexLayout) {
    m_vertexLayout = _vertexLayout;
}

void VboMesh::setDrawMode(GLenum _drawMode) {
    switch (_drawMode) {
        case GL_POINTS:
        case GL_LINE_STRIP:
        case GL_LINE_LOOP:
        case GL_LINES:
        case GL_TRIANGLE_STRIP:
        case GL_TRIANGLE_FAN:
        case GL_TRIANGLES:
            m_drawMode = _drawMode;
            break;
        default:
            logMsg("%s\n","Invalid draw mode for mesh! Defaulting to GL_TRIANGLES");
            m_drawMode = GL_TRIANGLES;
    }
}

void VboMesh::addVertex(GLbyte* _vertex) {

    addVertices(_vertex, 1);

}

void VboMesh::addVertices(GLbyte* _vertices, int _nVertices) {

    if (m_isUploaded) {
        logMsg("%s\n", "VboMesh cannot add vertices after upload!");
        return;
    }

    // Only add up to 65535 vertices, any more will overflow our 16-bit indices
    int indexSpace = MAX_INDEX_VALUE - m_nVertices;
    if (_nVertices > indexSpace) {
        _nVertices = indexSpace;
        logMsg("WARNING: Tried to add more vertices than available in index space\n");
    }

    int vertexBytes = m_vertexLayout->getStride() * _nVertices;
    m_vertexData.insert(m_vertexData.end(), _vertices, _vertices + vertexBytes);
    m_nVertices += _nVertices;

}

void VboMesh::addIndex(GLushort* _index) {

    addIndices(_index, 1);

}

void VboMesh::addIndices(GLushort* _indices, int _nIndices) {

    if (m_isUploaded) {
        logMsg("%s\n", "VboMesh cannot add indices after upload!");
        return;
    }

    if (m_nVertices >= MAX_INDEX_VALUE) {
        logMsg("WARNING: Vertex buffer full, not adding indices\n");
        return;
    }

    m_indices.insert(m_indices.end(), _indices, _indices + _nIndices);
    m_nIndices += _nIndices;

}

void VboMesh::upload() {

    if (m_nVertices > 0) {
        // Generate vertex buffer, if needed
        if (m_glVertexBuffer == 0) {
            glGenBuffers(1, &m_glVertexBuffer);
        }

        // Buffer vertex data
        glBindBuffer(GL_ARRAY_BUFFER, m_glVertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, m_vertexData.size(), m_vertexData.data(), GL_STATIC_DRAW);
    }

    if (m_nIndices > 0) {
        // Generate index buffer, if needed
        if (m_glIndexBuffer == 0) {
            glGenBuffers(1, &m_glIndexBuffer);
        }

        // Buffer element index data
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_glIndexBuffer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof(GLushort), m_indices.data(), GL_STATIC_DRAW);
    }

    // Release copies of geometry in CPU memory
    m_vertexData.clear();
    m_indices.clear();

    m_isUploaded = true;

}

void VboMesh::draw(const std::shared_ptr<ShaderProgram> _shader) {

    // Ensure that geometry is buffered into GPU
    if (!m_isUploaded) {
        upload();
    }

    // Bind buffers for drawing
    if (m_nVertices > 0) {
        glBindBuffer(GL_ARRAY_BUFFER, m_glVertexBuffer);
    }

    if (m_nIndices > 0) {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_glIndexBuffer);
    }

    // Enable shader program
    _shader->use();

    // Enable vertex attribs via vertex layout object
    m_vertexLayout->enable(_shader);

    // Draw as elements or arrays
    if (m_nIndices > 0) {
        glDrawElements(m_drawMode, m_nIndices, GL_UNSIGNED_SHORT, 0);
    } else if (m_nVertices > 0) {
        glDrawArrays(m_drawMode, 0, m_nVertices);
    }

}

void VboMesh::addManagedVBO(VboMesh* _vbo) {
    s_managedVBOs.insert(_vbo);
}

void VboMesh::removeManagedVBO(VboMesh* _vbo) {
    s_managedVBOs.erase(_vbo);
}

void VboMesh::invalidateAllVBOs() {

    for (auto vbo : s_managedVBOs) {
        vbo->m_isUploaded = false;
        vbo->m_nVertices = 0;
        vbo->m_nIndices = 0;
        // TODO: signal to re-build vertices!
    }

}
<commit_msg>VboMesh now correctly recovers from GL context loss;<commit_after>#include "vboMesh.h"
#include "platform.h"

#define MAX_INDEX_VALUE 65535 // Maximum value of GLushort

std::unordered_set<VboMesh*> VboMesh::s_managedVBOs;

VboMesh::VboMesh(std::shared_ptr<VertexLayout> _vertexLayout, GLenum _drawMode) : m_vertexLayout(_vertexLayout) {

    m_glVertexBuffer = 0;
    m_glIndexBuffer = 0;
    m_nVertices = 0;
    m_nIndices = 0;

    m_isUploaded = false;

    setDrawMode(_drawMode);

    addManagedVBO(this);
}

VboMesh::VboMesh() {
    m_glVertexBuffer = 0;
    m_glIndexBuffer = 0;
    m_nVertices = 0;
    m_nIndices = 0;

    m_isUploaded = false;

    addManagedVBO(this);
}

VboMesh::~VboMesh() {

    glDeleteBuffers(1, &m_glVertexBuffer);
    glDeleteBuffers(1, &m_glIndexBuffer);

    removeManagedVBO(this);

}

void VboMesh::setVertexLayout(std::shared_ptr<VertexLayout> _vertexLayout) {
    m_vertexLayout = _vertexLayout;
}

void VboMesh::setDrawMode(GLenum _drawMode) {
    switch (_drawMode) {
        case GL_POINTS:
        case GL_LINE_STRIP:
        case GL_LINE_LOOP:
        case GL_LINES:
        case GL_TRIANGLE_STRIP:
        case GL_TRIANGLE_FAN:
        case GL_TRIANGLES:
            m_drawMode = _drawMode;
            break;
        default:
            logMsg("%s\n","Invalid draw mode for mesh! Defaulting to GL_TRIANGLES");
            m_drawMode = GL_TRIANGLES;
    }
}

void VboMesh::addVertex(GLbyte* _vertex) {

    addVertices(_vertex, 1);

}

void VboMesh::addVertices(GLbyte* _vertices, int _nVertices) {

    if (m_isUploaded) {
        logMsg("%s\n", "VboMesh cannot add vertices after upload!");
        return;
    }

    // Only add up to 65535 vertices, any more will overflow our 16-bit indices
    int indexSpace = MAX_INDEX_VALUE - m_nVertices;
    if (_nVertices > indexSpace) {
        _nVertices = indexSpace;
        logMsg("WARNING: Tried to add more vertices than available in index space\n");
    }

    int vertexBytes = m_vertexLayout->getStride() * _nVertices;
    m_vertexData.insert(m_vertexData.end(), _vertices, _vertices + vertexBytes);
    m_nVertices += _nVertices;

}

void VboMesh::addIndex(GLushort* _index) {

    addIndices(_index, 1);

}

void VboMesh::addIndices(GLushort* _indices, int _nIndices) {

    if (m_isUploaded) {
        logMsg("%s\n", "VboMesh cannot add indices after upload!");
        return;
    }

    if (m_nVertices >= MAX_INDEX_VALUE) {
        logMsg("WARNING: Vertex buffer full, not adding indices\n");
        return;
    }

    m_indices.insert(m_indices.end(), _indices, _indices + _nIndices);
    m_nIndices += _nIndices;

}

void VboMesh::upload() {

    if (m_nVertices > 0) {
        // Generate vertex buffer, if needed
        if (m_glVertexBuffer == 0) {
            glGenBuffers(1, &m_glVertexBuffer);
        }

        // Buffer vertex data
        glBindBuffer(GL_ARRAY_BUFFER, m_glVertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, m_vertexData.size(), m_vertexData.data(), GL_STATIC_DRAW);
    }

    if (m_nIndices > 0) {
        // Generate index buffer, if needed
        if (m_glIndexBuffer == 0) {
            glGenBuffers(1, &m_glIndexBuffer);
        }

        // Buffer element index data
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_glIndexBuffer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof(GLushort), m_indices.data(), GL_STATIC_DRAW);
    }

    // Retaining CPU buffers for now
    //m_vertexData.clear();
    //m_indices.clear();

    m_isUploaded = true;

}

void VboMesh::draw(const std::shared_ptr<ShaderProgram> _shader) {

    // Ensure that geometry is buffered into GPU
    if (!m_isUploaded) {
        upload();
    }

    // Bind buffers for drawing
    if (m_nVertices > 0) {
        glBindBuffer(GL_ARRAY_BUFFER, m_glVertexBuffer);
    }

    if (m_nIndices > 0) {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_glIndexBuffer);
    }

    // Enable shader program
    _shader->use();

    // Enable vertex attribs via vertex layout object
    m_vertexLayout->enable(_shader);

    // Draw as elements or arrays
    if (m_nIndices > 0) {
        glDrawElements(m_drawMode, m_nIndices, GL_UNSIGNED_SHORT, 0);
    } else if (m_nVertices > 0) {
        glDrawArrays(m_drawMode, 0, m_nVertices);
    }

}

void VboMesh::addManagedVBO(VboMesh* _vbo) {
    s_managedVBOs.insert(_vbo);
}

void VboMesh::removeManagedVBO(VboMesh* _vbo) {
    s_managedVBOs.erase(_vbo);
}

void VboMesh::invalidateAllVBOs() {

    for (auto vbo : s_managedVBOs) {
        vbo->m_isUploaded = false;

        // Retaining CPU buffers for now
        //vbo->m_nVertices = 0;
        //vbo->m_nIndices = 0;

        vbo->m_glVertexBuffer = 0;
        vbo->m_glIndexBuffer = 0;

        // TODO: re-build vertices!
    }

}
<|endoftext|>


<commit_before>//===========================================================================
//
// File: testLRsplineRefinement.cpp
//
// Created: Tue Nov 20 10:55:54 2012
//
// Author: Sverre Briseid <Sverre.Briseid@sintef.no>
//
// Revision: $Id$
//
// Description: Compare result after refining different versions of lrsplines.
//
//===========================================================================

//#include "GoTools/lr_splines/LRSplineSurface.h"
#include "GoTools/lrsplines2D/LRSplineSurface.h"
#include "GoTools/lrsplines2D/LRSplinePlotUtils.h"
#include "GoTools/geometry/ObjectHeader.h"
#include "GoTools/geometry/SplineSurface.h"

#include <iostream>
#include <assert.h>
#include <fstream>

using namespace Go;

// Assuming the domain is the same.
double maxDist(const Go::ParamSurface* param_sf,
	       const Go::LRSplineSurface& lr_spline_sf,
	       int nmb_samples_u, int nmb_samples_v);

int main(int argc, char *argv[])
{
  if (argc != 3)
  {
      std::cout << "Usage: input_(lr_)spline_sf.g2 ref_lr_spline_sf.g2" << std::endl;
      return -1;
  }

  std::ifstream filein(argv[1]); // Input (regular) spline surface.
  std::ofstream fileout(argv[2]); // Input (regular) spline surface.
  shared_ptr<Go::ParamSurface> input_sf;
  Go::ObjectHeader header;
  filein >> header;
  shared_ptr<Go::LRSplineSurface> lr_spline_sf(new Go::LRSplineSurface());
  int num_coefs_u;
  int num_coefs_v;
  int order_u;
  int order_v;
  LRSplineSurface::Refinement2D ref_v, ref_u;
  // ref.d = Go::XFIXED; // Inserting a v/x knot.
  ref_u.multiplicity = ref_v.multiplicity = 1;
  LRSplineSurface lrsf;
  if (header.classType() == Go::Class_SplineSurface)
  {
      shared_ptr<Go::SplineSurface> spline_sf(new Go::SplineSurface());
      filein >> *spline_sf;
      num_coefs_u = spline_sf->numCoefs_u();
      num_coefs_v = spline_sf->numCoefs_v();
      order_u = spline_sf->order_u();
      order_v = spline_sf->order_v();

      input_sf = spline_sf;
      puts("Now we convert from SplineSurface to LRSplineSurface!");
      double knot_tol = 1e-05;
      lr_spline_sf = shared_ptr<Go::LRSplineSurface>(new Go::LRSplineSurface(spline_sf->clone(), knot_tol));
      puts("Done converting!");

#if 0//ndef NDEBUG
      {
	std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
	for (auto iter = lr_spline_sf->basisFunctionsBegin(); iter != lr_spline_sf->basisFunctionsEnd(); ++iter)
	  {
	    bas_funcs.push_back((*iter).second);
	  }
	puts("Remove when done debugging!");
      }
#endif

      double umax = lr_spline_sf->paramMax(Go::XFIXED);
      double vmax = lr_spline_sf->paramMax(Go::YFIXED);
      int mid_knot_ind_u = floor((num_coefs_u + order_u)/2);
      int mid_knot_ind_v = floor((num_coefs_u + order_u)/2);
      bool refine_at_line = false;//true;
//  std::cout << "parval: " << parval << ", start: " << start << " end: " << end << std::endl;
      ref_v.kval = (refine_at_line) ? spline_sf->basis_v().begin()[mid_knot_ind_v] :
	  0.5*(spline_sf->basis_v().begin()[mid_knot_ind_v] + spline_sf->basis_v().begin()[mid_knot_ind_v+1]);
      ref_v.start = spline_sf->basis_u().begin()[mid_knot_ind_u];
      ref_v.end = umax;
      ref_v.d = Go::YFIXED;

      ref_u.kval = (refine_at_line) ? spline_sf->basis_u().begin()[mid_knot_ind_u] :
	  0.5*(spline_sf->basis_u().begin()[mid_knot_ind_u] + spline_sf->basis_u().begin()[mid_knot_ind_u+1]);
      ref_u.start = spline_sf->basis_v().begin()[mid_knot_ind_v];
      ref_u.end = vmax;
      ref_u.d = Go::XFIXED;

  }
  else if (header.classType() == Go::Class_LRSplineSurface)
  {
      filein >> lrsf;
      lr_spline_sf = shared_ptr<LRSplineSurface>(lrsf.clone());
      puts("Refining a LRSplineSurface only working on specific cases, hardcoded values.");
      ref_v.kval = 0.625;
      ref_v.start = 0.5;
      ref_v.end = 1.0;
      ref_v.d = Go::YFIXED;
      ref_u = ref_v;
      ref_u.d = Go::XFIXED;
      input_sf = shared_ptr<ParamSurface>(lr_spline_sf->clone());

#if 0//ndef NDEBUG
      {
	std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
	for (auto iter = lr_spline_sf->basisFunctionsBegin(); iter != lr_spline_sf->basisFunctionsEnd(); ++iter)
	  {
	    bas_funcs.push_back((*iter).second);
	  }
	puts("Remove when done debugging!");
      }
#endif

  }
  else
  {
      puts("Unexpected geometry input.");
      return -1;
  }

#if 0//ndef NDEBUG
  {
    std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
    for (auto iter = lr_spline_sf->basisFunctionsBegin(); iter != lr_spline_sf->basisFunctionsEnd(); ++iter)
      {
	bas_funcs.push_back((*iter).second);
      }
    puts("Remove when done debugging!");
  }
#endif

  // We test to see if conversion was correct.
  int nmb_samples_u = 101; // Rather random.
  int nmb_samples_v = 36;
  double max_dist = maxDist(input_sf.get(), *lr_spline_sf, nmb_samples_u, nmb_samples_v);
  std::cout << "Max dist between input and converted surface: " << max_dist << std::endl;

  // We write to screen the number of element and basis functions.
  int num_basis_funcs = lr_spline_sf->numBasisFunctions();
  int num_elem = lr_spline_sf->numElements();
  std::cout << "Status prior to refinement: num_basis_funcs: " << num_basis_funcs << ", num_elem: " << num_elem << std::endl;

  // We hardcode a refinement.
  // Hardcoded values for debugging.

//  lr_spline_sf->refine((dir==0) ? Go::YFIXED : Go::XFIXED, parval, start, end, mult);
  std::vector<LRSplineSurface::Refinement2D> refs_single, refs_multi;
//  refs.push_back(ref);
  // LRSplineSurface::Refinement2D ref2 = ref;
  // ref2.kval = 0.4;
  // LRSplineSurface::Refinement2D ref3 = ref;
  // ref3.kval = 0.65;
//  LRSplineSurface::Refinement2D ref4 = ref;
  //ref4.d = Go::YFIXED;

//  refs.push_back(ref2);
//  refs.push_back(ref3);
  refs_single.push_back(ref_u);
  refs_single.push_back(ref_v);
  refs_multi.push_back(ref_u);
  refs_multi.push_back(ref_v);

#if 0//ndef NDEBUG
  {
    std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
    for (auto iter = lr_spline_sf->basisFunctionsBegin(); iter != lr_spline_sf->basisFunctionsEnd(); ++iter)
      {
	bas_funcs.push_back((*iter).second);
      }
    puts("Remove when done debugging!");
  }
#endif

  bool ref_single = true;

  if (ref_single)
    {

      for (uint ki = 0; ki < refs_single.size(); ++ki)
	{
#ifndef NDEBUG
	  std::cout << "DEBUG: ki = " << ki << std::endl;
#endif
	  lr_spline_sf->refine(refs_single[ki]);
	}
//  lr_spline_sf->refine(ref);
//  lr_spline_sf_multi->refine(refs);
//  lr_spline_sf->refine((dir==0) ? Go::YFIXED : Go::XFIXED, parval, start, end, mult);

#if 0//ndef NDEBUG
      {
	std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
	for (auto iter = lr_spline_sf->basisFunctionsBegin(); iter != lr_spline_sf->basisFunctionsEnd(); ++iter)
	  {
	    bas_funcs.push_back((*iter).second);
	  }
	puts("Remove when done debugging!");
      }
#endif

      double max_dist_post_ref = maxDist(input_sf.get(), *lr_spline_sf, nmb_samples_u, nmb_samples_v);
      std::cout << "Max dist input and ref surface (ref one at the time): " << max_dist_post_ref << std::endl;

      // We write to screen the number of element and basis functions.
      num_basis_funcs = lr_spline_sf->numBasisFunctions();
      num_elem = lr_spline_sf->numElements();
      std::cout << "Ref one at the time: num_basis_funcs: " << num_basis_funcs << ", num_elem: " << num_elem << std::endl;

// #ifndef NDEBUG
      std::ofstream lrsf_grid_ps("tmp/lrsf_grid.ps");
//  writePostscriptMesh(*lrsf);
// #endif NDEBUG

      writePostscriptMesh(*lr_spline_sf, lrsf_grid_ps);
      std::ofstream fileout2("tmp/ref_lr_single.g2");
      lr_spline_sf->writeStandardHeader(fileout2);
      lr_spline_sf->write(fileout2);

      lr_spline_sf->writeStandardHeader(fileout);
      lr_spline_sf->write(fileout);

    }

  bool refine_multi = true;
  if (refine_multi)
    {
      shared_ptr<LRSplineSurface> lr_spline_sf_multi(new LRSplineSurface());
      *lr_spline_sf_multi = *lr_spline_sf;

#if 0//ndef NDEBUG
      {
	std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
	for (auto iter = lr_spline_sf_multi->basisFunctionsBegin(); iter != lr_spline_sf_multi->basisFunctionsEnd(); ++iter)
	  {
	    bas_funcs.push_back((*iter).second);
	  }
	puts("Remove when done debugging!");
      }
#endif

      lr_spline_sf_multi->refine(refs_multi);

#if 0//ndef NDEBUG
      {
	std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
	for (auto iter = lr_spline_sf_multi->basisFunctionsBegin(); iter != lr_spline_sf_multi->basisFunctionsEnd(); ++iter)
	  {
	    bas_funcs.push_back((*iter).second);
	  }
	puts("Remove when done debugging!");
      }
#endif

      double max_dist_post_ref_multi_ref = maxDist(input_sf.get(), *lr_spline_sf_multi, nmb_samples_u, nmb_samples_v);
      std::cout << "Max dist input and ref surface: " << max_dist_post_ref_multi_ref << std::endl;

      int num_basis_funcs_multi = lr_spline_sf_multi->numBasisFunctions();
      int num_elem_multi = lr_spline_sf_multi->numElements();
      std::cout << "Ref using a vector of refinements: num_basis_funcs: " << num_basis_funcs_multi <<
	", num_elem_multi: " << num_elem_multi << std::endl;

      std::ofstream fileout3("tmp/ref_lr_multi.g2");
      lr_spline_sf_multi->writeStandardHeader(fileout3);
      lr_spline_sf_multi->write(fileout3);

      std::ofstream lrsf_multi_grid_ps("tmp/lrsf_grid_multi.ps");
     writePostscriptMesh(*lr_spline_sf_multi, lrsf_multi_grid_ps);

    }

}

double maxDist(const Go::ParamSurface* param_sf,
	       const Go::LRSplineSurface& lr_spline_sf,
	       int nmb_samples_u, int nmb_samples_v)
{
    // Assuming the domain is the same.
    double umin = lr_spline_sf.startparam_u();
    double umax = lr_spline_sf.endparam_u();
    double vmin = lr_spline_sf.startparam_v();
    double vmax = lr_spline_sf.endparam_v();
    double ustep = (umax - umin)/((double)nmb_samples_u - 1);
    double vstep = (vmax - vmin)/((double)nmb_samples_v - 1);
    Go::Point go_pt(3), lr_pt(3);
    double max_dist = -1.0;
// #ifndef NDEBUG
    double max_dist_u = 0.0;
    double max_dist_v = 0.0;
    Go::Point max_go_pt(3), max_lr_pt(3);
// #endif
    for (int kj = 0; kj < nmb_samples_v; ++kj)
    {
	double vpar = vmin + kj*vstep;
	for (int ki = 0; ki < nmb_samples_u; ++ki)
	{
	    double upar = umin + ki*ustep;
	    param_sf->point(go_pt, upar, vpar);
	    lr_spline_sf.point(lr_pt, upar, vpar);
	    double dist = go_pt.dist(lr_pt);
	    if (dist > max_dist)
	    {
		max_dist = dist;
// #ifndef NDEBUG
		max_dist_u = upar;
		max_dist_v = vpar;
		max_go_pt = go_pt;
		max_lr_pt = lr_pt;
// #endif
	    }
	}
    }

// #ifndef NDEBUG
    std::cout << "max_dist: u = " << max_dist_u << ", v = " << max_dist_v << std::endl;
    std::cout << "max_go_pt = " << max_go_pt << std::endl;
    std::cout << "max_lr_pt = " << max_lr_pt << std::endl;
// #endif

    return max_dist;
}
<commit_msg>Rational first deriv almost done.<commit_after>//===========================================================================
//
// File: testLRsplineRefinement.cpp
//
// Created: Tue Nov 20 10:55:54 2012
//
// Author: Sverre Briseid <Sverre.Briseid@sintef.no>
//
// Revision: $Id$
//
// Description: Compare result after refining different versions of lrsplines.
//
//===========================================================================

//#include "GoTools/lr_splines/LRSplineSurface.h"
#include "GoTools/lrsplines2D/LRSplineSurface.h"
#include "GoTools/lrsplines2D/LRSplinePlotUtils.h"
#include "GoTools/geometry/ObjectHeader.h"
#include "GoTools/geometry/SplineSurface.h"

#include <iostream>
#include <assert.h>
#include <fstream>

using namespace Go;

// Assuming the domain is the same.
double maxDist(const Go::ParamSurface* param_sf,
	       const Go::LRSplineSurface& lr_spline_sf,
	       int nmb_samples_u, int nmb_samples_v);

int main(int argc, char *argv[])
{
  if (argc != 3)
  {
      std::cout << "Usage: input_(lr_)spline_sf.g2 ref_lr_spline_sf.g2" << std::endl;
      return -1;
  }

  std::ifstream filein(argv[1]); // Input (regular) spline surface.
  std::ofstream fileout(argv[2]); // Input (regular) spline surface.
  shared_ptr<Go::ParamSurface> input_sf;
  Go::ObjectHeader header;
  filein >> header;
  shared_ptr<Go::LRSplineSurface> lr_spline_sf(new Go::LRSplineSurface());
  int num_coefs_u;
  int num_coefs_v;
  int order_u;
  int order_v;
  LRSplineSurface::Refinement2D ref_v, ref_u;
  // ref.d = Go::XFIXED; // Inserting a v/x knot.
  ref_u.multiplicity = ref_v.multiplicity = 1;
  LRSplineSurface lrsf;
  double knot_tol = 1e-05;
  shared_ptr<Go::SplineSurface> spline_sf(new Go::SplineSurface());
  if (header.classType() == Go::Class_SplineSurface)
  {
      filein >> *spline_sf;

#if 0
      // Making the input k-regular results in an error for the refinement (max_dist is 3e-03).
      MESSAGE("Deactivated making the surface k-regular. Should test that for cone_sf.g2");
#else
      MESSAGE("Making sure the input surface is k-regular.");
      // Having some problems with a rational case (cylinder_sf.g2) ...

      // Example is already k-regular.
      spline_sf->makeSurfaceKRegular();
      // Swpping the parameter directions does not help.
//      spline_sf->swapParameterDirection();
#endif

#if 0
      order_u = spline_sf->order_u();
      int raise_u = 0;
      int raise_v = 0;
      if (order_u < 4)
	  raise_u = 4 - order_u;
      order_v = spline_sf->order_v();
      if (order_v < 4)
	  raise_v = 4 - order_v;
      if (raise_u > 0 || raise_v > 0)
	  spline_sf->raiseOrder(raise_u, raise_v);
#endif

      num_coefs_u = spline_sf->numCoefs_u();
      num_coefs_v = spline_sf->numCoefs_v();
      order_u = spline_sf->order_u();
      order_v = spline_sf->order_v();

      input_sf = spline_sf;
      puts("Now we convert from SplineSurface to LRSplineSurface!");
      lr_spline_sf = shared_ptr<Go::LRSplineSurface>(new Go::LRSplineSurface(spline_sf->clone(), knot_tol));
      puts("Done converting!");

#ifndef NDEBUG
      {
	std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
	for (auto iter = lr_spline_sf->basisFunctionsBegin(); iter != lr_spline_sf->basisFunctionsEnd(); ++iter)
	  {
	    bas_funcs.push_back((*iter).second);
	  }
	puts("Remove when done debugging!");
      }
#endif

      const Mesh2D& mesh = lr_spline_sf->mesh();
      double umin = mesh.minParam(Go::XFIXED);
      int umin_ind = mesh.getKnotIdx(Go::XFIXED, umin, knot_tol);
      ref_u.kval = 0.5*(mesh.kval(Go::XFIXED, umin_ind) + mesh.kval(Go::XFIXED, umin_ind + 1));
      ref_u.start = mesh.minParam(Go::YFIXED);
      ref_u.end = mesh.maxParam(Go::YFIXED);
      ref_u.d = Go::XFIXED;
      double vmin = mesh.minParam(Go::YFIXED);
      int vmin_ind = mesh.getKnotIdx(Go::YFIXED, vmin, knot_tol);
      ref_v.kval = 0.5*(mesh.kval(Go::YFIXED, vmin_ind) + mesh.kval(Go::YFIXED, vmin_ind + 1));
      ref_v.start = mesh.minParam(Go::XFIXED);
      ref_v.end = mesh.maxParam(Go::XFIXED);
      ref_v.d = Go::YFIXED;

  }
  else if (header.classType() == Go::Class_LRSplineSurface)
  {
      filein >> lrsf;
      lr_spline_sf = shared_ptr<LRSplineSurface>(lrsf.clone());
      puts("Refining a LRSplineSurface only working on specific cases, hardcoded values.");

      const Mesh2D& mesh = lr_spline_sf->mesh();
      ref_u.kval = 0.5*(mesh.kval(Go::XFIXED, 0) + mesh.kval(Go::XFIXED, 1));
      ref_u.start = mesh.minParam(Go::YFIXED);
      ref_u.end = mesh.maxParam(Go::YFIXED);
      ref_u.d = Go::XFIXED;
      ref_v.kval = 0.5*(mesh.kval(Go::YFIXED, 0) + mesh.kval(Go::YFIXED, 1));
      ref_v.start = mesh.minParam(Go::XFIXED);
      ref_v.end = mesh.maxParam(Go::XFIXED);
      ref_v.d = Go::YFIXED;

      input_sf = shared_ptr<ParamSurface>(lr_spline_sf->clone());

#if 0//ndef NDEBUG
      {
	std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
	for (auto iter = lr_spline_sf->basisFunctionsBegin(); iter != lr_spline_sf->basisFunctionsEnd(); ++iter)
	  {
	    bas_funcs.push_back((*iter).second);
	  }
	puts("Remove when done debugging!");
      }
#endif

  }
  else
  {
      puts("Unexpected geometry input.");
      return -1;
  }

#if 0//ndef NDEBUG
  {
    std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
    for (auto iter = lr_spline_sf->basisFunctionsBegin(); iter != lr_spline_sf->basisFunctionsEnd(); ++iter)
      {
	bas_funcs.push_back((*iter).second);
      }
    puts("Remove when done debugging!");
  }
#endif

  // We test to see if conversion was correct.
  int nmb_samples_u = 101; // Rather random.
  int nmb_samples_v = 36;
  double max_dist = maxDist(input_sf.get(), *lr_spline_sf, nmb_samples_u, nmb_samples_v);
  std::cout << "Max dist between input and converted surface: " << max_dist << std::endl;

  // We write to screen the number of element and basis functions.
  int num_basis_funcs = lr_spline_sf->numBasisFunctions();
  int num_elem = lr_spline_sf->numElements();
  std::cout << "Status prior to refinement: num_basis_funcs: " << num_basis_funcs << ", num_elem: " << num_elem << std::endl;

  std::vector<LRSplineSurface::Refinement2D> refs_single, refs_multi;

#if 1
  refs_single.push_back(ref_u);
  refs_multi.push_back(ref_u);
#endif
#if 0
  refs_single.push_back(ref_v);
  refs_multi.push_back(ref_v);
#endif
#if 0
  LRSplineSurface::Refinement2D ref_v2 = ref_v;
  ref_v2.kval = -0.5;
  refs_single.push_back(ref_v2);
  refs_multi.push_back(ref_v2);
#endif

#if 0//ndef NDEBUG
  MESSAGE("Clearing the refinement vectors, for debugging!");
  refs_single.clear();
  refs_multi.clear();
#endif

#if 0//ndef NDEBUG
  {
    std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
    for (auto iter = lr_spline_sf->basisFunctionsBegin(); iter != lr_spline_sf->basisFunctionsEnd(); ++iter)
      {
	bas_funcs.push_back((*iter).second);
      }
    puts("Remove when done debugging!");
  }
#endif

  shared_ptr<LRSplineSurface> lr_spline_sf_multi(new LRSplineSurface());
#if 0
  // @@sbr201301 Still having some problems with the copy constructor, still pointing to some shared data.
  *lr_spline_sf_multi = *lr_spline_sf;
#else
  MESSAGE("Deactivated copy constructor usage for LRSplineSurface due to problems.");
  lr_spline_sf_multi = shared_ptr<Go::LRSplineSurface>(new Go::LRSplineSurface(spline_sf->clone(), knot_tol));
#endif

  bool ref_single = true;

  if (ref_single)
    {

      for (uint ki = 0; ki < refs_single.size(); ++ki)
	{
#ifndef NDEBUG
	  std::cout << "DEBUG: ki = " << ki << std::endl;
#endif
	  lr_spline_sf->refine(refs_single[ki]);
	}

#if 0//ndef NDEBUG
      {
	std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
	for (auto iter = lr_spline_sf->basisFunctionsBegin(); iter != lr_spline_sf->basisFunctionsEnd(); ++iter)
	  {
	    bas_funcs.push_back((*iter).second);
	  }
	puts("Remove when done debugging!");
      }
#endif

      double max_dist_post_ref = maxDist(input_sf.get(), *lr_spline_sf, nmb_samples_u, nmb_samples_v);
      std::cout << "Max dist input and ref surface (ref one at the time): " << max_dist_post_ref << std::endl;

      // We write to screen the number of element and basis functions.
      num_basis_funcs = lr_spline_sf->numBasisFunctions();
      num_elem = lr_spline_sf->numElements();
      std::cout << "Ref one at the time: num_basis_funcs: " << num_basis_funcs << ", num_elem: " << num_elem << std::endl;

      std::ofstream lrsf_grid_ps("tmp/lrsf_grid.ps");

      writePostscriptMesh(*lr_spline_sf, lrsf_grid_ps);
      std::ofstream fileout2("tmp/ref_lr_single.g2");
      lr_spline_sf->writeStandardHeader(fileout2);
      lr_spline_sf->write(fileout2);

      lr_spline_sf->writeStandardHeader(fileout);
      lr_spline_sf->write(fileout);

      num_basis_funcs = lr_spline_sf->numBasisFunctions();
      num_elem = lr_spline_sf->numElements();
      std::cout << "Status after refinement (single): num_basis_funcs: " << num_basis_funcs << ", num_elem: " << num_elem << std::endl;

    }

  bool refine_multi = true;
  if (refine_multi)
    {

#if 0//ndef NDEBUG
      {
	std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
	for (auto iter = lr_spline_sf_multi->basisFunctionsBegin(); iter != lr_spline_sf_multi->basisFunctionsEnd(); ++iter)
	  {
	    bas_funcs.push_back((*iter).second);
	  }
	puts("Remove when done debugging!");
      }
#endif

      lr_spline_sf_multi->refine(refs_multi);

      num_basis_funcs = lr_spline_sf_multi->numBasisFunctions();
      num_elem = lr_spline_sf_multi->numElements();
      std::cout << "Status after refinement (multi): num_basis_funcs: " << num_basis_funcs << ", num_elem: " << num_elem << std::endl;

#if 0//ndef NDEBUG
      {
	std::vector<shared_ptr<LRBSpline2D> > bas_funcs;
	for (auto iter = lr_spline_sf_multi->basisFunctionsBegin(); iter != lr_spline_sf_multi->basisFunctionsEnd(); ++iter)
	  {
	    bas_funcs.push_back((*iter).second);
	  }
	puts("Remove when done debugging!");
      }
#endif

      double max_dist_post_ref_multi_ref = maxDist(input_sf.get(), *lr_spline_sf_multi, nmb_samples_u, nmb_samples_v);
      std::cout << "Max dist input and ref surface: " << max_dist_post_ref_multi_ref << std::endl;

      int num_basis_funcs_multi = lr_spline_sf_multi->numBasisFunctions();
      int num_elem_multi = lr_spline_sf_multi->numElements();
      std::cout << "Ref using a vector of refinements: num_basis_funcs: " << num_basis_funcs_multi <<
	", num_elem_multi: " << num_elem_multi << std::endl;

      std::ofstream fileout3("tmp/ref_lr_multi.g2");
      lr_spline_sf_multi->writeStandardHeader(fileout3);
      lr_spline_sf_multi->write(fileout3);

      std::ofstream lrsf_multi_grid_ps("tmp/lrsf_grid_multi.ps");
      writePostscriptMesh(*lr_spline_sf_multi, lrsf_multi_grid_ps);

    }

}

double maxDist(const Go::ParamSurface* param_sf,
	       const Go::LRSplineSurface& lr_spline_sf,
	       int nmb_samples_u, int nmb_samples_v)
{
    // Assuming the domain is the same.
    double umin = lr_spline_sf.startparam_u();
    double umax = lr_spline_sf.endparam_u();
    double vmin = lr_spline_sf.startparam_v();
    double vmax = lr_spline_sf.endparam_v();
    double ustep = (umax - umin)/((double)nmb_samples_u - 1);
    double vstep = (vmax - vmin)/((double)nmb_samples_v - 1);
    Go::Point go_pt(3), lr_pt(3);
    double max_dist = -1.0;
// #ifndef NDEBUG
    double max_dist_u = 0.0;
    double max_dist_v = 0.0;
    Go::Point max_go_pt(3), max_lr_pt(3);
// #endif
    for (int kj = 0; kj < nmb_samples_v; ++kj)
    {
	double vpar = vmin + kj*vstep;
	for (int ki = 0; ki < nmb_samples_u; ++ki)
	{
	    double upar = umin + ki*ustep;
	    param_sf->point(go_pt, upar, vpar);
	    lr_spline_sf.point(lr_pt, upar, vpar);
	    double dist = go_pt.dist(lr_pt);
	    if (dist > max_dist)
	    {
		max_dist = dist;
// #ifndef NDEBUG
		max_dist_u = upar;
		max_dist_v = vpar;
		max_go_pt = go_pt;
		max_lr_pt = lr_pt;
// #endif
	    }
	}
    }

// #ifndef NDEBUG
    std::cout << "max_dist: u = " << max_dist_u << ", v = " << max_dist_v << std::endl;
    std::cout << "max_go_pt = " << max_go_pt << std::endl;
    std::cout << "max_lr_pt = " << max_lr_pt << std::endl;
// #endif

    return max_dist;
}
<|endoftext|>