<commit_before>/***************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of mcompositor.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/
/*!
  \class MCompositeWindowAnimation
  \brief MCompositeWindowAnimation class which provides full control
  of position transformation and opacity animations of composite window objects.

  To create more complex animations, re-implement the virtual functions
  windowShown(), windowClosed(), widowIconified() and windowRestored();
  A QParallelAnimationGroup object is provided which can be a container
  for more complex animations for more flexibility.
*/
// TODO: import icongeometry. export signals when animation done to hook
// to composting on off

#include <QMetaMethod>
#include <QRectF>
#include <QDesktopWidget>
#include <QPropertyAnimation>
#include <mcompositewindow.h>
#include <mcompositewindowanimation.h>
#include <QApplication>
#include <QParallelAnimationGroup>
#include <mcompositemanager.h>
#include <mcompositemanager_p.h>
#include <QVector>

class McParallelAnimation: public QParallelAnimationGroup
{
public:
    McParallelAnimation(MCompositeWindowAnimation* p)
        :QParallelAnimationGroup(p),
         parent(p)
    {}

protected:
    virtual void updateCurrentTime(int currentTime)
    {
        MCompositeWindow::update();
        QParallelAnimationGroup::updateCurrentTime(currentTime);
    }

    virtual void updateState(QAbstractAnimation::State newState,
                             QAbstractAnimation::State oldState)
    {
        if (newState == QAbstractAnimation::Running &&
            oldState == QAbstractAnimation::Stopped) {
            parent->ensureAnimationVisible();
            if (parent->targetWindow())
                parent->targetWindow()->beginAnimation();
            if (parent->targetWindow2())
                parent->targetWindow2()->beginAnimation();
        } else if (newState == QAbstractAnimation::Stopped) {
            if (parent->targetWindow())
                parent->targetWindow()->endAnimation();
            if (parent->targetWindow2())
                parent->targetWindow2()->endAnimation();
        }
        QParallelAnimationGroup::updateState(newState, oldState);
    }
private:
    MCompositeWindowAnimation* parent;
};

class MCompositeWindowAnimationPrivate
{
public:

    MCompositeWindowAnimationPrivate(MCompositeWindowAnimation* animation)
        : crossfade(0),
          pending_animation(MCompositeWindowAnimation::NoAnimation),
          is_replaceable(true),
          animhandler(MCompositeWindowAnimation::AnimationTotal, 0)
    {
        const MCompositeManager *mc = static_cast<MCompositeManager*>(qApp);
        // default duration of 1 ms to keep "NOP animation" short
        int duration = 1;

        scale = new QPropertyAnimation(animation);
        scale->setPropertyName("scale");
        scale->setDuration(duration);
        scale->setStartValue(1);
        scale->setEndValue(1);

        position = new QPropertyAnimation(animation);
        position->setPropertyName("pos");
        position->setDuration(duration);
        position->setStartValue(QPoint(0, 0));
        position->setEndValue(QPoint(0, 0));

        opacity = new QPropertyAnimation(animation);
        opacity->setPropertyName("opacity");
        opacity->setDuration(duration);
        opacity->setStartValue(1);
        opacity->setEndValue(1);

        scalepos = new McParallelAnimation(animation);
        scalepos->addAnimation(scale);
        scalepos->addAnimation(position);
        scalepos->addAnimation(opacity);

        QObject::connect(scalepos, SIGNAL(finished()), animation,
                         SLOT(finalizeState()));
    }

    void setTargetWindow(MCompositeWindow* window)
    {
        if (scale && position && opacity) {
            scale->setTargetObject(window);
            position->setTargetObject(window);
            opacity->setTargetObject(window);
        }
    }

    bool handledByInvoker(MCompositeWindowAnimation::AnimationType type)
    {
        MAbstractAnimationHandler* handler = animhandler[type];
        if (handler) {
            handler->target_window = target_window;
            switch (type) {
            case MCompositeWindowAnimation::Showing:
                handler->windowShown();
                return true;
            case MCompositeWindowAnimation::Closing:
                handler->windowClosed();
                return true;
            case MCompositeWindowAnimation::Iconify:
                handler->windowIconified();
                return true;
            case MCompositeWindowAnimation::Restore:
                handler->windowRestored();
                return true;
            default:
                break;
            }
        }

        return false;
    }

    QPointer<MCompositeWindow> target_window, target_window2;
    QPointer<QPropertyAnimation> scale;
    QPointer<QPropertyAnimation> position;
    QPointer<QPropertyAnimation> opacity;
    McParallelAnimation* scalepos, *crossfade;
    MCompositeWindowAnimation::AnimationType pending_animation;
    bool is_replaceable;
    QVector< MAbstractAnimationHandler* > animhandler;
};

MCompositeWindowAnimation::MCompositeWindowAnimation(QObject* parent)
    :QObject(parent),
     d_ptr(new MCompositeWindowAnimationPrivate(this))
{
}

MCompositeWindowAnimation::~MCompositeWindowAnimation()
{
}

void MCompositeWindowAnimation::setTargetWindow(MCompositeWindow* window)
{
    Q_D(MCompositeWindowAnimation);

    // never override a non-replaceable animation
    if (window->animator && !window->animator->isReplaceable()) {
        deleteLater();
        return;
    }

    // replace the old animator if there is one
    if (window->animator && (window->animator != this))
        delete window->animator;

    d->target_window = window;
    d->target_window->animator = this;
    disconnect(SIGNAL(q_finalizeState()));
    connect(this, SIGNAL(q_finalizeState()), window, SLOT(finalizeState()));

    d->setTargetWindow(window);
}

void MCompositeWindowAnimation::finalizeState()
{
    emit q_finalizeState();
}

// returns a group animation for this animator
QParallelAnimationGroup* MCompositeWindowAnimation::animationGroup() const
{
    Q_D(const MCompositeWindowAnimation);
    return d->scalepos;
}

MCompositeWindow* MCompositeWindowAnimation::targetWindow() const
{
    Q_D(const MCompositeWindowAnimation);
    return d->target_window;
}

MCompositeWindow* MCompositeWindowAnimation::targetWindow2() const
{
    Q_D(const MCompositeWindowAnimation);
    return d->target_window2;
}

// Exposed animation properties
QPropertyAnimation* MCompositeWindowAnimation::scaleAnimation() const
{
    Q_D(const MCompositeWindowAnimation);
    return d->scale;
}

QPropertyAnimation* MCompositeWindowAnimation::positionAnimation() const
{
    Q_D(const MCompositeWindowAnimation);
    return d->position;
}

QPropertyAnimation* MCompositeWindowAnimation::opacityAnimation() const
{
    Q_D(const MCompositeWindowAnimation);
    return d->opacity;
}

// Default effect
void MCompositeWindowAnimation::windowShown()
{
#define OPAQUE 1.0
#define DIMMED 0.1
    Q_D(MCompositeWindowAnimation);
    if (!d->target_window)
        return;

    if (d->handledByInvoker(Showing))
        return;

    const qreal scaleStart = 0.2;
    const QRectF &iconGeometry = d->target_window->propertyCache()->iconGeometry();
    QPointF topLeft = iconGeometry.topLeft();

    if (iconGeometry.isEmpty()) {
        const QRectF d = QApplication::desktop()->availableGeometry();
        topLeft.setX(d.width()/2.0f * (1.0f-scaleStart));
        topLeft.setY(d.height()/2.0f * (1.0f-scaleStart));
    }

    d->target_window->setPos(topLeft);
    positionAnimation()->setEasingCurve(QEasingCurve::OutQuad);
    positionAnimation()->setStartValue(topLeft);
    positionAnimation()->setEndValue(
        QPointF(d->target_window->propertyCache()->realGeometry().x(),
                d->target_window->propertyCache()->realGeometry().y()));
    scaleAnimation()->setEasingCurve(QEasingCurve::OutQuad);

    // TODO: use icon geometry signal
    scaleAnimation()->setStartValue(scaleStart);
    scaleAnimation()->setEndValue(1.0);
    opacityAnimation()->setEasingCurve(QEasingCurve::OutQuad);
    opacityAnimation()->setStartValue(DIMMED);
    opacityAnimation()->setEndValue(OPAQUE);

    animationGroup()->setDirection(QAbstractAnimation::Forward);
    animationGroup()->start();
}

void MCompositeWindowAnimation::windowClosed()
{
    Q_D(MCompositeWindowAnimation);
    if (d->handledByInvoker(Closing))
        return;

    positionAnimation()->setEasingCurve(QEasingCurve::InQuad);
    scaleAnimation()->setEasingCurve(QEasingCurve::InQuad);
    opacityAnimation()->setEasingCurve(QEasingCurve::InQuad);
    animationGroup()->setDirection(QAbstractAnimation::Backward);
    animationGroup()->start();
}

void MCompositeWindowAnimation::deferAnimation(MCompositeWindowAnimation::AnimationType type)
{
    Q_D(MCompositeWindowAnimation);
    d->pending_animation = type;
}

void MCompositeWindowAnimation::windowIconified()
{
    Q_D(MCompositeWindowAnimation);

    if (d->handledByInvoker(Iconify))
        return;
    positionAnimation()->setEasingCurve(QEasingCurve::InQuad);
    scaleAnimation()->setEasingCurve(QEasingCurve::InQuad);
    opacityAnimation()->setEasingCurve(QEasingCurve::InQuad);
    animationGroup()->setDirection(QAbstractAnimation::Backward);
    animationGroup()->start();
}

void MCompositeWindowAnimation::windowRestored()
{
    Q_D(MCompositeWindowAnimation);

    if (d->handledByInvoker(Restore))
        return;
    positionAnimation()->setEasingCurve(QEasingCurve::OutQuad);
    scaleAnimation()->setEasingCurve(QEasingCurve::OutQuad);
    opacityAnimation()->setEasingCurve(QEasingCurve::OutQuad);
    animationGroup()->setDirection(QAbstractAnimation::Forward);
    animationGroup()->start();
}

void MCompositeWindowAnimation::crossFadeTo(MCompositeWindow *cw)
{
    Q_D(MCompositeWindowAnimation);
    const MCompositeManager *mc = static_cast<MCompositeManager*>(qApp);

    if (d->crossfade)
        delete d->crossfade;
    d->crossfade = new McParallelAnimation(this);

    QPropertyAnimation *op = new QPropertyAnimation(this);
    op->setTargetObject(cw);
    op->setEasingCurve(QEasingCurve::Linear);
    op->setPropertyName("opacity");
    op->setDuration(mc->configInt("crossfade-duration"));
    op->setStartValue(0);
    op->setEndValue(1);
    d->crossfade->addAnimation(op);

    d->target_window2 = cw;

    d->crossfade->setDirection(QAbstractAnimation::Forward);
}

void MCompositeWindowAnimation::startTransition()
{
    Q_D(MCompositeWindowAnimation);

    switch (d->pending_animation) {
    case Showing:
        windowShown();
        d->pending_animation = NoAnimation;
        break;
    case Closing:
        windowClosed();
        d->pending_animation = NoAnimation;
        break;
    case Iconify:
        windowIconified();
        d->pending_animation = NoAnimation;
        break;
    case Restore:
        windowRestored();
        d->pending_animation = NoAnimation;
        break;
    case CrossFade:
        if (targetWindow2())
            targetWindow2()->show();
        d->crossfade->start();
        d->pending_animation = NoAnimation;
        break;
    default:  break;
    }
}

void MCompositeWindowAnimation::ensureAnimationVisible()
{
    // Always ensure the animation is REALLY visible. Z-values get corrected
    // later at checkStacking if needed
    if (targetWindow() &&
        targetWindow()->propertyCache()->windowState() != IconicState)
        targetWindow()->setZValue(
              ((MCompositeManager*)qApp)->d->stacking_list.size() + 1);
    if (targetWindow2())
        targetWindow2()->setZValue(
              ((MCompositeManager*)qApp)->d->stacking_list.size() + 2);
}

// plays the animation group;
void MCompositeWindowAnimation::start()
{
    animationGroup()->start();
}

// pauses the animation group;
void MCompositeWindowAnimation::pause()
{
    animationGroup()->pause();
}

void MCompositeWindowAnimation::finish()
{
  QParallelAnimationGroup *ag = animationGroup();

  // Let the timeline's finish() run.
  ag->setDirection(QAbstractAnimation::Backward);
  ag->start();
  ag->setCurrentTime(ag->duration());
}

bool MCompositeWindowAnimation::isActive()
{
    return (animationGroup()->state() != QAbstractAnimation::Stopped);
}

MCompositeWindowAnimation::AnimationType MCompositeWindowAnimation::pendingAnimation() const
{
    Q_D(const MCompositeWindowAnimation);

    return d->pending_animation;
}

/*!
  Enabled or disables this animation. Re-implement this function to customize
  how a custom animation can be disabled or disabled
 */
void MCompositeWindowAnimation::setEnabled(bool enabled)
{
    if (enabled) {
        if (animationGroup()->indexOfAnimation(positionAnimation()) == -1)
            animationGroup()->addAnimation(positionAnimation());
        if (animationGroup()->indexOfAnimation(scaleAnimation()) == -1)
            animationGroup()->addAnimation(scaleAnimation());
        if (animationGroup()->indexOfAnimation(opacityAnimation()) == -1)
            animationGroup()->addAnimation(opacityAnimation());
    } else if (!enabled) {
        animationGroup()->stop();
        animationGroup()->removeAnimation(positionAnimation());
        animationGroup()->removeAnimation(scaleAnimation());
        animationGroup()->removeAnimation(opacityAnimation());
    }
}

/*!
  \return Whether this animation can be replaced or not. By default, window
  animations can be replaced with a custom animation
 */
bool MCompositeWindowAnimation::isReplaceable() const
{
    Q_D(const MCompositeWindowAnimation);

    return d->is_replaceable;
}

/*!
   If \a replaceable is true, this animation can be replaced with another custom
   animation object. If \a replaceable is false, the animation may not be
   replaced with another animation and will be set for the lifetime of the
   window that is initially associated with it.
 */
void MCompositeWindowAnimation::setReplaceable(bool replaceable)
{
    Q_D(MCompositeWindowAnimation);

    d->is_replaceable = replaceable;
}

/*!
   Sets a custom external animation handler for animation \a type. If an
   external handler is set it will use the virtual functions of that animation
   \a handler instead of this object's handlers.
   To create an external handler, inherit from MAbstractAnimationHandler class
   and reimplement the required animation functions as needed.
 */
void MCompositeWindowAnimation::setAnimationHandler(AnimationType type,
                                                    MAbstractAnimationHandler* handler)
{
    Q_D(MCompositeWindowAnimation);

    handler->main_animator = this;
    d->animhandler[type] = handler;
}

void MCompositeWindowAnimation::disconnectHandler(MAbstractAnimationHandler* handler)
{
    Q_D(MCompositeWindowAnimation);
    int type = d->animhandler.indexOf(handler);
    if (type > -1)
        d->animhandler[type] = 0;
}

void MAbstractAnimationHandler::windowShown()
{// NOOP
}

void MAbstractAnimationHandler::windowClosed()
{// NOOP
}

void MAbstractAnimationHandler::windowIconified()
{// NOOP
}

void MAbstractAnimationHandler::windowRestored()
{// NOOP
}

MCompositeWindow* MAbstractAnimationHandler::targetWindow() const
{
    return target_window;
}

MAbstractAnimationHandler::~MAbstractAnimationHandler()
{
    if (main_animator)
        main_animator->disconnectHandler(this);
}
<commit_msg>unbreak ut_anim<commit_after>/***************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of mcompositor.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/
/*!
  \class MCompositeWindowAnimation
  \brief MCompositeWindowAnimation class which provides full control
  of position transformation and opacity animations of composite window objects.

  To create more complex animations, re-implement the virtual functions
  windowShown(), windowClosed(), widowIconified() and windowRestored();
  A QParallelAnimationGroup object is provided which can be a container
  for more complex animations for more flexibility.
*/
// TODO: import icongeometry. export signals when animation done to hook
// to composting on off

#include <QMetaMethod>
#include <QRectF>
#include <QDesktopWidget>
#include <QPropertyAnimation>
#include <mcompositewindow.h>
#include <mcompositewindowanimation.h>
#include <QApplication>
#include <QParallelAnimationGroup>
#include <mcompositemanager.h>
#include <mcompositemanager_p.h>
#include <QVector>

class McParallelAnimation: public QParallelAnimationGroup
{
public:
    McParallelAnimation(MCompositeWindowAnimation* p)
        :QParallelAnimationGroup(p),
         parent(p)
    {}

protected:
    virtual void updateCurrentTime(int currentTime)
    {
        MCompositeWindow::update();
        QParallelAnimationGroup::updateCurrentTime(currentTime);
    }

    virtual void updateState(QAbstractAnimation::State newState,
                             QAbstractAnimation::State oldState)
    {
        if (newState == QAbstractAnimation::Running &&
            oldState == QAbstractAnimation::Stopped) {
            parent->ensureAnimationVisible();
            if (parent->targetWindow())
                parent->targetWindow()->beginAnimation();
            if (parent->targetWindow2())
                parent->targetWindow2()->beginAnimation();
        } else if (newState == QAbstractAnimation::Stopped) {
            if (parent->targetWindow())
                parent->targetWindow()->endAnimation();
            if (parent->targetWindow2())
                parent->targetWindow2()->endAnimation();
        }
        QParallelAnimationGroup::updateState(newState, oldState);
    }
private:
    MCompositeWindowAnimation* parent;
};

class MCompositeWindowAnimationPrivate
{
public:

    MCompositeWindowAnimationPrivate(MCompositeWindowAnimation* animation)
        : crossfade(0),
          pending_animation(MCompositeWindowAnimation::NoAnimation),
          is_replaceable(true),
          animhandler(MCompositeWindowAnimation::AnimationTotal, 0)
    {
        const MCompositeManager *mc = static_cast<MCompositeManager*>(qApp);
        // default duration of 1 ms to keep "NOP animation" short
        int duration = 2;

        scale = new QPropertyAnimation(animation);
        scale->setPropertyName("scale");
        scale->setDuration(duration);
        scale->setStartValue(1);
        scale->setEndValue(1);

        position = new QPropertyAnimation(animation);
        position->setPropertyName("pos");
        position->setDuration(duration);
        position->setStartValue(QPoint(0, 0));
        position->setEndValue(QPoint(0, 0));

        opacity = new QPropertyAnimation(animation);
        opacity->setPropertyName("opacity");
        opacity->setDuration(duration);
        opacity->setStartValue(1);
        opacity->setEndValue(1);

        scalepos = new McParallelAnimation(animation);
        scalepos->addAnimation(scale);
        scalepos->addAnimation(position);
        scalepos->addAnimation(opacity);

        QObject::connect(scalepos, SIGNAL(finished()), animation,
                         SLOT(finalizeState()));
    }

    void setTargetWindow(MCompositeWindow* window)
    {
        if (scale && position && opacity) {
            scale->setTargetObject(window);
            position->setTargetObject(window);
            opacity->setTargetObject(window);
        }
    }

    bool handledByInvoker(MCompositeWindowAnimation::AnimationType type)
    {
        MAbstractAnimationHandler* handler = animhandler[type];
        if (handler) {
            handler->target_window = target_window;
            switch (type) {
            case MCompositeWindowAnimation::Showing:
                handler->windowShown();
                return true;
            case MCompositeWindowAnimation::Closing:
                handler->windowClosed();
                return true;
            case MCompositeWindowAnimation::Iconify:
                handler->windowIconified();
                return true;
            case MCompositeWindowAnimation::Restore:
                handler->windowRestored();
                return true;
            default:
                break;
            }
        }

        return false;
    }

    QPointer<MCompositeWindow> target_window, target_window2;
    QPointer<QPropertyAnimation> scale;
    QPointer<QPropertyAnimation> position;
    QPointer<QPropertyAnimation> opacity;
    McParallelAnimation* scalepos, *crossfade;
    MCompositeWindowAnimation::AnimationType pending_animation;
    bool is_replaceable;
    QVector< MAbstractAnimationHandler* > animhandler;
};

MCompositeWindowAnimation::MCompositeWindowAnimation(QObject* parent)
    :QObject(parent),
     d_ptr(new MCompositeWindowAnimationPrivate(this))
{
}

MCompositeWindowAnimation::~MCompositeWindowAnimation()
{
}

void MCompositeWindowAnimation::setTargetWindow(MCompositeWindow* window)
{
    Q_D(MCompositeWindowAnimation);

    // never override a non-replaceable animation
    if (window->animator && !window->animator->isReplaceable()) {
        deleteLater();
        return;
    }

    // replace the old animator if there is one
    if (window->animator && (window->animator != this))
        delete window->animator;

    d->target_window = window;
    d->target_window->animator = this;
    disconnect(SIGNAL(q_finalizeState()));
    connect(this, SIGNAL(q_finalizeState()), window, SLOT(finalizeState()));

    d->setTargetWindow(window);
}

void MCompositeWindowAnimation::finalizeState()
{
    emit q_finalizeState();
}

// returns a group animation for this animator
QParallelAnimationGroup* MCompositeWindowAnimation::animationGroup() const
{
    Q_D(const MCompositeWindowAnimation);
    return d->scalepos;
}

MCompositeWindow* MCompositeWindowAnimation::targetWindow() const
{
    Q_D(const MCompositeWindowAnimation);
    return d->target_window;
}

MCompositeWindow* MCompositeWindowAnimation::targetWindow2() const
{
    Q_D(const MCompositeWindowAnimation);
    return d->target_window2;
}

// Exposed animation properties
QPropertyAnimation* MCompositeWindowAnimation::scaleAnimation() const
{
    Q_D(const MCompositeWindowAnimation);
    return d->scale;
}

QPropertyAnimation* MCompositeWindowAnimation::positionAnimation() const
{
    Q_D(const MCompositeWindowAnimation);
    return d->position;
}

QPropertyAnimation* MCompositeWindowAnimation::opacityAnimation() const
{
    Q_D(const MCompositeWindowAnimation);
    return d->opacity;
}

// Default effect
void MCompositeWindowAnimation::windowShown()
{
#define OPAQUE 1.0
#define DIMMED 0.1
    Q_D(MCompositeWindowAnimation);
    if (!d->target_window)
        return;

    if (d->handledByInvoker(Showing))
        return;

    const qreal scaleStart = 0.2;
    const QRectF &iconGeometry = d->target_window->propertyCache()->iconGeometry();
    QPointF topLeft = iconGeometry.topLeft();

    if (iconGeometry.isEmpty()) {
        const QRectF d = QApplication::desktop()->availableGeometry();
        topLeft.setX(d.width()/2.0f * (1.0f-scaleStart));
        topLeft.setY(d.height()/2.0f * (1.0f-scaleStart));
    }

    d->target_window->setPos(topLeft);
    positionAnimation()->setEasingCurve(QEasingCurve::OutQuad);
    positionAnimation()->setStartValue(topLeft);
    positionAnimation()->setEndValue(
        QPointF(d->target_window->propertyCache()->realGeometry().x(),
                d->target_window->propertyCache()->realGeometry().y()));
    scaleAnimation()->setEasingCurve(QEasingCurve::OutQuad);

    // TODO: use icon geometry signal
    scaleAnimation()->setStartValue(scaleStart);
    scaleAnimation()->setEndValue(1.0);
    opacityAnimation()->setEasingCurve(QEasingCurve::OutQuad);
    opacityAnimation()->setStartValue(DIMMED);
    opacityAnimation()->setEndValue(OPAQUE);

    animationGroup()->setDirection(QAbstractAnimation::Forward);
    animationGroup()->start();
}

void MCompositeWindowAnimation::windowClosed()
{
    Q_D(MCompositeWindowAnimation);
    if (d->handledByInvoker(Closing))
        return;

    positionAnimation()->setEasingCurve(QEasingCurve::InQuad);
    scaleAnimation()->setEasingCurve(QEasingCurve::InQuad);
    opacityAnimation()->setEasingCurve(QEasingCurve::InQuad);
    animationGroup()->setDirection(QAbstractAnimation::Backward);
    animationGroup()->start();
}

void MCompositeWindowAnimation::deferAnimation(MCompositeWindowAnimation::AnimationType type)
{
    Q_D(MCompositeWindowAnimation);
    d->pending_animation = type;
}

void MCompositeWindowAnimation::windowIconified()
{
    Q_D(MCompositeWindowAnimation);

    if (d->handledByInvoker(Iconify))
        return;
    positionAnimation()->setEasingCurve(QEasingCurve::InQuad);
    scaleAnimation()->setEasingCurve(QEasingCurve::InQuad);
    opacityAnimation()->setEasingCurve(QEasingCurve::InQuad);
    animationGroup()->setDirection(QAbstractAnimation::Backward);
    animationGroup()->start();
}

void MCompositeWindowAnimation::windowRestored()
{
    Q_D(MCompositeWindowAnimation);

    if (d->handledByInvoker(Restore))
        return;
    positionAnimation()->setEasingCurve(QEasingCurve::OutQuad);
    scaleAnimation()->setEasingCurve(QEasingCurve::OutQuad);
    opacityAnimation()->setEasingCurve(QEasingCurve::OutQuad);
    animationGroup()->setDirection(QAbstractAnimation::Forward);
    animationGroup()->start();
}

void MCompositeWindowAnimation::crossFadeTo(MCompositeWindow *cw)
{
    Q_D(MCompositeWindowAnimation);
    const MCompositeManager *mc = static_cast<MCompositeManager*>(qApp);

    if (d->crossfade)
        delete d->crossfade;
    d->crossfade = new McParallelAnimation(this);

    QPropertyAnimation *op = new QPropertyAnimation(this);
    op->setTargetObject(cw);
    op->setEasingCurve(QEasingCurve::Linear);
    op->setPropertyName("opacity");
    op->setDuration(mc->configInt("crossfade-duration"));
    op->setStartValue(0);
    op->setEndValue(1);
    d->crossfade->addAnimation(op);

    d->target_window2 = cw;

    d->crossfade->setDirection(QAbstractAnimation::Forward);
}

void MCompositeWindowAnimation::startTransition()
{
    Q_D(MCompositeWindowAnimation);

    switch (d->pending_animation) {
    case Showing:
        windowShown();
        d->pending_animation = NoAnimation;
        break;
    case Closing:
        windowClosed();
        d->pending_animation = NoAnimation;
        break;
    case Iconify:
        windowIconified();
        d->pending_animation = NoAnimation;
        break;
    case Restore:
        windowRestored();
        d->pending_animation = NoAnimation;
        break;
    case CrossFade:
        if (targetWindow2())
            targetWindow2()->show();
        d->crossfade->start();
        d->pending_animation = NoAnimation;
        break;
    default:  break;
    }
}

void MCompositeWindowAnimation::ensureAnimationVisible()
{
    // Always ensure the animation is REALLY visible. Z-values get corrected
    // later at checkStacking if needed
    if (targetWindow() &&
        targetWindow()->propertyCache()->windowState() != IconicState)
        targetWindow()->setZValue(
              ((MCompositeManager*)qApp)->d->stacking_list.size() + 1);
    if (targetWindow2())
        targetWindow2()->setZValue(
              ((MCompositeManager*)qApp)->d->stacking_list.size() + 2);
}

// plays the animation group;
void MCompositeWindowAnimation::start()
{
    animationGroup()->start();
}

// pauses the animation group;
void MCompositeWindowAnimation::pause()
{
    animationGroup()->pause();
}

void MCompositeWindowAnimation::finish()
{
  QParallelAnimationGroup *ag = animationGroup();

  // Let the timeline's finish() run.
  ag->setDirection(QAbstractAnimation::Backward);
  ag->start();
  ag->setCurrentTime(ag->duration());
}

bool MCompositeWindowAnimation::isActive()
{
    return (animationGroup()->state() != QAbstractAnimation::Stopped);
}

MCompositeWindowAnimation::AnimationType MCompositeWindowAnimation::pendingAnimation() const
{
    Q_D(const MCompositeWindowAnimation);

    return d->pending_animation;
}

/*!
  Enabled or disables this animation. Re-implement this function to customize
  how a custom animation can be disabled or disabled
 */
void MCompositeWindowAnimation::setEnabled(bool enabled)
{
    if (enabled) {
        if (animationGroup()->indexOfAnimation(positionAnimation()) == -1)
            animationGroup()->addAnimation(positionAnimation());
        if (animationGroup()->indexOfAnimation(scaleAnimation()) == -1)
            animationGroup()->addAnimation(scaleAnimation());
        if (animationGroup()->indexOfAnimation(opacityAnimation()) == -1)
            animationGroup()->addAnimation(opacityAnimation());
    } else if (!enabled) {
        animationGroup()->stop();
        animationGroup()->removeAnimation(positionAnimation());
        animationGroup()->removeAnimation(scaleAnimation());
        animationGroup()->removeAnimation(opacityAnimation());
    }
}

/*!
  \return Whether this animation can be replaced or not. By default, window
  animations can be replaced with a custom animation
 */
bool MCompositeWindowAnimation::isReplaceable() const
{
    Q_D(const MCompositeWindowAnimation);

    return d->is_replaceable;
}

/*!
   If \a replaceable is true, this animation can be replaced with another custom
   animation object. If \a replaceable is false, the animation may not be
   replaced with another animation and will be set for the lifetime of the
   window that is initially associated with it.
 */
void MCompositeWindowAnimation::setReplaceable(bool replaceable)
{
    Q_D(MCompositeWindowAnimation);

    d->is_replaceable = replaceable;
}

/*!
   Sets a custom external animation handler for animation \a type. If an
   external handler is set it will use the virtual functions of that animation
   \a handler instead of this object's handlers.
   To create an external handler, inherit from MAbstractAnimationHandler class
   and reimplement the required animation functions as needed.
 */
void MCompositeWindowAnimation::setAnimationHandler(AnimationType type,
                                                    MAbstractAnimationHandler* handler)
{
    Q_D(MCompositeWindowAnimation);

    handler->main_animator = this;
    d->animhandler[type] = handler;
}

void MCompositeWindowAnimation::disconnectHandler(MAbstractAnimationHandler* handler)
{
    Q_D(MCompositeWindowAnimation);
    int type = d->animhandler.indexOf(handler);
    if (type > -1)
        d->animhandler[type] = 0;
}

void MAbstractAnimationHandler::windowShown()
{// NOOP
}

void MAbstractAnimationHandler::windowClosed()
{// NOOP
}

void MAbstractAnimationHandler::windowIconified()
{// NOOP
}

void MAbstractAnimationHandler::windowRestored()
{// NOOP
}

MCompositeWindow* MAbstractAnimationHandler::targetWindow() const
{
    return target_window;
}

MAbstractAnimationHandler::~MAbstractAnimationHandler()
{
    if (main_animator)
        main_animator->disconnectHandler(this);
}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "BaseFrame.h"
#include "GL2DProgram.h"
#include "GL3DProgram.h"
#include "ShaderGroup.h"
#include "ActorPlayer.h"
#include "VoxEngine.h"

#include "Window.h"
#include "Button.h"
#include "Listbox.h"

#include "OS.h"

#include "ParticleSystem.h"
#include "ParticleData.h"

#include "Demo.h"
#include "Building.h"

BaseFrame::BaseFrame(ShaderGroup * shaders) : GameSystem(shaders), Physics(&Voxels) {
	cout << "\t Constructing base frame\n";

	// Create the first person controller depending on the current platform
#ifdef __MOBILE__
	FirstPerson = new FirstPersonModeMobile();
#else
	FirstPerson = new FirstPersonMode();
#endif

	// Enable the first person controller
	FirstPerson->Enable(true);

	cout << "\t Finished base frame\n";
	//testSystem = NULL;
}
BaseFrame::~BaseFrame() {

}

//synchronously saves the game
bool BaseFrame::Save(string saveFile) {
	//Serialize this class
	vector<unsigned char> saveData = Savable::Serialize(this);

	//Open for writing binary data
	SDL_RWops *file = SDL_RWFromFile(saveFile.c_str(), "wb");

	//Check the file opened
	if(!file)
		return false;
	//Write the file containing all the save data
	SDL_RWwrite(file,(char*)&saveData.front(),1,saveData.size());
	SDL_RWclose(file);

	return true;
}
//synchronously loads the game over any existing data
bool BaseFrame::Load(string saveFile) {
	SDL_RWops *file = SDL_RWFromFile(saveFile.c_str(), "rb");
	long size;

	//Check the file opened
	if(!file)
		return false;

	//Determine file size
	SDL_RWseek(file , 0 , RW_SEEK_END);
	size = (long)SDL_RWtell(file);
	SDL_RWseek(file,0,RW_SEEK_SET);

	//allocate space for file data
	vector<unsigned char> fileData(size);
	SDL_RWread(file,&fileData.front(), 1, (size_t)size);
	SDL_RWclose(file);

	//Deserialize the data
	Savable::Deserialize(fileData,this);

	return 0;
}

void BaseFrame::Load(Json::Value & parentValue, LoadData & loadData) {
	//This is necessary because FirstPerson is a user owned handle
	//so we must notify the save system we have constructed it
	REPAIR_HANDLE(FirstPerson);
	//Continue loading
	Savable::Load(parentValue,loadData);
}

void BaseFrame::OnFrameFocus() {
	//Build actors, right now just the player
	player = new ActorPlayer();
	//The player autoregisters himself with the actor system
	//we do not need to do that by hand

	//The physics demo
	//we won't have this forever
	demo = new Demo();
#ifdef __MOBILE__
	demoWindow = new DemoWindow(demo);
	Controls.AddWindow(demoWindow);
#endif
	notification = Notification::init();
	Controls.AddWindow(notification);
}

void BaseFrame::Build() {

	//load the audio
	cout << "Loading audio\n";
	audio = new AudioPlayer(100);
	audio->Subscribe(player);

}

bool BaseFrame::Update(double delta,double now, vector<InputEvent> inputEvents) {
	//Issue events to dialog
	//Run the dialog system and monitor pressed keys
	passEventsToControl(inputEvents);

	//Update the looking direction
	FirstPerson->ReadInput(currentlyPressedKeys,inputEvents);

	//The player is the only actor which reads input
	//player->ReadInput(inputEvents);

	Voxels.Update(player->GetPosition());

	//Update actors
	Actors.Update(delta,now);

	demo->OnInput(inputEvents,player->GetPosition(),FirstPerson->GetLookVector());
	demo->Update(now,delta);

	//Update physics/Particles
	Physics.Update(delta,now);
	Particles.UpdateCloud(now,delta);
	//if (testSystem != NULL)
		//testSystem->UpdateEmitter(now);

	//Update demo
	demo->CheckTouchInput(player->GetPosition(),FirstPerson->GetLookVector());
	return true;
}

void BaseFrame::Draw(double width, double height) {
	vec2 viewPortSize = vec2((float)width,(float)height);
	//Save size in camera as well (for unprojection)
	Camera.UpdateViewSize(viewPortSize);

	//Update the texture caching system
	Textures.Refresh();

	//Startup 3d rendering
	GL3DProgram * shaders3d = (GL3DProgram*)shaders->GetShader("3d");
	shaders3d->UseProgram();
	//Setup basics of shader program per-frame
	shaders3d->Model.Reset();
	shaders3d->Model.Apply();
	shaders3d->Acid.SetCurrentTime(VoxEngine::GetGameSimTime());
	//Acid factor currently managed by the demo system
	//this will be moved to a more powerful game logic system in the future
	shaders3d->Acid.SetAcidFactor(demo->CurrentAcidStrength);
	//Enable sensible defaults
	glEnable(GL_BLEND);
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	//Compute view distance and handle fog
	ViewDistance.CalculateAndApply(shaders3d,fpsCount.GetFps());
	shaders3d->Fog.SetFogColor(vec4(1,1,1,1));

	//We add 1.5 to ground level. This assumes the person is 5ft between the ground
	//and his eye line
	vec3 pos = player->GetPosition();
	//The player is 3 height right now
	pos.z += 2.5;
	//Calculate voxel draw rectangle
	pair<vec2,vec2> drawRectangle = ViewDistance.VoxDrawCoordinates(viewPortSize,vec2(pos),FirstPerson->GetAngleVector().x/180.0f*(float)M_PI);
	vec2 minPoint = drawRectangle.first;
	vec2 maxPoint = drawRectangle.second;

	//Draw the frame
	//camera draw also sets up world light
	Camera.SetCameraView(pos,FirstPerson->GetLookVector());
	Camera.Draw(shaders3d);

	// Draw voxels
	Voxels.Draw(shaders3d,pos,(int)minPoint.x,(int)minPoint.y,(int)maxPoint.x,(int)maxPoint.y);
	//The physics system uses the same texture that the voxels above binds every time it draws
	//so it must always immediately follow Voxels.draw()
	Physics.Draw(shaders);

	//The particle system will use a different shader entirely soon
	Particles.Draw(shaders);

	//Update the voxel debug counter
	Controls.Debug.Voxels = Voxels.GetLastVoxelCount();

	//Call the parent draw to draw interface
	GameSystem::Draw(width,height);

	// Draw the UI for joysticks
	//Assume that gameSystem::Draw has set up the 2d shader
	GL2DProgram * shaders2d = (GL2DProgram*)shaders->GetShader("2d");
	FirstPerson->Draw(width, height, shaders2d);
} <commit_msg>disabled debug fog color<commit_after>#include "stdafx.h"
#include "BaseFrame.h"
#include "GL2DProgram.h"
#include "GL3DProgram.h"
#include "ShaderGroup.h"
#include "ActorPlayer.h"
#include "VoxEngine.h"

#include "Window.h"
#include "Button.h"
#include "Listbox.h"

#include "OS.h"

#include "ParticleSystem.h"
#include "ParticleData.h"

#include "Demo.h"
#include "Building.h"

BaseFrame::BaseFrame(ShaderGroup * shaders) : GameSystem(shaders), Physics(&Voxels) {
	cout << "\t Constructing base frame\n";

	// Create the first person controller depending on the current platform
#ifdef __MOBILE__
	FirstPerson = new FirstPersonModeMobile();
#else
	FirstPerson = new FirstPersonMode();
#endif

	// Enable the first person controller
	FirstPerson->Enable(true);

	cout << "\t Finished base frame\n";
	//testSystem = NULL;
}
BaseFrame::~BaseFrame() {

}

//synchronously saves the game
bool BaseFrame::Save(string saveFile) {
	//Serialize this class
	vector<unsigned char> saveData = Savable::Serialize(this);

	//Open for writing binary data
	SDL_RWops *file = SDL_RWFromFile(saveFile.c_str(), "wb");

	//Check the file opened
	if(!file)
		return false;
	//Write the file containing all the save data
	SDL_RWwrite(file,(char*)&saveData.front(),1,saveData.size());
	SDL_RWclose(file);

	return true;
}
//synchronously loads the game over any existing data
bool BaseFrame::Load(string saveFile) {
	SDL_RWops *file = SDL_RWFromFile(saveFile.c_str(), "rb");
	long size;

	//Check the file opened
	if(!file)
		return false;

	//Determine file size
	SDL_RWseek(file , 0 , RW_SEEK_END);
	size = (long)SDL_RWtell(file);
	SDL_RWseek(file,0,RW_SEEK_SET);

	//allocate space for file data
	vector<unsigned char> fileData(size);
	SDL_RWread(file,&fileData.front(), 1, (size_t)size);
	SDL_RWclose(file);

	//Deserialize the data
	Savable::Deserialize(fileData,this);

	return 0;
}

void BaseFrame::Load(Json::Value & parentValue, LoadData & loadData) {
	//This is necessary because FirstPerson is a user owned handle
	//so we must notify the save system we have constructed it
	REPAIR_HANDLE(FirstPerson);
	//Continue loading
	Savable::Load(parentValue,loadData);
}

void BaseFrame::OnFrameFocus() {
	//Build actors, right now just the player
	player = new ActorPlayer();
	//The player autoregisters himself with the actor system
	//we do not need to do that by hand

	//The physics demo
	//we won't have this forever
	demo = new Demo();
#ifdef __MOBILE__
	demoWindow = new DemoWindow(demo);
	Controls.AddWindow(demoWindow);
#endif
	notification = Notification::init();
	Controls.AddWindow(notification);
}

void BaseFrame::Build() {

	//load the audio
	cout << "Loading audio\n";
	audio = new AudioPlayer(100);
	audio->Subscribe(player);

}

bool BaseFrame::Update(double delta,double now, vector<InputEvent> inputEvents) {
	//Issue events to dialog
	//Run the dialog system and monitor pressed keys
	passEventsToControl(inputEvents);

	//Update the looking direction
	FirstPerson->ReadInput(currentlyPressedKeys,inputEvents);

	//The player is the only actor which reads input
	//player->ReadInput(inputEvents);

	Voxels.Update(player->GetPosition());

	//Update actors
	Actors.Update(delta,now);

	demo->OnInput(inputEvents,player->GetPosition(),FirstPerson->GetLookVector());
	demo->Update(now,delta);

	//Update physics/Particles
	Physics.Update(delta,now);
	Particles.UpdateCloud(now,delta);
	//if (testSystem != NULL)
		//testSystem->UpdateEmitter(now);

	//Update demo
	demo->CheckTouchInput(player->GetPosition(),FirstPerson->GetLookVector());
	return true;
}

void BaseFrame::Draw(double width, double height) {
	vec2 viewPortSize = vec2((float)width,(float)height);
	//Save size in camera as well (for unprojection)
	Camera.UpdateViewSize(viewPortSize);

	//Update the texture caching system
	Textures.Refresh();

	//Startup 3d rendering
	GL3DProgram * shaders3d = (GL3DProgram*)shaders->GetShader("3d");
	shaders3d->UseProgram();
	//Setup basics of shader program per-frame
	shaders3d->Model.Reset();
	shaders3d->Model.Apply();
	shaders3d->Acid.SetCurrentTime(VoxEngine::GetGameSimTime());
	//Acid factor currently managed by the demo system
	//this will be moved to a more powerful game logic system in the future
	shaders3d->Acid.SetAcidFactor(demo->CurrentAcidStrength);
	//Enable sensible defaults
	glEnable(GL_BLEND);
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	//Compute view distance and handle fog
	ViewDistance.CalculateAndApply(shaders3d,fpsCount.GetFps());
	shaders3d->Fog.SetFogColor(vec4(0,0,0,1));

	//We add 1.5 to ground level. This assumes the person is 5ft between the ground
	//and his eye line
	vec3 pos = player->GetPosition();
	//The player is 3 height right now
	pos.z += 2.5;
	//Calculate voxel draw rectangle
	pair<vec2,vec2> drawRectangle = ViewDistance.VoxDrawCoordinates(viewPortSize,vec2(pos),FirstPerson->GetAngleVector().x/180.0f*(float)M_PI);
	vec2 minPoint = drawRectangle.first;
	vec2 maxPoint = drawRectangle.second;

	//Draw the frame
	//camera draw also sets up world light
	Camera.SetCameraView(pos,FirstPerson->GetLookVector());
	Camera.Draw(shaders3d);

	// Draw voxels
	Voxels.Draw(shaders3d,pos,(int)minPoint.x,(int)minPoint.y,(int)maxPoint.x,(int)maxPoint.y);
	//The physics system uses the same texture that the voxels above binds every time it draws
	//so it must always immediately follow Voxels.draw()
	Physics.Draw(shaders);

	//The particle system will use a different shader entirely soon
	Particles.Draw(shaders);

	//Update the voxel debug counter
	Controls.Debug.Voxels = Voxels.GetLastVoxelCount();

	//Call the parent draw to draw interface
	GameSystem::Draw(width,height);

	// Draw the UI for joysticks
	//Assume that gameSystem::Draw has set up the 2d shader
	GL2DProgram * shaders2d = (GL2DProgram*)shaders->GetShader("2d");
	FirstPerson->Draw(width, height, shaders2d);
} <|endoftext|>


<commit_before>/*//////////////////////////////////////////////////////////////////
////       SKIRT -- an advanced radiative transfer code         ////
////       © Astronomical Observatory, Ghent University         ////
///////////////////////////////////////////////////////////////// */

#ifndef TORUSGEOMETRY_HPP
#define TORUSGEOMETRY_HPP

#include "AxGeometry.hpp"

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

/** The TorusGeometry class is a subclass of the AxGeometry class and describes the geometry of an
    axisymmetric torus as assumed to be present in the centre of active galactic nuclei (AGN). This
    geometry is described by a radial power-law density with a finite opening angle; see Stalevski
    et al. (2012, MNRAS, 420, 2756–2772) and Granato & Danese (1994, MNRAS, 268, 235). In formula,
    it is most easily expressed in spherical coordinates as \f[ \rho(r,\theta) = A\,
    r^{-p}\,{\text{e}}^{-q|\cos\theta|} \quad\text{for } r_{\text{min}}<r<r_{\text{max}} \text{ and
    } \frac{\pi}{2}-\Delta<\theta<\frac{\pi}{2} +\Delta. \f] There are five free parameters
    describing this dust geometry: the inner and outer radii \f$r_{\text{min}}\f$ and
    \f$r_{\text{max}}\f$ of the torus, the radial power law index \f$p\f$, the polar index \f$q\f$
    and the angle \f$\Delta\f$ describing the opening angle of the torus.

    If the dusty system under consideration is in the vicinity of an AGN central engine or another
    source which is luminous enough to heat the dust up to sublimation temperature, the inner
    radius should correspond to sublimation radius and scale as \f$ r_{\text{min}} \propto
    L(\theta)^{0.5}\f$ (Barvainis, 1987, ApJ, 320, 537, eq (5)). If the primary source assumes
    anisotropic emission, the inner radius must follow the same dependence as the distribution of
    the primary source luminosity. Otherwise, dust temperature on the inner boundary of geometry is
    very likely to be under- or over-estimated. Thus, if the NetzerAccretionDiskGeometry
    distribution is chosen to describe primary source emission, it is recommended to turn on the
    anisotropic inner radius option for the torus geometry. The inner radius will then be set by
    the following formula: \f[ r_{\text{min}} \propto (\cos\theta\,(2\cos\theta+1))^{0.5}.\f] This
    should allow dust to approach all the way to the primary central source in the equatorial
    plane. However, due to the finite resolution of dust cells, it may happen that some of the
    innermost cells end up with unphysically high temperatures. For this reason, there is an
    additional input parameter, the cutoff radius \f$r_{\text{cut}}\f$. The value of the cutoff
    radius is usually found after a few trial-and-error experiments by inspecting temperature
    distribution maps, until the inner wall of the geometry is at the expected sublimation
    temperature for a given dust population.

    The total dust mass of the model corresponds to the mass of the original geometry, before the
    inner wall is reshaped to account for anisotropy; the difference is usually rather small. */
class TorusGeometry : public AxGeometry
{
    Q_OBJECT
    Q_CLASSINFO("Title", "a torus geometry")

    Q_CLASSINFO("Property", "expon")
    Q_CLASSINFO("Title", "the radial powerlaw exponent p of the torus")
    Q_CLASSINFO("MinValue", "0")

    Q_CLASSINFO("Property", "index")
    Q_CLASSINFO("Title", "the polar index q of the torus")
    Q_CLASSINFO("MinValue", "0")

    Q_CLASSINFO("Property", "openAngle")
    Q_CLASSINFO("Title", "the half opening angle of the torus")
    Q_CLASSINFO("Quantity", "posangle")
    Q_CLASSINFO("MinValue", "0 deg")
    Q_CLASSINFO("MaxValue", "90 deg")

    Q_CLASSINFO("Property", "minRadius")
    Q_CLASSINFO("Title", "the minimum radius of the torus")
    Q_CLASSINFO("Quantity", "length")
    Q_CLASSINFO("MinValue", "0")

    Q_CLASSINFO("Property", "maxRadius")
    Q_CLASSINFO("Title", "the maximum radius of the torus")
    Q_CLASSINFO("Quantity", "length")
    Q_CLASSINFO("MinValue", "0")

    Q_CLASSINFO("Property", "anisoRadius")
    Q_CLASSINFO("Title", "the anisotropic inner radius")
    Q_CLASSINFO("Default", "no")

    Q_CLASSINFO("Property", "cutRadius")
    Q_CLASSINFO("Title", "the inner cutoff radius of the torus")
    Q_CLASSINFO("Quantity", "length")
    Q_CLASSINFO("MinValue", "0")
    Q_CLASSINFO("Default", "0")
    Q_CLASSINFO("RelevantIf", "anisoRadius")

    //============= Construction - Setup - Destruction =============

public:
    /** The default constructor. */
    Q_INVOKABLE TorusGeometry();

protected:
    /** This function verifies the validity of the geometry parameters. The normalization
        parameter \f$A\f$ is set by the normalization
        condition that total mass equals one, i.e. \f[ 1 = 2\pi\, A\,
        \int_{\pi/2-\Delta}^{\pi/2+\Delta} e^{-q|\cos\theta|}\sin\theta\, {\text{d}}\theta
        \int_{r_{\text{min}}}^{r_{\text{max}}} r^{2-p}\, {\text{d}}r. \f] This results in \f[ A =
        \frac{q}{4\pi\, (1-{\text{e}}^{-q\sin\Delta})}\, \frac{1}{ {\text{gln}}_{p-2}\,
        r_{\text{max}} - {\text{gln}}_{p-2}\, r_{\text{min}} }, \f] with \f${\text{gln}}_p\, x\f$
        the generalized logarithm defined in SpecialFunctions::gln. If \f$q=0\f$, this expression
        reduces to \f[ A = \frac{1}{4\pi\,\sin\Delta\, ({\text{gln}}_{p-2}\,
        r_{\text{max}} - {\text{gln}}_{p-2}\, r_{\text{min}} )}. \f] */
    void setupSelfBefore();

    //======== Setters & Getters for Discoverable Attributes =======

public:
    /** Sets the radial power law exponent \f$p\f$ of the torus. */
    Q_INVOKABLE void setExpon(double value);

    /** Returns the radial power law exponent \f$p\f$ of the torus. */
    Q_INVOKABLE double expon() const;

    /** Sets the polar index \f$q\f$ of the torus. */
    Q_INVOKABLE void setIndex(double value);

    /** Returns the polar index \f$q\f$ of the torus. */
    Q_INVOKABLE double index() const;

    /** Sets the half opening angle of the torus. */
    Q_INVOKABLE void setOpenAngle(double value);

    /** Returns the half opening angle of the torus. */
    Q_INVOKABLE double openAngle() const;

    /** Sets the minimum radius of the torus. */
    Q_INVOKABLE void setMinRadius(double value);

    /** Returns the minimum radius of the torus. */
    Q_INVOKABLE double minRadius() const;

    /** Sets the maximum radius of the torus. */
    Q_INVOKABLE void setMaxRadius(double value);

    /** Returns the maximum radius of the torus. */
    Q_INVOKABLE double maxRadius() const;

    /** Sets the flag indicating whether to reshape the inner wall of the torus according to the
        Netzer luminosity profile. */
    Q_INVOKABLE void setAnisoRadius(bool value);

    /** Returns the flag indicating whether to reshape the inner wall of the torus according to the
        Netzer luminosity profile. */
    Q_INVOKABLE bool anisoRadius() const;

    /** Sets the inner cutoff radius of the torus. */
    Q_INVOKABLE void setCutRadius(double value);

    /** Returns the inner cutoff radius of the torus. */
    Q_INVOKABLE double cutRadius() const;

    //======================== Other Functions =======================

public:
    /** This function returns the density \f$\rho(R,z)\f$ at the cylindrical radius
        \f$R\f$ and height \f$z\f$. It just implements the analytical formula. */
    double density(double R, double z) const;

    /** This function generates a random position from the torus geometry, by
        drawing a random point from the three-dimensional probability density \f$p({\bf{r}})\,
        {\text{d}}{\bf{r}} = \rho({\bf{r}})\, {\text{d}}{\bf{r}}\f$. For the torus geometry,
        the density is a separable function of \f$r\f$ and \f$\theta\f$, so that a random
        position can hence be constructed by combining random spherical coordinates, each chosen
        from their own probability distributions. A random azimuth \f$\phi\f$ is readily found by
        chosing a random deviate \f${\cal{X}}\f$ and setting \f$ \phi = 2\pi {\cal{X}} \f$.

        For the
        radial coordinate, the appropriate probability distribution is \f$ p(r)\,{\text{d}}r \propto
        r^{p-2}\,{\text{d}}r \f$. A random radius is generated by picking a new uniform deviate
        \f${\cal{X}}\f$, and solving the equation \f[ {\cal{X}} = \int_0^r p(r')\, {\text{d}}r'
        \f] for \f$r\f$. We obtain \f[ {\cal{X}} =
        \frac{ {\text{gln}}_{p-2}\, r - {\text{gln}}_{p-2}\, r_{\text{min}} }{
        {\text{gln}}_{p-2}\, r_{\text{max}} - {\text{gln}}_{p-2}\,
        r_{\text{min}} }. \f] Inverting this equation results in \f[ r =
        {\text{gexp}}_{p-2} \Big[ {\text{gln}}_{p-2}\, r_{\text{min}} + X (
        {\text{gln}}_{p-2}\, r_{\text{max}} - {\text{gln}}_{p-2}\, r_{\text{min}} )
        \Bigr]. \f] In these expressions, \f${\text{gln}}_p\,x\f$ and \f${\text{gexp}}_p\,x\f$ are the
        generalized logarithm and exponential functions defined in
        SpecialFunctions::gln and SpecialFunctions::gexp respectively.

        Finally, for the polar angle, the appropriate distribution function is \f[ p(\theta)\,
        {\text{d}}\theta \propto e^{-q|\cos\theta|}\sin\theta\, {\text{d}}\theta. \f] A random polar
        angle is generated by picking a new uniform deviate \f${\cal{X}}\f$, and solving the equation
        \f[ {\cal{X}} = \int_0^\theta p(\theta')\, {\text{d}}\theta' \f] for \f$\theta\f$. We obtain
        after some calculation \f[ {\cal{X}} = \begin{cases} \; \dfrac12 \left( 1 -
        \dfrac{1-{\text{e}}^{-q\cos\theta}}{1-{\text{e}}^{-q\sin\Delta}} \right) & \quad\text{for }
        \frac{\pi}{2}-\Delta < \theta < \frac{\pi}{2} \\[1.2em]  \;\dfrac12 \left( 1 +
        \dfrac{1-{\text{e}}^{q\cos\theta}}{1-{\text{e}}^{-q\sin\Delta}} \right) & \quad\text{for }
        \frac{\pi}{2} < \theta < \frac{\pi}{2}+\Delta \end{cases} \f] Inverting this
        gives \f[ \cos\theta = \begin{cases}\; -\dfrac{1}{q} \ln\left[ 1-\left(1-
        {\text{e}}^{-q\sin\Delta}\right) (1-2{\cal{X}}) \right] &
        \quad\text{if $0<{\cal{X}}<\tfrac12$} \\[1.2em] \; \dfrac{1}{q} \ln\left[ 1-\left(1
        -{\text{e}}^{-q\sin\Delta}\right) (2{\cal{X}}-1) \right] & \quad\text{if $\tfrac12<{\cal{X}}<1$}
        \end{cases}. \f] */
    Position generatePosition() const;

    /** This function returns the radial surface density, i.e. the integration of
        the density along a line in the equatorial plane starting at the centre of the coordinate
        system, \f[ \Sigma_R = \int_0^\infty \rho(R,0)\,{\text{d}}R. \f] For the torus geometry,
        \f[ \Sigma_R = A\, ( {\text{gln}}_p\, r_{\text{max}} -
        {\text{gln}}_p\, r_{\text{min}} ) \f] with \f${\text{gln}}_p\,x\f$ the generalized
        logarithm defined in SpecialFunctions::gln.*/
    double SigmaR() const;

    /** This function returns the Z-axis surface density, i.e. the integration of
        the density along the entire Z-axis, \f[ \Sigma_Z = \int_{-\infty}^\infty \rho(0,0,z)\,
        {\text{d}}z. \f] For the torus geometry this integral is simply zero (we exclude the special
        limiting case where \f$\Delta=\tfrac{\pi}{2}\f$). */
    double SigmaZ() const;

    //======================== Data Members ========================

private:
    // data members for which there are setters and getters
    double _p;
    double _q;
    double _Delta;
    double _rmin;
    double _rmax;
    bool _rani;
    double _rcut;

    // data members initialized during setup
    double _sinDelta;
    double _smin;
    double _sdiff;
    double _A;
};

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

#endif // TORUSGEOMETRY_HPP
<commit_msg>update description for 'reshape' boolean property in TorusGeometry<commit_after>/*//////////////////////////////////////////////////////////////////
////       SKIRT -- an advanced radiative transfer code         ////
////       © Astronomical Observatory, Ghent University         ////
///////////////////////////////////////////////////////////////// */

#ifndef TORUSGEOMETRY_HPP
#define TORUSGEOMETRY_HPP

#include "AxGeometry.hpp"

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

/** The TorusGeometry class is a subclass of the AxGeometry class and describes the geometry of an
    axisymmetric torus as assumed to be present in the centre of active galactic nuclei (AGN). This
    geometry is described by a radial power-law density with a finite opening angle; see Stalevski
    et al. (2012, MNRAS, 420, 2756–2772) and Granato & Danese (1994, MNRAS, 268, 235). In formula,
    it is most easily expressed in spherical coordinates as \f[ \rho(r,\theta) = A\,
    r^{-p}\,{\text{e}}^{-q|\cos\theta|} \quad\text{for } r_{\text{min}}<r<r_{\text{max}} \text{ and
    } \frac{\pi}{2}-\Delta<\theta<\frac{\pi}{2} +\Delta. \f] There are five free parameters
    describing this dust geometry: the inner and outer radii \f$r_{\text{min}}\f$ and
    \f$r_{\text{max}}\f$ of the torus, the radial power law index \f$p\f$, the polar index \f$q\f$
    and the angle \f$\Delta\f$ describing the opening angle of the torus.

    If the dusty system under consideration is in the vicinity of an AGN central engine or another
    source which is luminous enough to heat the dust up to sublimation temperature, the inner
    radius should correspond to sublimation radius and scale as \f$ r_{\text{min}} \propto
    L(\theta)^{0.5}\f$ (Barvainis, 1987, ApJ, 320, 537, eq (5)). If the primary source assumes
    anisotropic emission, the inner radius must follow the same dependence as the distribution of
    the primary source luminosity. Otherwise, dust temperature on the inner boundary of geometry is
    very likely to be under- or over-estimated. Thus, if the NetzerAccretionDiskGeometry
    distribution is chosen to describe primary source emission, it is recommended to turn on the
    anisotropic inner radius option for the torus geometry. The inner radius will then be set by
    the following formula: \f[ r_{\text{min}} \propto (\cos\theta\,(2\cos\theta+1))^{0.5}.\f] This
    should allow dust to approach all the way to the primary central source in the equatorial
    plane. However, due to the finite resolution of dust cells, it may happen that some of the
    innermost cells end up with unphysically high temperatures. For this reason, there is an
    additional input parameter, the cutoff radius \f$r_{\text{cut}}\f$. The value of the cutoff
    radius is usually found after a few trial-and-error experiments by inspecting temperature
    distribution maps, until the inner wall of the geometry is at the expected sublimation
    temperature for a given dust population.

    The total dust mass of the model corresponds to the mass of the original geometry, before the
    inner wall is reshaped to account for anisotropy; the difference is usually rather small. */
class TorusGeometry : public AxGeometry
{
    Q_OBJECT
    Q_CLASSINFO("Title", "a torus geometry")

    Q_CLASSINFO("Property", "expon")
    Q_CLASSINFO("Title", "the radial powerlaw exponent p of the torus")
    Q_CLASSINFO("MinValue", "0")

    Q_CLASSINFO("Property", "index")
    Q_CLASSINFO("Title", "the polar index q of the torus")
    Q_CLASSINFO("MinValue", "0")

    Q_CLASSINFO("Property", "openAngle")
    Q_CLASSINFO("Title", "the half opening angle of the torus")
    Q_CLASSINFO("Quantity", "posangle")
    Q_CLASSINFO("MinValue", "0 deg")
    Q_CLASSINFO("MaxValue", "90 deg")

    Q_CLASSINFO("Property", "minRadius")
    Q_CLASSINFO("Title", "the minimum radius of the torus")
    Q_CLASSINFO("Quantity", "length")
    Q_CLASSINFO("MinValue", "0")

    Q_CLASSINFO("Property", "maxRadius")
    Q_CLASSINFO("Title", "the maximum radius of the torus")
    Q_CLASSINFO("Quantity", "length")
    Q_CLASSINFO("MinValue", "0")

    Q_CLASSINFO("Property", "anisoRadius")
    Q_CLASSINFO("Title", "reshape the inner radius according to the Netzer luminosity profile")
    Q_CLASSINFO("Default", "no")

    Q_CLASSINFO("Property", "cutRadius")
    Q_CLASSINFO("Title", "the inner cutoff radius of the torus")
    Q_CLASSINFO("Quantity", "length")
    Q_CLASSINFO("MinValue", "0")
    Q_CLASSINFO("Default", "0")
    Q_CLASSINFO("RelevantIf", "anisoRadius")

    //============= Construction - Setup - Destruction =============

public:
    /** The default constructor. */
    Q_INVOKABLE TorusGeometry();

protected:
    /** This function verifies the validity of the geometry parameters. The normalization
        parameter \f$A\f$ is set by the normalization
        condition that total mass equals one, i.e. \f[ 1 = 2\pi\, A\,
        \int_{\pi/2-\Delta}^{\pi/2+\Delta} e^{-q|\cos\theta|}\sin\theta\, {\text{d}}\theta
        \int_{r_{\text{min}}}^{r_{\text{max}}} r^{2-p}\, {\text{d}}r. \f] This results in \f[ A =
        \frac{q}{4\pi\, (1-{\text{e}}^{-q\sin\Delta})}\, \frac{1}{ {\text{gln}}_{p-2}\,
        r_{\text{max}} - {\text{gln}}_{p-2}\, r_{\text{min}} }, \f] with \f${\text{gln}}_p\, x\f$
        the generalized logarithm defined in SpecialFunctions::gln. If \f$q=0\f$, this expression
        reduces to \f[ A = \frac{1}{4\pi\,\sin\Delta\, ({\text{gln}}_{p-2}\,
        r_{\text{max}} - {\text{gln}}_{p-2}\, r_{\text{min}} )}. \f] */
    void setupSelfBefore();

    //======== Setters & Getters for Discoverable Attributes =======

public:
    /** Sets the radial power law exponent \f$p\f$ of the torus. */
    Q_INVOKABLE void setExpon(double value);

    /** Returns the radial power law exponent \f$p\f$ of the torus. */
    Q_INVOKABLE double expon() const;

    /** Sets the polar index \f$q\f$ of the torus. */
    Q_INVOKABLE void setIndex(double value);

    /** Returns the polar index \f$q\f$ of the torus. */
    Q_INVOKABLE double index() const;

    /** Sets the half opening angle of the torus. */
    Q_INVOKABLE void setOpenAngle(double value);

    /** Returns the half opening angle of the torus. */
    Q_INVOKABLE double openAngle() const;

    /** Sets the minimum radius of the torus. */
    Q_INVOKABLE void setMinRadius(double value);

    /** Returns the minimum radius of the torus. */
    Q_INVOKABLE double minRadius() const;

    /** Sets the maximum radius of the torus. */
    Q_INVOKABLE void setMaxRadius(double value);

    /** Returns the maximum radius of the torus. */
    Q_INVOKABLE double maxRadius() const;

    /** Sets the flag indicating whether to reshape the inner wall of the torus according to the
        Netzer luminosity profile. */
    Q_INVOKABLE void setAnisoRadius(bool value);

    /** Returns the flag indicating whether to reshape the inner wall of the torus according to the
        Netzer luminosity profile. */
    Q_INVOKABLE bool anisoRadius() const;

    /** Sets the inner cutoff radius of the torus. */
    Q_INVOKABLE void setCutRadius(double value);

    /** Returns the inner cutoff radius of the torus. */
    Q_INVOKABLE double cutRadius() const;

    //======================== Other Functions =======================

public:
    /** This function returns the density \f$\rho(R,z)\f$ at the cylindrical radius
        \f$R\f$ and height \f$z\f$. It just implements the analytical formula. */
    double density(double R, double z) const;

    /** This function generates a random position from the torus geometry, by
        drawing a random point from the three-dimensional probability density \f$p({\bf{r}})\,
        {\text{d}}{\bf{r}} = \rho({\bf{r}})\, {\text{d}}{\bf{r}}\f$. For the torus geometry,
        the density is a separable function of \f$r\f$ and \f$\theta\f$, so that a random
        position can hence be constructed by combining random spherical coordinates, each chosen
        from their own probability distributions. A random azimuth \f$\phi\f$ is readily found by
        chosing a random deviate \f${\cal{X}}\f$ and setting \f$ \phi = 2\pi {\cal{X}} \f$.

        For the
        radial coordinate, the appropriate probability distribution is \f$ p(r)\,{\text{d}}r \propto
        r^{p-2}\,{\text{d}}r \f$. A random radius is generated by picking a new uniform deviate
        \f${\cal{X}}\f$, and solving the equation \f[ {\cal{X}} = \int_0^r p(r')\, {\text{d}}r'
        \f] for \f$r\f$. We obtain \f[ {\cal{X}} =
        \frac{ {\text{gln}}_{p-2}\, r - {\text{gln}}_{p-2}\, r_{\text{min}} }{
        {\text{gln}}_{p-2}\, r_{\text{max}} - {\text{gln}}_{p-2}\,
        r_{\text{min}} }. \f] Inverting this equation results in \f[ r =
        {\text{gexp}}_{p-2} \Big[ {\text{gln}}_{p-2}\, r_{\text{min}} + X (
        {\text{gln}}_{p-2}\, r_{\text{max}} - {\text{gln}}_{p-2}\, r_{\text{min}} )
        \Bigr]. \f] In these expressions, \f${\text{gln}}_p\,x\f$ and \f${\text{gexp}}_p\,x\f$ are the
        generalized logarithm and exponential functions defined in
        SpecialFunctions::gln and SpecialFunctions::gexp respectively.

        Finally, for the polar angle, the appropriate distribution function is \f[ p(\theta)\,
        {\text{d}}\theta \propto e^{-q|\cos\theta|}\sin\theta\, {\text{d}}\theta. \f] A random polar
        angle is generated by picking a new uniform deviate \f${\cal{X}}\f$, and solving the equation
        \f[ {\cal{X}} = \int_0^\theta p(\theta')\, {\text{d}}\theta' \f] for \f$\theta\f$. We obtain
        after some calculation \f[ {\cal{X}} = \begin{cases} \; \dfrac12 \left( 1 -
        \dfrac{1-{\text{e}}^{-q\cos\theta}}{1-{\text{e}}^{-q\sin\Delta}} \right) & \quad\text{for }
        \frac{\pi}{2}-\Delta < \theta < \frac{\pi}{2} \\[1.2em]  \;\dfrac12 \left( 1 +
        \dfrac{1-{\text{e}}^{q\cos\theta}}{1-{\text{e}}^{-q\sin\Delta}} \right) & \quad\text{for }
        \frac{\pi}{2} < \theta < \frac{\pi}{2}+\Delta \end{cases} \f] Inverting this
        gives \f[ \cos\theta = \begin{cases}\; -\dfrac{1}{q} \ln\left[ 1-\left(1-
        {\text{e}}^{-q\sin\Delta}\right) (1-2{\cal{X}}) \right] &
        \quad\text{if $0<{\cal{X}}<\tfrac12$} \\[1.2em] \; \dfrac{1}{q} \ln\left[ 1-\left(1
        -{\text{e}}^{-q\sin\Delta}\right) (2{\cal{X}}-1) \right] & \quad\text{if $\tfrac12<{\cal{X}}<1$}
        \end{cases}. \f] */
    Position generatePosition() const;

    /** This function returns the radial surface density, i.e. the integration of
        the density along a line in the equatorial plane starting at the centre of the coordinate
        system, \f[ \Sigma_R = \int_0^\infty \rho(R,0)\,{\text{d}}R. \f] For the torus geometry,
        \f[ \Sigma_R = A\, ( {\text{gln}}_p\, r_{\text{max}} -
        {\text{gln}}_p\, r_{\text{min}} ) \f] with \f${\text{gln}}_p\,x\f$ the generalized
        logarithm defined in SpecialFunctions::gln.*/
    double SigmaR() const;

    /** This function returns the Z-axis surface density, i.e. the integration of
        the density along the entire Z-axis, \f[ \Sigma_Z = \int_{-\infty}^\infty \rho(0,0,z)\,
        {\text{d}}z. \f] For the torus geometry this integral is simply zero (we exclude the special
        limiting case where \f$\Delta=\tfrac{\pi}{2}\f$). */
    double SigmaZ() const;

    //======================== Data Members ========================

private:
    // data members for which there are setters and getters
    double _p;
    double _q;
    double _Delta;
    double _rmin;
    double _rmax;
    bool _rani;
    double _rcut;

    // data members initialized during setup
    double _sinDelta;
    double _smin;
    double _sdiff;
    double _A;
};

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

#endif // TORUSGEOMETRY_HPP
<|endoftext|>


<commit_before>#pragma once
//=====================================================================//
/*!	@file
	@brief	遅延ユーティリティー
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2016, 2017 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=====================================================================//
#include <cstdint>

#ifndef F_ICLK
#  error "delay.hpp requires F_ICLK to be defined"
#endif

namespace utils {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief  ソフトウェアー待ち
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	struct delay {

		//-----------------------------------------------------------------//
		/*!
			@brief  ループ
			@param[in]	cnt	ループ回数
		*/
		//-----------------------------------------------------------------//
		static void loop(uint32_t cnt)
		{
			while(cnt > 0) {
				asm("nop");
				--cnt;
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  マイクロ秒単位の待ち @n
					あまり正確では無く、速度に関連する設定に依存します。
			@param[in]	us	待ち時間（マイクロ秒）
		*/
		//-----------------------------------------------------------------//
		static void micro_second(uint32_t us)
		{
			while(us > 0) {
#if defined(SIG_RX64M) || defined(SIG_RX63T) || defined(SIG_RX71M) || defined(SIG_RX72M)
				for(uint32_t n = 0; n < (F_ICLK / 4285714); ++n) {
					asm("nop");
				}
#elif defined(SIG_RX65N)
				for(uint32_t n = 0; n < (F_ICLK / 4444444); ++n) {
					asm("nop");
				}
#elif defined(SIG_RX24T)
				for(uint32_t n = 0; n < (F_ICLK / 4444444); ++n) {
					asm("nop");
				}
#elif defined(SIG_RX66T)
				for(uint32_t n = 0; n < (F_ICLK / 3346666); ++n) {
					asm("nop");
				}
#else
#  error "delay.hpp requires tune dummy operations"
#endif
				--us;
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  ミリ秒単位の待ち
			@param[in]	ms	待ち時間（ミリ秒）
		*/
		//-----------------------------------------------------------------//
		static void milli_second(uint32_t ms)
		{
			for(uint32_t i = 0; i < ms; ++i) {
				micro_second(1000);
			}
		}
	};
}
<commit_msg>Update: RTOS delay<commit_after>#pragma once
//=====================================================================//
/*!	@file
	@brief	遅延ユーティリティー
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2016, 2017 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=====================================================================//
#include <cstdint>
#ifdef RTOS
#include "FreeRTOS.h"
#include "task.h"
#endif

#ifndef F_ICLK
#  error "delay.hpp requires F_ICLK to be defined"
#endif

namespace utils {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief  ソフトウェアー待ち
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	struct delay {

		//-----------------------------------------------------------------//
		/*!
			@brief  ループ
			@param[in]	cnt	ループ回数
		*/
		//-----------------------------------------------------------------//
		static void loop(uint32_t cnt)
		{
			while(cnt > 0) {
				asm("nop");
				--cnt;
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  マイクロ秒単位の待ち @n
					あまり正確では無く、速度に関連する設定に依存します。
			@param[in]	us	待ち時間（マイクロ秒）
		*/
		//-----------------------------------------------------------------//
		static void micro_second(uint32_t us)
		{
			while(us > 0) {
#if defined(SIG_RX64M) || defined(SIG_RX63T) || defined(SIG_RX71M) || defined(SIG_RX72M)
				for(uint32_t n = 0; n < (F_ICLK / 4285714); ++n) {
					asm("nop");
				}
#elif defined(SIG_RX65N)
				for(uint32_t n = 0; n < (F_ICLK / 4444444); ++n) {
					asm("nop");
				}
#elif defined(SIG_RX24T)
				for(uint32_t n = 0; n < (F_ICLK / 4444444); ++n) {
					asm("nop");
				}
#elif defined(SIG_RX66T)
				for(uint32_t n = 0; n < (F_ICLK / 3346666); ++n) {
					asm("nop");
				}
#else
#  error "delay.hpp requires tune dummy operations"
#endif
				--us;
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  ミリ秒単位の待ち
			@param[in]	ms	待ち時間（ミリ秒）
		*/
		//-----------------------------------------------------------------//
		static void milli_second(uint32_t ms)
		{
#ifdef RTOS
			vTaskDelay(ms / portTICK_PERIOD_MS);
#else
			for(uint32_t i = 0; i < ms; ++i) {
				micro_second(1000);
			}
#endif
		}
	};
}
<|endoftext|>


<commit_before>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  David Shah <david@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "timing.h"
#include <algorithm>
#include <unordered_map>
#include <utility>
#include "log.h"
#include "util.h"

NEXTPNR_NAMESPACE_BEGIN

typedef std::list<const PortRef *> PortRefList;

static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, delay_t slack, bool update, delay_t &min_slack,
                          PortRefList *current_path, PortRefList *crit_path);

// Follow a path, returning budget to annotate
static delay_t follow_user_port(Context *ctx, PortRef &user, int path_length, delay_t slack, bool update,
                                delay_t &min_slack, PortRefList *current_path, PortRefList *crit_path)
{
    delay_t value;
    if (ctx->getPortClock(user.cell, user.port) != IdString()) {
        // At the end of a timing path (arguably, should check setup time
        // here too)
        value = slack / path_length;
        if (slack < min_slack) {
            min_slack = slack;
            if (crit_path)
                *crit_path = *current_path;
        }
    } else {
        // Default to the path ending here, if no further paths found
        value = slack / path_length;
        // Follow outputs of the user
        for (auto port : user.cell->ports) {
            if (port.second.type == PORT_OUT) {
                DelayInfo comb_delay;
                // Look up delay through this path
                bool is_path = ctx->getCellDelay(user.cell, user.port, port.first, comb_delay);
                if (is_path) {
                    NetInfo *net = port.second.net;
                    if (net) {
                        delay_t path_budget = follow_net(ctx, net, path_length, slack - comb_delay, update, min_slack,
                                                         current_path, crit_path);
                        value = std::min(value, path_budget);
                    }
                }
            }
        }
    }
    return value;
}

static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, delay_t slack, bool update, delay_t &min_slack,
                          PortRefList *current_path, PortRefList *crit_path)
{
    delay_t net_budget = slack / (path_length + 1);
    for (unsigned i = 0; i < net->users.size(); ++i) {
        auto &usr = net->users[i];
        if (crit_path)
            current_path->push_back(&usr);
        // If budget override is less than existing budget, then do not increment path length
        int pl = path_length + 1;
        auto budget = ctx->getBudgetOverride(net, i, net_budget);
        if (budget < net_budget) {
            net_budget = budget;
            pl = std::max(1, path_length);
        }
        auto delay = ctx->getNetinfoRouteDelay(net, i);
        net_budget = std::min(
                net_budget, follow_user_port(ctx, usr, pl, slack - delay, update, min_slack, current_path, crit_path));
        if (update)
            usr.budget = std::min(usr.budget, delay + net_budget);
        if (crit_path)
            current_path->pop_back();
    }
    return net_budget;
}

static delay_t walk_paths(Context *ctx, bool update, PortRefList *crit_path)
{
    delay_t default_slack = delay_t(1.0e12 / ctx->target_freq);
    delay_t min_slack = default_slack;

    PortRefList current_path;

    // Go through all clocked drivers and distribute the available path
    //   slack evenly into the budget of every sink on the path ---
    //   record this value into the UpdateMap
    for (auto &cell : ctx->cells) {
        for (auto port : cell.second->ports) {
            if (port.second.type == PORT_OUT) {
                IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first);
                if (clock_domain != IdString()) {
                    delay_t slack = default_slack; // TODO: clock constraints
                    delay_t clkToQ;
                    if (ctx->getCellDelay(cell.second.get(), clock_domain, port.first, clkToQ))
                        slack -= clkToQ.maxDelay();
                    if (port.second.net)
                        follow_net(ctx, port.second.net, 0, slack, update, min_slack, &current_path, crit_path);
                }
            }
        }
    }

    return min_slack;
}

void assign_budget(Context *ctx, bool quiet)
{
    if (!quiet) {
        log_break();
        log_info("Annotating ports with timing budgets\n");
    }

    // Clear delays to a very high value first
    delay_t default_slack = delay_t(1.0e12 / ctx->target_freq);
    for (auto &net : ctx->nets) {
        for (auto &usr : net.second->users) {
            usr.budget = default_slack;
        }
    }

    delay_t min_slack = walk_paths(ctx, true, nullptr);

    if (!quiet || ctx->verbose) {
        for (auto &net : ctx->nets) {
            for (auto &user : net.second->users) {
                // Post-update check
                if (ctx->user_freq && user.budget < 0)
                    log_warning("port %s.%s, connected to net '%s', has negative "
                                "timing budget of %fns\n",
                                user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
                                ctx->getDelayNS(user.budget));
                else if (ctx->verbose)
                    log_info("port %s.%s, connected to net '%s', has "
                             "timing budget of %fns\n",
                             user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
                             ctx->getDelayNS(user.budget));
            }
        }
    }

    // If user has not specified a frequency, dynamically adjust the target
    //   frequency to be the current maximum
    if (!ctx->user_freq) {
        ctx->target_freq = 1e12 / (default_slack - 1.2 * min_slack);
        if (ctx->verbose)
            log_info("minimum slack for this assign = %d, target Fmax for next update = %.2f MHz\n", min_slack,
                     ctx->target_freq / 1e6);
    }

    if (!quiet)
        log_info("Checksum: 0x%08x\n", ctx->checksum());
}

delay_t timing_analysis(Context *ctx, bool print_fmax, bool print_path)
{
    delay_t default_slack = delay_t(1.0e12 / ctx->target_freq);
    PortRefList crit_path;
    delay_t min_slack = walk_paths(ctx, false, &crit_path);
    if (print_path) {
        delay_t total = 0;
        log_break();
        log_info("Critical path report:\n");
        log_info("curr total\n");
        auto &front = crit_path.front();
        auto &front_port = front->cell->ports.at(front->port);
        auto &front_driver = front_port.net->driver;
        auto last_port = ctx->getPortClock(front_driver.cell, front_driver.port);
        for (auto sink : crit_path) {
            auto sink_cell = sink->cell;
            auto &port = sink_cell->ports.at(sink->port);
            auto net = port.net;
            unsigned i = 0;
            for (auto &usr : net->users)
                if (&usr == sink)
                    break;
                else
                    ++i;
            auto &driver = net->driver;
            auto driver_cell = driver.cell;
            delay_t comb_delay;
            ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay);
            total += comb_delay;
            log_info("%4d %4d  Source %s.%s\n", comb_delay, total, driver_cell->name.c_str(ctx),
                     driver.port.c_str(ctx));
            delay_t net_delay = ctx->getNetinfoRouteDelay(net, i);
            total += net_delay;
            auto driver_loc = ctx->getBelLocation(driver_cell->bel);
            auto sink_loc = ctx->getBelLocation(sink_cell->bel);
            log_info("%4d %4d    Net %s budget %d (%d,%d) -> (%d,%d)\n", net_delay, total, net->name.c_str(ctx),
                     sink->budget, driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y);
            log_info("                Sink %s.%s\n", sink_cell->name.c_str(ctx), sink->port.c_str(ctx));
            last_port = sink->port;
        }
        log_break();
    }
    if (print_fmax)
        log_info("estimated Fmax = %.2f MHz\n", 1e6 / (default_slack - min_slack));
    return min_slack;
}

NEXTPNR_NAMESPACE_END
<commit_msg>Fix merge issues<commit_after>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  David Shah <david@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "timing.h"
#include <algorithm>
#include <unordered_map>
#include <utility>
#include "log.h"
#include "util.h"

NEXTPNR_NAMESPACE_BEGIN

typedef std::list<const PortRef *> PortRefList;

static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, delay_t slack, bool update, delay_t &min_slack,
                          PortRefList *current_path, PortRefList *crit_path);

// Follow a path, returning budget to annotate
static delay_t follow_user_port(Context *ctx, PortRef &user, int path_length, delay_t slack, bool update,
                                delay_t &min_slack, PortRefList *current_path, PortRefList *crit_path)
{
    delay_t value;
    if (ctx->getPortClock(user.cell, user.port) != IdString()) {
        // At the end of a timing path (arguably, should check setup time
        // here too)
        value = slack / path_length;
        if (slack < min_slack) {
            min_slack = slack;
            if (crit_path)
                *crit_path = *current_path;
        }
    } else {
        // Default to the path ending here, if no further paths found
        value = slack / path_length;
        // Follow outputs of the user
        for (auto port : user.cell->ports) {
            if (port.second.type == PORT_OUT) {
                DelayInfo comb_delay;
                // Look up delay through this path
                bool is_path = ctx->getCellDelay(user.cell, user.port, port.first, comb_delay);
                if (is_path) {
                    NetInfo *net = port.second.net;
                    if (net) {
                        delay_t path_budget = follow_net(ctx, net, path_length, slack - comb_delay.maxDelay(), update, min_slack,
                                                         current_path, crit_path);
                        value = std::min(value, path_budget);
                    }
                }
            }
        }
    }
    return value;
}

static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, delay_t slack, bool update, delay_t &min_slack,
                          PortRefList *current_path, PortRefList *crit_path)
{
    delay_t net_budget = slack / (path_length + 1);
    for (unsigned i = 0; i < net->users.size(); ++i) {
        auto &usr = net->users[i];
        if (crit_path)
            current_path->push_back(&usr);
        // If budget override is less than existing budget, then do not increment path length
        int pl = path_length + 1;
        auto budget = ctx->getBudgetOverride(net, i, net_budget);
        if (budget < net_budget) {
            net_budget = budget;
            pl = std::max(1, path_length);
        }
        auto delay = ctx->getNetinfoRouteDelay(net, i);
        net_budget = std::min(
                net_budget, follow_user_port(ctx, usr, pl, slack - delay, update, min_slack, current_path, crit_path));
        if (update)
            usr.budget = std::min(usr.budget, delay + net_budget);
        if (crit_path)
            current_path->pop_back();
    }
    return net_budget;
}

static delay_t walk_paths(Context *ctx, bool update, PortRefList *crit_path)
{
    delay_t default_slack = delay_t(1.0e12 / ctx->target_freq);
    delay_t min_slack = default_slack;

    PortRefList current_path;

    // Go through all clocked drivers and distribute the available path
    //   slack evenly into the budget of every sink on the path ---
    //   record this value into the UpdateMap
    for (auto &cell : ctx->cells) {
        for (auto port : cell.second->ports) {
            if (port.second.type == PORT_OUT) {
                IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first);
                if (clock_domain != IdString()) {
                    delay_t slack = default_slack; // TODO: clock constraints
                    DelayInfo clkToQ;
                    if (ctx->getCellDelay(cell.second.get(), clock_domain, port.first, clkToQ))
                        slack -= clkToQ.maxDelay();
                    if (port.second.net)
                        follow_net(ctx, port.second.net, 0, slack, update, min_slack, &current_path, crit_path);
                }
            }
        }
    }

    return min_slack;
}

void assign_budget(Context *ctx, bool quiet)
{
    if (!quiet) {
        log_break();
        log_info("Annotating ports with timing budgets\n");
    }

    // Clear delays to a very high value first
    delay_t default_slack = delay_t(1.0e12 / ctx->target_freq);
    for (auto &net : ctx->nets) {
        for (auto &usr : net.second->users) {
            usr.budget = default_slack;
        }
    }

    delay_t min_slack = walk_paths(ctx, true, nullptr);

    if (!quiet || ctx->verbose) {
        for (auto &net : ctx->nets) {
            for (auto &user : net.second->users) {
                // Post-update check
                if (ctx->user_freq && user.budget < 0)
                    log_warning("port %s.%s, connected to net '%s', has negative "
                                "timing budget of %fns\n",
                                user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
                                ctx->getDelayNS(user.budget));
                else if (ctx->verbose)
                    log_info("port %s.%s, connected to net '%s', has "
                             "timing budget of %fns\n",
                             user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
                             ctx->getDelayNS(user.budget));
            }
        }
    }

    // If user has not specified a frequency, dynamically adjust the target
    //   frequency to be the current maximum
    if (!ctx->user_freq) {
        ctx->target_freq = 1e12 / (default_slack - 1.2 * min_slack);
        if (ctx->verbose)
            log_info("minimum slack for this assign = %d, target Fmax for next update = %.2f MHz\n", min_slack,
                     ctx->target_freq / 1e6);
    }

    if (!quiet)
        log_info("Checksum: 0x%08x\n", ctx->checksum());
}

delay_t timing_analysis(Context *ctx, bool print_fmax, bool print_path)
{
    delay_t default_slack = delay_t(1.0e12 / ctx->target_freq);
    PortRefList crit_path;
    delay_t min_slack = walk_paths(ctx, false, &crit_path);
    if (print_path) {
        delay_t total = 0;
        log_break();
        log_info("Critical path report:\n");
        log_info("curr total\n");
        auto &front = crit_path.front();
        auto &front_port = front->cell->ports.at(front->port);
        auto &front_driver = front_port.net->driver;
        auto last_port = ctx->getPortClock(front_driver.cell, front_driver.port);
        for (auto sink : crit_path) {
            auto sink_cell = sink->cell;
            auto &port = sink_cell->ports.at(sink->port);
            auto net = port.net;
            unsigned i = 0;
            for (auto &usr : net->users)
                if (&usr == sink)
                    break;
                else
                    ++i;
            auto &driver = net->driver;
            auto driver_cell = driver.cell;
            DelayInfo comb_delay;
            ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay);
            total += comb_delay.maxDelay();
            log_info("%4d %4d  Source %s.%s\n", comb_delay.maxDelay(), total, driver_cell->name.c_str(ctx),
                     driver.port.c_str(ctx));
            delay_t net_delay = ctx->getNetinfoRouteDelay(net, i);
            total += net_delay;
            auto driver_loc = ctx->getBelLocation(driver_cell->bel);
            auto sink_loc = ctx->getBelLocation(sink_cell->bel);
            log_info("%4d %4d    Net %s budget %d (%d,%d) -> (%d,%d)\n", net_delay, total, net->name.c_str(ctx),
                     sink->budget, driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y);
            log_info("                Sink %s.%s\n", sink_cell->name.c_str(ctx), sink->port.c_str(ctx));
            last_port = sink->port;
        }
        log_break();
    }
    if (print_fmax)
        log_info("estimated Fmax = %.2f MHz\n", 1e6 / (default_slack - min_slack));
    return min_slack;
}

NEXTPNR_NAMESPACE_END
<|endoftext|>