<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: formatpaintbrushctrl.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: obo $ $Date: 2006-10-12 13:20:40 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    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.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svx.hxx"

#include "formatpaintbrushctrl.hxx"

// header for class SfxBoolItem
#ifndef _SFXENUMITEM_HXX
#include <svtools/eitem.hxx>
#endif

// header for define SFX_APP
#ifndef _SFXAPP_HXX
#include <sfx2/app.hxx>
#endif

// header for class ToolBox
#ifndef _SV_TOOLBOX_HXX
#include <vcl/toolbox.hxx>
#endif

//.............................................................................
namespace svx
{
//.............................................................................

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;

SFX_IMPL_TOOLBOX_CONTROL( FormatPaintBrushToolBoxControl, SfxBoolItem );

FormatPaintBrushToolBoxControl::FormatPaintBrushToolBoxControl( USHORT nSlotId, USHORT nId, ToolBox& rTbx )
    : SfxToolBoxControl( nSlotId, nId, rTbx )
    , m_bPersistentCopy(false)
    , m_aDoubleClickTimer()
{
    ULONG nDblClkTime = rTbx.GetSettings().GetMouseSettings().GetDoubleClickTime();

    m_aDoubleClickTimer.SetTimeoutHdl( LINK(this, FormatPaintBrushToolBoxControl, WaitDoubleClickHdl) );
    m_aDoubleClickTimer.SetTimeout(nDblClkTime);
}

// -----------------------------------------------------------------------

FormatPaintBrushToolBoxControl::~FormatPaintBrushToolBoxControl()
{
    m_aDoubleClickTimer.Stop();
}

// -----------------------------------------------------------------------
void FormatPaintBrushToolBoxControl::impl_executePaintBrush()
{
    Sequence< PropertyValue > aArgs( 1 );
    aArgs[0].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PersistentCopy" ));
    aArgs[0].Value = makeAny( static_cast<sal_Bool>(m_bPersistentCopy) );
    Dispatch( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ".uno:FormatPaintbrush" ))
        , aArgs );
}

// -----------------------------------------------------------------------
void FormatPaintBrushToolBoxControl::DoubleClick()
{
    m_aDoubleClickTimer.Stop();

    m_bPersistentCopy = true;
    this->impl_executePaintBrush();
}

// -----------------------------------------------------------------------
void FormatPaintBrushToolBoxControl::Click()
{
    m_bPersistentCopy = false;
    m_aDoubleClickTimer.Start();
}

// -----------------------------------------------------------------------
IMPL_LINK(FormatPaintBrushToolBoxControl, WaitDoubleClickHdl, void*, EMPTYARG )
{
    //there was no second click during waiting
    this->impl_executePaintBrush();
    return 0;
}

// -----------------------------------------------------------------------
void FormatPaintBrushToolBoxControl::Select( BOOL )
{
}

// -----------------------------------------------------------------------
void FormatPaintBrushToolBoxControl::StateChanged( USHORT nSID, SfxItemState eState,
                const SfxPoolItem* pState )
{
    if( ( eState & SFX_ITEM_SET ) == 0 )
        m_bPersistentCopy = false;
    SfxToolBoxControl::StateChanged( nSID, eState, pState );
}

//.............................................................................
} //namespace svx
//.............................................................................
<commit_msg>INTEGRATION: CWS changefileheader (1.5.698); FILE MERGED 2008/04/01 15:51:47 thb 1.5.698.2: #i85898# Stripping all external header guards 2008/03/31 14:24:07 rt 1.5.698.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: formatpaintbrushctrl.cxx,v $
 * $Revision: 1.6 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svx.hxx"

#include "formatpaintbrushctrl.hxx"

// header for class SfxBoolItem
#include <svtools/eitem.hxx>

// header for define SFX_APP
#include <sfx2/app.hxx>

// header for class ToolBox
#include <vcl/toolbox.hxx>

//.............................................................................
namespace svx
{
//.............................................................................

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;

SFX_IMPL_TOOLBOX_CONTROL( FormatPaintBrushToolBoxControl, SfxBoolItem );

FormatPaintBrushToolBoxControl::FormatPaintBrushToolBoxControl( USHORT nSlotId, USHORT nId, ToolBox& rTbx )
    : SfxToolBoxControl( nSlotId, nId, rTbx )
    , m_bPersistentCopy(false)
    , m_aDoubleClickTimer()
{
    ULONG nDblClkTime = rTbx.GetSettings().GetMouseSettings().GetDoubleClickTime();

    m_aDoubleClickTimer.SetTimeoutHdl( LINK(this, FormatPaintBrushToolBoxControl, WaitDoubleClickHdl) );
    m_aDoubleClickTimer.SetTimeout(nDblClkTime);
}

// -----------------------------------------------------------------------

FormatPaintBrushToolBoxControl::~FormatPaintBrushToolBoxControl()
{
    m_aDoubleClickTimer.Stop();
}

// -----------------------------------------------------------------------
void FormatPaintBrushToolBoxControl::impl_executePaintBrush()
{
    Sequence< PropertyValue > aArgs( 1 );
    aArgs[0].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PersistentCopy" ));
    aArgs[0].Value = makeAny( static_cast<sal_Bool>(m_bPersistentCopy) );
    Dispatch( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ".uno:FormatPaintbrush" ))
        , aArgs );
}

// -----------------------------------------------------------------------
void FormatPaintBrushToolBoxControl::DoubleClick()
{
    m_aDoubleClickTimer.Stop();

    m_bPersistentCopy = true;
    this->impl_executePaintBrush();
}

// -----------------------------------------------------------------------
void FormatPaintBrushToolBoxControl::Click()
{
    m_bPersistentCopy = false;
    m_aDoubleClickTimer.Start();
}

// -----------------------------------------------------------------------
IMPL_LINK(FormatPaintBrushToolBoxControl, WaitDoubleClickHdl, void*, EMPTYARG )
{
    //there was no second click during waiting
    this->impl_executePaintBrush();
    return 0;
}

// -----------------------------------------------------------------------
void FormatPaintBrushToolBoxControl::Select( BOOL )
{
}

// -----------------------------------------------------------------------
void FormatPaintBrushToolBoxControl::StateChanged( USHORT nSID, SfxItemState eState,
                const SfxPoolItem* pState )
{
    if( ( eState & SFX_ITEM_SET ) == 0 )
        m_bPersistentCopy = false;
    SfxToolBoxControl::StateChanged( nSID, eState, pState );
}

//.............................................................................
} //namespace svx
//.............................................................................
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "commitdata.h"
#include "gitsubmiteditorwidget.h"
#include "githighlighters.h"
#include "logchangedialog.h"

#include <utils/completingtextedit.h>

#include <QRegExpValidator>
#include <QTextEdit>

#include <QDir>
#include <QGroupBox>
#include <QRegExp>
#include <QVBoxLayout>
#include <QMenu>

namespace Git {
namespace Internal {

// ------------------
GitSubmitEditorWidget::GitSubmitEditorWidget() :
    m_pushAction(NoPush),
    m_gitSubmitPanel(new QWidget),
    m_logChangeWidget(0),
    m_hasUnmerged(false),
    m_isInitialized(false)
{
    m_gitSubmitPanelUi.setupUi(m_gitSubmitPanel);
    new GitSubmitHighlighter(descriptionEdit());

    m_emailValidator = new QRegExpValidator(QRegExp(QLatin1String("[^@ ]+@[^@ ]+\\.[a-zA-Z]+")), this);

    connect(m_gitSubmitPanelUi.authorLineEdit, &QLineEdit::textChanged,
            this, &GitSubmitEditorWidget::authorInformationChanged);
    connect(m_gitSubmitPanelUi.emailLineEdit, &QLineEdit::textChanged,
            this, &GitSubmitEditorWidget::authorInformationChanged);
}

void GitSubmitEditorWidget::setPanelInfo(const GitSubmitEditorPanelInfo &info)
{
    m_gitSubmitPanelUi.repositoryLabel->setText(QDir::toNativeSeparators(info.repository));
    if (info.branch.contains(QLatin1String("(no branch)")))
        m_gitSubmitPanelUi.branchLabel->setText(QString::fromLatin1("%1")
                                                .arg(tr("Detached HEAD")));
    else
        m_gitSubmitPanelUi.branchLabel->setText(info.branch);
}

QString GitSubmitEditorWidget::amendSHA1() const
{
    return m_logChangeWidget ? m_logChangeWidget->commit() : QString();
}

void GitSubmitEditorWidget::setHasUnmerged(bool e)
{
    m_hasUnmerged = e;
}

void GitSubmitEditorWidget::initialize(CommitType commitType,
                                       const QString &repository,
                                       const GitSubmitEditorPanelData &data,
                                       const GitSubmitEditorPanelInfo &info,
                                       bool enablePush)
{
    if (m_isInitialized)
        return;
    m_isInitialized = true;
    if (commitType == FixupCommit) {
        auto logChangeGroupBox = new QGroupBox(tr("Select Change"));
        auto logChangeLayout = new QVBoxLayout;
        logChangeGroupBox->setLayout(logChangeLayout);
        m_logChangeWidget = new LogChangeWidget;
        m_logChangeWidget->init(repository);
        connect(m_logChangeWidget, &LogChangeWidget::activated,
                this, &GitSubmitEditorWidget::show);
        logChangeLayout->addWidget(m_logChangeWidget);
        insertTopWidget(logChangeGroupBox);
        m_gitSubmitPanelUi.editGroup->hide();
        hideDescription();
    }
    insertTopWidget(m_gitSubmitPanel);
    setPanelData(data);
    setPanelInfo(info);

    if (enablePush) {
        auto menu = new QMenu(this);
        menu->addAction(tr("&Commit only"), this, SLOT(commitOnlySlot()));
        menu->addAction(tr("Commit and &Push"), this, SLOT(commitAndPushSlot()));
        menu->addAction(tr("Commit and Push to &Gerrit"), this, SLOT(commitAndPushToGerritSlot()));
        addSubmitButtonMenu(menu);
    }
}

void GitSubmitEditorWidget::refreshLog(const QString &repository)
{
    if (m_logChangeWidget)
        m_logChangeWidget->init(repository);
}

GitSubmitEditorPanelData GitSubmitEditorWidget::panelData() const
{
    GitSubmitEditorPanelData rc;
    const QString author = m_gitSubmitPanelUi.authorLineEdit->text();
    const QString email = m_gitSubmitPanelUi.emailLineEdit->text();
    if (author != m_originalAuthor || email != m_originalEmail) {
        rc.author = author;
        rc.email = email;
    }
    rc.bypassHooks = m_gitSubmitPanelUi.bypassHooksCheckBox->isChecked();
    rc.pushAction = m_pushAction;
    return rc;
}

void GitSubmitEditorWidget::setPanelData(const GitSubmitEditorPanelData &data)
{
    m_originalAuthor = data.author;
    m_originalEmail = data.email;
    m_gitSubmitPanelUi.authorLineEdit->setText(data.author);
    m_gitSubmitPanelUi.emailLineEdit->setText(data.email);
    m_gitSubmitPanelUi.bypassHooksCheckBox->setChecked(data.bypassHooks);
    authorInformationChanged();
}

bool GitSubmitEditorWidget::canSubmit() const
{
    if (m_gitSubmitPanelUi.invalidAuthorLabel->isVisible()
        || m_gitSubmitPanelUi.invalidEmailLabel->isVisible()
        || m_hasUnmerged)
        return false;
    return SubmitEditorWidget::canSubmit();
}

QString GitSubmitEditorWidget::cleanupDescription(const QString &input) const
{
    // We need to manually purge out comment lines starting with
    // hash '#' since git does not do that when using -F.
    const QChar newLine = QLatin1Char('\n');
    const QChar hash = QLatin1Char('#');
    QString message = input;
    for (int pos = 0; pos < message.size(); ) {
        const int newLinePos = message.indexOf(newLine, pos);
        const int startOfNextLine = newLinePos == -1 ? message.size() : newLinePos + 1;
        if (message.at(pos) == hash)
            message.remove(pos, startOfNextLine - pos);
        else
            pos = startOfNextLine;
    }
    return message;

}

QString GitSubmitEditorWidget::commitName() const
{
    if (m_pushAction == NormalPush)
        return tr("&Commit and Push");
    else if (m_pushAction == PushToGerrit)
        return tr("&Commit and Push to Gerrit");

    return tr("&Commit");
}

void GitSubmitEditorWidget::authorInformationChanged()
{
    bool bothEmpty = m_gitSubmitPanelUi.authorLineEdit->text().isEmpty() &&
            m_gitSubmitPanelUi.emailLineEdit->text().isEmpty();

    m_gitSubmitPanelUi.invalidAuthorLabel->
            setVisible(m_gitSubmitPanelUi.authorLineEdit->text().isEmpty() && !bothEmpty);
    m_gitSubmitPanelUi.invalidEmailLabel->
            setVisible(!emailIsValid() && !bothEmpty);

    updateSubmitAction();
}

void GitSubmitEditorWidget::commitOnlySlot()
{
    m_pushAction = NoPush;
    updateSubmitAction();
}

void GitSubmitEditorWidget::commitAndPushSlot()
{
    m_pushAction = NormalPush;
    updateSubmitAction();
}

void GitSubmitEditorWidget::commitAndPushToGerritSlot()
{
    m_pushAction = PushToGerrit;
    updateSubmitAction();
}

bool GitSubmitEditorWidget::emailIsValid() const
{
    int pos = m_gitSubmitPanelUi.emailLineEdit->cursorPosition();
    QString text = m_gitSubmitPanelUi.emailLineEdit->text();
    return m_emailValidator->validate(text, pos) == QValidator::Acceptable;
}

} // namespace Internal
} // namespace Git
<commit_msg>Git: commit submit widget add tooltip to error label<commit_after>/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "commitdata.h"
#include "gitsubmiteditorwidget.h"
#include "githighlighters.h"
#include "logchangedialog.h"

#include <utils/completingtextedit.h>

#include <QRegExpValidator>
#include <QTextEdit>

#include <QDir>
#include <QGroupBox>
#include <QRegExp>
#include <QVBoxLayout>
#include <QMenu>

namespace Git {
namespace Internal {

// ------------------
GitSubmitEditorWidget::GitSubmitEditorWidget() :
    m_pushAction(NoPush),
    m_gitSubmitPanel(new QWidget),
    m_logChangeWidget(0),
    m_hasUnmerged(false),
    m_isInitialized(false)
{
    m_gitSubmitPanelUi.setupUi(m_gitSubmitPanel);
    new GitSubmitHighlighter(descriptionEdit());

    m_emailValidator = new QRegExpValidator(QRegExp(QLatin1String("[^@ ]+@[^@ ]+\\.[a-zA-Z]+")), this);
    m_gitSubmitPanelUi.invalidEmailLabel->setToolTip(tr("Provide a valid email to commit."));

    connect(m_gitSubmitPanelUi.authorLineEdit, &QLineEdit::textChanged,
            this, &GitSubmitEditorWidget::authorInformationChanged);
    connect(m_gitSubmitPanelUi.emailLineEdit, &QLineEdit::textChanged,
            this, &GitSubmitEditorWidget::authorInformationChanged);
}

void GitSubmitEditorWidget::setPanelInfo(const GitSubmitEditorPanelInfo &info)
{
    m_gitSubmitPanelUi.repositoryLabel->setText(QDir::toNativeSeparators(info.repository));
    if (info.branch.contains(QLatin1String("(no branch)")))
        m_gitSubmitPanelUi.branchLabel->setText(QString::fromLatin1("%1")
                                                .arg(tr("Detached HEAD")));
    else
        m_gitSubmitPanelUi.branchLabel->setText(info.branch);
}

QString GitSubmitEditorWidget::amendSHA1() const
{
    return m_logChangeWidget ? m_logChangeWidget->commit() : QString();
}

void GitSubmitEditorWidget::setHasUnmerged(bool e)
{
    m_hasUnmerged = e;
}

void GitSubmitEditorWidget::initialize(CommitType commitType,
                                       const QString &repository,
                                       const GitSubmitEditorPanelData &data,
                                       const GitSubmitEditorPanelInfo &info,
                                       bool enablePush)
{
    if (m_isInitialized)
        return;
    m_isInitialized = true;
    if (commitType == FixupCommit) {
        auto logChangeGroupBox = new QGroupBox(tr("Select Change"));
        auto logChangeLayout = new QVBoxLayout;
        logChangeGroupBox->setLayout(logChangeLayout);
        m_logChangeWidget = new LogChangeWidget;
        m_logChangeWidget->init(repository);
        connect(m_logChangeWidget, &LogChangeWidget::activated,
                this, &GitSubmitEditorWidget::show);
        logChangeLayout->addWidget(m_logChangeWidget);
        insertTopWidget(logChangeGroupBox);
        m_gitSubmitPanelUi.editGroup->hide();
        hideDescription();
    }
    insertTopWidget(m_gitSubmitPanel);
    setPanelData(data);
    setPanelInfo(info);

    if (enablePush) {
        auto menu = new QMenu(this);
        menu->addAction(tr("&Commit only"), this, SLOT(commitOnlySlot()));
        menu->addAction(tr("Commit and &Push"), this, SLOT(commitAndPushSlot()));
        menu->addAction(tr("Commit and Push to &Gerrit"), this, SLOT(commitAndPushToGerritSlot()));
        addSubmitButtonMenu(menu);
    }
}

void GitSubmitEditorWidget::refreshLog(const QString &repository)
{
    if (m_logChangeWidget)
        m_logChangeWidget->init(repository);
}

GitSubmitEditorPanelData GitSubmitEditorWidget::panelData() const
{
    GitSubmitEditorPanelData rc;
    const QString author = m_gitSubmitPanelUi.authorLineEdit->text();
    const QString email = m_gitSubmitPanelUi.emailLineEdit->text();
    if (author != m_originalAuthor || email != m_originalEmail) {
        rc.author = author;
        rc.email = email;
    }
    rc.bypassHooks = m_gitSubmitPanelUi.bypassHooksCheckBox->isChecked();
    rc.pushAction = m_pushAction;
    return rc;
}

void GitSubmitEditorWidget::setPanelData(const GitSubmitEditorPanelData &data)
{
    m_originalAuthor = data.author;
    m_originalEmail = data.email;
    m_gitSubmitPanelUi.authorLineEdit->setText(data.author);
    m_gitSubmitPanelUi.emailLineEdit->setText(data.email);
    m_gitSubmitPanelUi.bypassHooksCheckBox->setChecked(data.bypassHooks);
    authorInformationChanged();
}

bool GitSubmitEditorWidget::canSubmit() const
{
    if (m_gitSubmitPanelUi.invalidAuthorLabel->isVisible()
        || m_gitSubmitPanelUi.invalidEmailLabel->isVisible()
        || m_hasUnmerged)
        return false;
    return SubmitEditorWidget::canSubmit();
}

QString GitSubmitEditorWidget::cleanupDescription(const QString &input) const
{
    // We need to manually purge out comment lines starting with
    // hash '#' since git does not do that when using -F.
    const QChar newLine = QLatin1Char('\n');
    const QChar hash = QLatin1Char('#');
    QString message = input;
    for (int pos = 0; pos < message.size(); ) {
        const int newLinePos = message.indexOf(newLine, pos);
        const int startOfNextLine = newLinePos == -1 ? message.size() : newLinePos + 1;
        if (message.at(pos) == hash)
            message.remove(pos, startOfNextLine - pos);
        else
            pos = startOfNextLine;
    }
    return message;

}

QString GitSubmitEditorWidget::commitName() const
{
    if (m_pushAction == NormalPush)
        return tr("&Commit and Push");
    else if (m_pushAction == PushToGerrit)
        return tr("&Commit and Push to Gerrit");

    return tr("&Commit");
}

void GitSubmitEditorWidget::authorInformationChanged()
{
    bool bothEmpty = m_gitSubmitPanelUi.authorLineEdit->text().isEmpty() &&
            m_gitSubmitPanelUi.emailLineEdit->text().isEmpty();

    m_gitSubmitPanelUi.invalidAuthorLabel->
            setVisible(m_gitSubmitPanelUi.authorLineEdit->text().isEmpty() && !bothEmpty);
    m_gitSubmitPanelUi.invalidEmailLabel->
            setVisible(!emailIsValid() && !bothEmpty);

    updateSubmitAction();
}

void GitSubmitEditorWidget::commitOnlySlot()
{
    m_pushAction = NoPush;
    updateSubmitAction();
}

void GitSubmitEditorWidget::commitAndPushSlot()
{
    m_pushAction = NormalPush;
    updateSubmitAction();
}

void GitSubmitEditorWidget::commitAndPushToGerritSlot()
{
    m_pushAction = PushToGerrit;
    updateSubmitAction();
}

bool GitSubmitEditorWidget::emailIsValid() const
{
    int pos = m_gitSubmitPanelUi.emailLineEdit->cursorPosition();
    QString text = m_gitSubmitPanelUi.emailLineEdit->text();
    return m_emailValidator->validate(text, pos) == QValidator::Acceptable;
}

} // namespace Internal
} // namespace Git
<|endoftext|>


<commit_before>#include "CGEKComponentController.h"
#include <algorithm>
#include <ppl.h>

#include "GEKEngineCLSIDs.h"

BEGIN_INTERFACE_LIST(CGEKComponentController)
    INTERFACE_LIST_ENTRY_COM(IGEKComponent)
END_INTERFACE_LIST_UNKNOWN

REGISTER_CLASS(CGEKComponentController)

CGEKComponentController::CGEKComponentController(void)
{
}

CGEKComponentController::~CGEKComponentController(void)
{
}

STDMETHODIMP_(LPCWSTR) CGEKComponentController::GetName(void) const
{
    return L"controller";
};

STDMETHODIMP CGEKComponentController::AddComponent(const GEKENTITYID &nEntityID)
{
    m_aData[nEntityID] = DATA();
    return S_OK;
}

STDMETHODIMP CGEKComponentController::RemoveComponent(const GEKENTITYID &nEntityID)
{
    auto pIterator = m_aData.find(nEntityID);
    if (pIterator != m_aData.end())
    {
        m_aData.unsafe_erase(pIterator);
    }

    return S_OK;
}

STDMETHODIMP_(bool) CGEKComponentController::HasComponent(const GEKENTITYID &nEntityID) const
{
    return (m_aData.find(nEntityID) != m_aData.end());
}

STDMETHODIMP_(void) CGEKComponentController::ListProperties(const GEKENTITYID &nEntityID, std::function<void(LPCWSTR, const GEKVALUE &)> OnProperty) const
{
    auto pIterator = m_aData.find(nEntityID);
    if (pIterator != m_aData.end())
    {
        OnProperty(L"turn", (*pIterator).second.m_nTurn);
        OnProperty(L"tilt", (*pIterator).second.m_nTilt);
    }
}

STDMETHODIMP_(bool) CGEKComponentController::GetProperty(const GEKENTITYID &nEntityID, LPCWSTR pName, GEKVALUE &kValue) const
{
    bool bReturn = false;
    auto pIterator = m_aData.find(nEntityID);
    if (pIterator != m_aData.end())
    {
        if (wcscmp(pName, L"turn") == 0)
        {
            kValue = (*pIterator).second.m_nTurn;
            bReturn = true;
        }
        else if (wcscmp(pName, L"tilt") == 0)
        {
            kValue = (*pIterator).second.m_nTilt;
            bReturn = true;
        }
    }

    return bReturn;
}

STDMETHODIMP_(bool) CGEKComponentController::SetProperty(const GEKENTITYID &nEntityID, LPCWSTR pName, const GEKVALUE &kValue)
{
    bool bReturn = false;
    auto pIterator = m_aData.find(nEntityID);
    if (pIterator != m_aData.end())
    {
        if (wcscmp(pName, L"turn") == 0)
        {
            (*pIterator).second.m_nTurn = kValue.GetFloat();
            bReturn = true;
        }
        else if (wcscmp(pName, L"tilt") == 0)
        {
            (*pIterator).second.m_nTilt = kValue.GetFloat();
            bReturn = true;
        }
    }

    return bReturn;
}

BEGIN_INTERFACE_LIST(CGEKComponentSystemController)
    INTERFACE_LIST_ENTRY_COM(IGEKInputObserver)
    INTERFACE_LIST_ENTRY_COM(IGEKSceneObserver)
    INTERFACE_LIST_ENTRY_COM(IGEKComponentSystem)
END_INTERFACE_LIST_UNKNOWN

REGISTER_CLASS(CGEKComponentSystemController)

CGEKComponentSystemController::CGEKComponentSystemController(void)
{
}

CGEKComponentSystemController::~CGEKComponentSystemController(void)
{
}

STDMETHODIMP CGEKComponentSystemController::Initialize(void)
{
    GetContext()->AddCachedObserver(CLSID_GEKEngine, (IGEKInputObserver *)GetUnknown());
    return GetContext()->AddCachedObserver(CLSID_GEKPopulationManager, (IGEKSceneObserver *)GetUnknown());
};

STDMETHODIMP_(void) CGEKComponentSystemController::Destroy(void)
{
    GetContext()->RemoveCachedObserver(CLSID_GEKPopulationManager, (IGEKSceneObserver *)GetUnknown());
    GetContext()->RemoveCachedObserver(CLSID_GEKEngine, (IGEKInputObserver *)GetUnknown());
}

STDMETHODIMP_(void) CGEKComponentSystemController::OnAction(LPCWSTR pName, const GEKVALUE &kValue)
{
    IGEKSceneManager *pSceneManager = GetContext()->GetCachedClass<IGEKSceneManager>(CLSID_GEKPopulationManager);
    if (pSceneManager != nullptr)
    {
        pSceneManager->ListComponentsEntities({ L"transform", L"controller" }, [&](const GEKENTITYID &nEntityID)->void
        {
            m_aActions[nEntityID][pName] = kValue.GetFloat();
        });
    }
}

STDMETHODIMP_(void) CGEKComponentSystemController::OnPreUpdate(float nGameTime, float nFrameTime)
{
    IGEKSceneManager *pSceneManager = GetContext()->GetCachedClass<IGEKSceneManager>(CLSID_GEKPopulationManager);
    if (pSceneManager != nullptr)
    {
        for (auto pEntity : m_aActions)
        {
            GEKVALUE kPosition;
            GEKVALUE kRotation;
            pSceneManager->GetProperty(pEntity.first, L"transform", L"position", kPosition);
            pSceneManager->GetProperty(pEntity.first, L"transform", L"rotation", kRotation);

            GEKVALUE kTurn;
            GEKVALUE kTilt;
            pSceneManager->GetProperty(pEntity.first, L"controller", L"turn", kTurn);
            pSceneManager->GetProperty(pEntity.first, L"controller", L"tilt", kTilt);

            float nTurn = (kTurn.GetFloat() + pEntity.second[L"turn"] * 0.01f);
            float nTilt = (kTilt.GetFloat() + pEntity.second[L"tilt"] * 0.01f);
            pSceneManager->SetProperty(pEntity.first, L"controller", L"turn", nTurn);
            pSceneManager->SetProperty(pEntity.first, L"controller", L"tilt", nTilt);

            float4x4 nRotation(nTilt, nTurn, 0.0f);

            float3 nForce(0.0f, 0.0f, 0.0f);
            nForce += nRotation.rz * pEntity.second[L"forward"];
            nForce -= nRotation.rz * pEntity.second[L"backward"];
            nForce += nRotation.rx * pEntity.second[L"strafe_right"];
            nForce -= nRotation.rx * pEntity.second[L"strafe_left"];
            nForce += nRotation.ry * pEntity.second[L"rise"];
            nForce -= nRotation.ry * pEntity.second[L"fall"];
            nForce *= 10.0f;

            float3 nPosition = (kPosition.GetFloat3() + (nForce * nFrameTime));
            pSceneManager->SetProperty(pEntity.first, L"transform", L"position", nPosition);
            pSceneManager->SetProperty(pEntity.first, L"transform", L"rotation", quaternion(nRotation));
        }
    }

    m_aActions.clear();
}<commit_msg>Fixing view controller to separate turn and tilt<commit_after>#include "CGEKComponentController.h"
#include <algorithm>
#include <ppl.h>

#include "GEKEngineCLSIDs.h"

BEGIN_INTERFACE_LIST(CGEKComponentController)
    INTERFACE_LIST_ENTRY_COM(IGEKComponent)
END_INTERFACE_LIST_UNKNOWN

REGISTER_CLASS(CGEKComponentController)

CGEKComponentController::CGEKComponentController(void)
{
}

CGEKComponentController::~CGEKComponentController(void)
{
}

STDMETHODIMP_(LPCWSTR) CGEKComponentController::GetName(void) const
{
    return L"controller";
};

STDMETHODIMP CGEKComponentController::AddComponent(const GEKENTITYID &nEntityID)
{
    m_aData[nEntityID] = DATA();
    return S_OK;
}

STDMETHODIMP CGEKComponentController::RemoveComponent(const GEKENTITYID &nEntityID)
{
    auto pIterator = m_aData.find(nEntityID);
    if (pIterator != m_aData.end())
    {
        m_aData.unsafe_erase(pIterator);
    }

    return S_OK;
}

STDMETHODIMP_(bool) CGEKComponentController::HasComponent(const GEKENTITYID &nEntityID) const
{
    return (m_aData.find(nEntityID) != m_aData.end());
}

STDMETHODIMP_(void) CGEKComponentController::ListProperties(const GEKENTITYID &nEntityID, std::function<void(LPCWSTR, const GEKVALUE &)> OnProperty) const
{
    auto pIterator = m_aData.find(nEntityID);
    if (pIterator != m_aData.end())
    {
        OnProperty(L"turn", (*pIterator).second.m_nTurn);
        OnProperty(L"tilt", (*pIterator).second.m_nTilt);
    }
}

STDMETHODIMP_(bool) CGEKComponentController::GetProperty(const GEKENTITYID &nEntityID, LPCWSTR pName, GEKVALUE &kValue) const
{
    bool bReturn = false;
    auto pIterator = m_aData.find(nEntityID);
    if (pIterator != m_aData.end())
    {
        if (wcscmp(pName, L"turn") == 0)
        {
            kValue = (*pIterator).second.m_nTurn;
            bReturn = true;
        }
        else if (wcscmp(pName, L"tilt") == 0)
        {
            kValue = (*pIterator).second.m_nTilt;
            bReturn = true;
        }
    }

    return bReturn;
}

STDMETHODIMP_(bool) CGEKComponentController::SetProperty(const GEKENTITYID &nEntityID, LPCWSTR pName, const GEKVALUE &kValue)
{
    bool bReturn = false;
    auto pIterator = m_aData.find(nEntityID);
    if (pIterator != m_aData.end())
    {
        if (wcscmp(pName, L"turn") == 0)
        {
            (*pIterator).second.m_nTurn = kValue.GetFloat();
            bReturn = true;
        }
        else if (wcscmp(pName, L"tilt") == 0)
        {
            (*pIterator).second.m_nTilt = kValue.GetFloat();
            bReturn = true;
        }
    }

    return bReturn;
}

BEGIN_INTERFACE_LIST(CGEKComponentSystemController)
    INTERFACE_LIST_ENTRY_COM(IGEKInputObserver)
    INTERFACE_LIST_ENTRY_COM(IGEKSceneObserver)
    INTERFACE_LIST_ENTRY_COM(IGEKComponentSystem)
END_INTERFACE_LIST_UNKNOWN

REGISTER_CLASS(CGEKComponentSystemController)

CGEKComponentSystemController::CGEKComponentSystemController(void)
{
}

CGEKComponentSystemController::~CGEKComponentSystemController(void)
{
}

STDMETHODIMP CGEKComponentSystemController::Initialize(void)
{
    GetContext()->AddCachedObserver(CLSID_GEKEngine, (IGEKInputObserver *)GetUnknown());
    return GetContext()->AddCachedObserver(CLSID_GEKPopulationManager, (IGEKSceneObserver *)GetUnknown());
};

STDMETHODIMP_(void) CGEKComponentSystemController::Destroy(void)
{
    GetContext()->RemoveCachedObserver(CLSID_GEKPopulationManager, (IGEKSceneObserver *)GetUnknown());
    GetContext()->RemoveCachedObserver(CLSID_GEKEngine, (IGEKInputObserver *)GetUnknown());
}

STDMETHODIMP_(void) CGEKComponentSystemController::OnAction(LPCWSTR pName, const GEKVALUE &kValue)
{
    IGEKSceneManager *pSceneManager = GetContext()->GetCachedClass<IGEKSceneManager>(CLSID_GEKPopulationManager);
    if (pSceneManager != nullptr)
    {
        pSceneManager->ListComponentsEntities({ L"transform", L"controller" }, [&](const GEKENTITYID &nEntityID)->void
        {
            m_aActions[nEntityID][pName] = kValue.GetFloat();
        });
    }
}

STDMETHODIMP_(void) CGEKComponentSystemController::OnPreUpdate(float nGameTime, float nFrameTime)
{
    IGEKSceneManager *pSceneManager = GetContext()->GetCachedClass<IGEKSceneManager>(CLSID_GEKPopulationManager);
    if (pSceneManager != nullptr)
    {
        for (auto pEntity : m_aActions)
        {
            GEKVALUE kPosition;
            GEKVALUE kRotation;
            pSceneManager->GetProperty(pEntity.first, L"transform", L"position", kPosition);
            pSceneManager->GetProperty(pEntity.first, L"transform", L"rotation", kRotation);

            GEKVALUE kTurn;
            GEKVALUE kTilt;
            pSceneManager->GetProperty(pEntity.first, L"controller", L"turn", kTurn);
            pSceneManager->GetProperty(pEntity.first, L"controller", L"tilt", kTilt);

            float nTurn = (kTurn.GetFloat() + pEntity.second[L"turn"] * 0.01f);
            float nTilt = (kTilt.GetFloat() + pEntity.second[L"tilt"] * 0.01f);
            pSceneManager->SetProperty(pEntity.first, L"controller", L"turn", nTurn);
            pSceneManager->SetProperty(pEntity.first, L"controller", L"tilt", nTilt);

            float4x4 nRotation = float4x4(nTilt, 0.0f, 0.0f) * float4x4(0.0f, nTurn, 0.0f);

            float3 nForce(0.0f, 0.0f, 0.0f);
            nForce += nRotation.rz * pEntity.second[L"forward"];
            nForce -= nRotation.rz * pEntity.second[L"backward"];
            nForce += nRotation.rx * pEntity.second[L"strafe_right"];
            nForce -= nRotation.rx * pEntity.second[L"strafe_left"];
            nForce += nRotation.ry * pEntity.second[L"rise"];
            nForce -= nRotation.ry * pEntity.second[L"fall"];
            nForce *= 10.0f;

            float3 nPosition = (kPosition.GetFloat3() + (nForce * nFrameTime));
            pSceneManager->SetProperty(pEntity.first, L"transform", L"position", nPosition);
            pSceneManager->SetProperty(pEntity.first, L"transform", L"rotation", quaternion(nRotation));
        }
    }

    m_aActions.clear();
}<|endoftext|>


<commit_before>// Copyright 2013 Tanel Lebedev

#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include "./kopsik_api.h"
#include "./https_client.h"

#include "Poco/FileStream.h"
#include "Poco/File.h"

#define TESTDB "test.db"
#define ERRLEN 1024

namespace kopsik {

    class MockHTTPSClient : public HTTPSClient {
    public:
        MOCK_METHOD0(ListenToWebsocket, kopsik::error());
        MOCK_METHOD5(PostJSON, kopsik::error(
            std::string relative_url,
            std::string json,
            std::string basic_auth_username,
            std::string basic_auth_password,
            std::string *response_body));
        MOCK_METHOD4(GetJSON, kopsik::error(
            std::string relative_url,
            std::string basic_auth_username,
            std::string basic_auth_password,
            std::string *response_body));
    };

    TEST(KopsikApiTest, kopsik_context_init) {
        KopsikContext *ctx = kopsik_context_init();
        ASSERT_TRUE(ctx);
        kopsik_context_clear(ctx);
        ASSERT_FALSE(ctx->db);
        ASSERT_FALSE(ctx->current_user);
    }

    TEST(KopsikApiTest, kopsik_version) {
        int major = 0;
        int minor = 0;
        int patch = 0;
        kopsik_version(&major, &minor, &patch);
        ASSERT_TRUE(major || minor || patch);
    }

    TEST(KopsikApiTest, kopsik_set_proxy) {
        KopsikContext *ctx = kopsik_context_init();
        kopsik_set_proxy(ctx, "localhost", 8000, "johnsmith", "secret");
        ASSERT_TRUE(true);
        kopsik_context_clear(ctx);
    }

    TEST(KopsikApiTest, kopsik_set_db_path) {
        KopsikContext *ctx = kopsik_context_init();
        {
            Poco::File f(TESTDB);
            if (f.exists()) f.remove(false);
        }
        kopsik_set_db_path(ctx, TESTDB);
        kopsik_context_clear(ctx);
        Poco::File f(TESTDB);
        ASSERT_TRUE(f.exists());
    }

    TEST(KopsikApiTest, kopsik_set_log_path) {
        KopsikContext *ctx = kopsik_context_init();
        kopsik_set_log_path(ctx, "test.log");
        ASSERT_TRUE(true);
        kopsik_context_clear(ctx);
    }

    TEST(KopsikApiTest, kopsik_user_init) {
        KopsikUser *user = kopsik_user_init();
        ASSERT_TRUE(user);
        kopsik_user_clear(user);
    }

    TEST(KopsikApiTest, kopsik_set_api_token) {
        KopsikContext *ctx = kopsik_context_init();
        Poco::File f(TESTDB);
        if (f.exists()) f.remove(false);
        kopsik_set_db_path(ctx, TESTDB);
        char err[ERRLEN];
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_set_api_token(ctx, err, ERRLEN, "token"));
        const int kMaxStrLen = 10;
        char str[kMaxStrLen];
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_get_api_token(ctx, err, ERRLEN, str, kMaxStrLen));
        ASSERT_EQ("token", std::string(str));
        kopsik_context_clear(ctx);
    }

    TEST(KopsikApiTest, kopsik_lifecycle) {
        KopsikContext *ctx = kopsik_context_init();

        Poco::File f(TESTDB);
        if (f.exists()) f.remove(false);
        kopsik_set_db_path(ctx, TESTDB);

        kopsik::HTTPSClient *client =
            reinterpret_cast<HTTPSClient *>(ctx->https_client);
        delete client;
        MockHTTPSClient *mock_client = new MockHTTPSClient();
        ctx->https_client = mock_client;

        Poco::FileStream fis("testdata/me.json", std::ios::binary);
        std::stringstream ss;
        ss << fis.rdbuf();
        fis.close();
        std::string json = ss.str();

        // Login
        EXPECT_CALL(*mock_client, GetJSON(
            std::string("/api/v8/me?with_related_data=true"),
            std::string("foo@bar.com"),
            std::string("secret"),
            testing::_))
        .WillOnce(testing::DoAll(
            testing::SetArgPointee<3>(json),
            testing::Return("")));
        char err[ERRLEN];
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_login(
            ctx,
            err, ERRLEN,
            "foo@bar.com", "secret"));

        // We should have the API token now
        const int kMaxStrLen = 100;
        char str[kMaxStrLen];
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_get_api_token(ctx, err, ERRLEN, str, kMaxStrLen));
        ASSERT_EQ("30eb0ae954b536d2f6628f7fec47beb6", std::string(str));

        // We should have current user now
        KopsikUser *user = kopsik_user_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_current_user(ctx, err, ERRLEN, user));
        ASSERT_EQ((unsigned int)10471231, user->ID);
        ASSERT_EQ(std::string("John Smith"), std::string(user->Fullname));
        kopsik_user_clear(user);

        // Count time entry items before start. It should be 3, since
        // there are 3 time entries in the me.json file we're using:
        KopsikTimeEntryViewItemList *list =
            kopsik_time_entry_view_item_list_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_time_entry_view_items(
            ctx, err, ERRLEN, list));
        ASSERT_EQ((unsigned int)3, list->Length);
        int number_of_items = list->Length;
        kopsik_time_entry_view_item_list_clear(list);

        // Start tracking
        KopsikTimeEntryViewItem *item = kopsik_time_entry_view_item_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_start(ctx, err, ERRLEN, "Test", item));
        ASSERT_EQ(std::string("Test"), std::string(item->Description));
        kopsik_time_entry_view_item_clear(item);

        // We should now have a running time entry
        int is_tracking = 0;
        KopsikTimeEntryViewItem *running = kopsik_time_entry_view_item_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_running_time_entry_view_item(
            ctx, err, ERRLEN, running, &is_tracking));
        ASSERT_TRUE(is_tracking);
        ASSERT_GT(0, running->DurationInSeconds);
        ASSERT_EQ(std::string("Test"), std::string(running->Description));
        ASSERT_FALSE(running->Project);
        ASSERT_TRUE(running->Duration);
        ASSERT_FALSE(running->Color);
        ASSERT_TRUE(running->GUID);
        std::string GUID(running->GUID);
        kopsik_time_entry_view_item_clear(running);

        // The running time entry should *not* be listed
        // among time entry view items.
        list = kopsik_time_entry_view_item_list_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_time_entry_view_items(
            ctx, err, ERRLEN, list));
        ASSERT_TRUE(list);
        ASSERT_EQ((unsigned int)number_of_items + 0, list->Length);
        kopsik_time_entry_view_item_list_clear(list);

        // Stop the time entry
        KopsikTimeEntryViewItem *stopped = kopsik_time_entry_view_item_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_stop(ctx, err, ERRLEN, stopped));
        ASSERT_EQ(std::string("Test"), std::string(stopped->Description));
        kopsik_time_entry_view_item_clear(stopped);

        // Now the stopped time entry should be listed
        // among time entry view items.
        list = kopsik_time_entry_view_item_list_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_time_entry_view_items(
            ctx, err, ERRLEN, list));
        ASSERT_TRUE(list);
        ASSERT_EQ((unsigned int)number_of_items + 1, list->Length);
        kopsik_time_entry_view_item_list_clear(list);

        // We should no longer get a running time entry from API.
        is_tracking = 0;
        running = kopsik_time_entry_view_item_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_running_time_entry_view_item(
            ctx, err, ERRLEN, running, &is_tracking));
        ASSERT_FALSE(is_tracking);
        kopsik_time_entry_view_item_clear(running);

        // We started and stopped one time entry.
        // This means we should have one dirty model now.
        KopsikDirtyModels dirty_models;
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_dirty_models(
            ctx, err, ERRLEN, &dirty_models));
        ASSERT_EQ((unsigned int)1, dirty_models.TimeEntries);

        // Push changes
        std::stringstream response_body;
        response_body << "["
            << "{\"status\": 200, \"body\": \""
                << "{\"data\": {\"id\": 123456789, \"ui_modified_at\": "
                << time(0) << "}}\""
            << "}"
            << "]";
        std::string response_json = response_body.str();
        EXPECT_CALL(*mock_client, PostJSON(
            std::string("/api/v8/batch_updates"),
            testing::An<std::string>(),
            std::string("30eb0ae954b536d2f6628f7fec47beb6"),
            std::string("api_token"),
            testing::_))
        .WillOnce(testing::DoAll(
            testing::SetArgPointee<4>(response_json),
            testing::Return("")));

        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_push(
            ctx, err, ERRLEN));

        // Check that no dirty models are left.
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_dirty_models(
            ctx, err, ERRLEN, &dirty_models));
        ASSERT_EQ((unsigned int)0, dirty_models.TimeEntries);

        // Continue the time entry we created in the start.
        KopsikTimeEntryViewItem *continued =
            kopsik_time_entry_view_item_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_continue(
            ctx, err, ERRLEN, GUID.c_str(), continued));
        ASSERT_NE(std::string(GUID), std::string(continued->GUID));
        ASSERT_FALSE(std::string(continued->Duration).empty());
        ASSERT_GT(0, continued->DurationInSeconds);
        kopsik_time_entry_view_item_clear(continued);

        // We should now once again have a dirty model.
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_dirty_models(
            ctx, err, ERRLEN, &dirty_models));
        ASSERT_EQ((unsigned int)1, dirty_models.TimeEntries);

        /*

        // Sync, to get rid of it.
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_sync(ctx,
            err, ERRLEN, 1));

        // We should have not dirty models left.
         ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_dirty_models(
            ctx, err, ERRLEN, &dirty_models));
        ASSERT_EQ((unsigned int)0, dirty_models.TimeEntries);

        */

        // Log out
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_logout(ctx, err, ERRLEN));

        // Check that we have no API token after user logged out.
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_get_api_token(ctx, err, ERRLEN, str, kMaxStrLen));
        ASSERT_EQ("", std::string(str));

        kopsik_context_clear(ctx);
    }

    TEST(KopsikApiTest, kopsik_time_entry_view_item_init) {
        KopsikTimeEntryViewItem *te = kopsik_time_entry_view_item_init();
        ASSERT_TRUE(te);
        kopsik_time_entry_view_item_clear(te);
    }

    TEST(KopsikApiTest, kopsik_format_duration_in_seconds) {
        const int kMaxStrLen = 100;
        char str[kMaxStrLen];
        kopsik_format_duration_in_seconds(10, str, kMaxStrLen);
        ASSERT_EQ("10 sec", std::string(str));
        kopsik_format_duration_in_seconds(60, str, kMaxStrLen);
        ASSERT_EQ("01:00 min", std::string(str));
        kopsik_format_duration_in_seconds(65, str, kMaxStrLen);
        ASSERT_EQ("01:05 min", std::string(str));
        kopsik_format_duration_in_seconds(3600, str, kMaxStrLen);
        ASSERT_EQ("01:00:00", std::string(str));
        kopsik_format_duration_in_seconds(5400, str, kMaxStrLen);
        ASSERT_EQ("01:30:00", std::string(str));
        kopsik_format_duration_in_seconds(5410, str, kMaxStrLen);
        ASSERT_EQ("01:30:10", std::string(str));
    }

    TEST(KopsikApiTest, kopsik_time_entry_view_item_list_init) {
        KopsikTimeEntryViewItemList *list =
            kopsik_time_entry_view_item_list_init();
        ASSERT_TRUE(list);
        kopsik_time_entry_view_item_list_clear(list);
    }

}  // namespace kopsik
<commit_msg>Dont assert members of deleted object<commit_after>// Copyright 2013 Tanel Lebedev

#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include "./kopsik_api.h"
#include "./https_client.h"

#include "Poco/FileStream.h"
#include "Poco/File.h"

#define TESTDB "test.db"
#define ERRLEN 1024

namespace kopsik {

    class MockHTTPSClient : public HTTPSClient {
    public:
        MOCK_METHOD0(ListenToWebsocket, kopsik::error());
        MOCK_METHOD5(PostJSON, kopsik::error(
            std::string relative_url,
            std::string json,
            std::string basic_auth_username,
            std::string basic_auth_password,
            std::string *response_body));
        MOCK_METHOD4(GetJSON, kopsik::error(
            std::string relative_url,
            std::string basic_auth_username,
            std::string basic_auth_password,
            std::string *response_body));
    };

    TEST(KopsikApiTest, kopsik_context_init) {
        KopsikContext *ctx = kopsik_context_init();
        ASSERT_TRUE(ctx);
        kopsik_context_clear(ctx);
    }

    TEST(KopsikApiTest, kopsik_version) {
        int major = 0;
        int minor = 0;
        int patch = 0;
        kopsik_version(&major, &minor, &patch);
        ASSERT_TRUE(major || minor || patch);
    }

    TEST(KopsikApiTest, kopsik_set_proxy) {
        KopsikContext *ctx = kopsik_context_init();
        kopsik_set_proxy(ctx, "localhost", 8000, "johnsmith", "secret");
        ASSERT_TRUE(true);
        kopsik_context_clear(ctx);
    }

    TEST(KopsikApiTest, kopsik_set_db_path) {
        KopsikContext *ctx = kopsik_context_init();
        {
            Poco::File f(TESTDB);
            if (f.exists()) f.remove(false);
        }
        kopsik_set_db_path(ctx, TESTDB);
        kopsik_context_clear(ctx);
        Poco::File f(TESTDB);
        ASSERT_TRUE(f.exists());
    }

    TEST(KopsikApiTest, kopsik_set_log_path) {
        KopsikContext *ctx = kopsik_context_init();
        kopsik_set_log_path(ctx, "test.log");
        ASSERT_TRUE(true);
        kopsik_context_clear(ctx);
    }

    TEST(KopsikApiTest, kopsik_user_init) {
        KopsikUser *user = kopsik_user_init();
        ASSERT_TRUE(user);
        kopsik_user_clear(user);
    }

    TEST(KopsikApiTest, kopsik_set_api_token) {
        KopsikContext *ctx = kopsik_context_init();
        Poco::File f(TESTDB);
        if (f.exists()) f.remove(false);
        kopsik_set_db_path(ctx, TESTDB);
        char err[ERRLEN];
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_set_api_token(ctx, err, ERRLEN, "token"));
        const int kMaxStrLen = 10;
        char str[kMaxStrLen];
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_get_api_token(ctx, err, ERRLEN, str, kMaxStrLen));
        ASSERT_EQ("token", std::string(str));
        kopsik_context_clear(ctx);
    }

    TEST(KopsikApiTest, kopsik_lifecycle) {
        KopsikContext *ctx = kopsik_context_init();

        Poco::File f(TESTDB);
        if (f.exists()) f.remove(false);
        kopsik_set_db_path(ctx, TESTDB);

        kopsik::HTTPSClient *client =
            reinterpret_cast<HTTPSClient *>(ctx->https_client);
        delete client;
        MockHTTPSClient *mock_client = new MockHTTPSClient();
        ctx->https_client = mock_client;

        Poco::FileStream fis("testdata/me.json", std::ios::binary);
        std::stringstream ss;
        ss << fis.rdbuf();
        fis.close();
        std::string json = ss.str();

        // Login
        EXPECT_CALL(*mock_client, GetJSON(
            std::string("/api/v8/me?with_related_data=true"),
            std::string("foo@bar.com"),
            std::string("secret"),
            testing::_))
        .WillOnce(testing::DoAll(
            testing::SetArgPointee<3>(json),
            testing::Return("")));
        char err[ERRLEN];
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_login(
            ctx,
            err, ERRLEN,
            "foo@bar.com", "secret"));

        // We should have the API token now
        const int kMaxStrLen = 100;
        char str[kMaxStrLen];
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_get_api_token(ctx, err, ERRLEN, str, kMaxStrLen));
        ASSERT_EQ("30eb0ae954b536d2f6628f7fec47beb6", std::string(str));

        // We should have current user now
        KopsikUser *user = kopsik_user_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_current_user(ctx, err, ERRLEN, user));
        ASSERT_EQ((unsigned int)10471231, user->ID);
        ASSERT_EQ(std::string("John Smith"), std::string(user->Fullname));
        kopsik_user_clear(user);

        // Count time entry items before start. It should be 3, since
        // there are 3 time entries in the me.json file we're using:
        KopsikTimeEntryViewItemList *list =
            kopsik_time_entry_view_item_list_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_time_entry_view_items(
            ctx, err, ERRLEN, list));
        ASSERT_EQ((unsigned int)3, list->Length);
        int number_of_items = list->Length;
        kopsik_time_entry_view_item_list_clear(list);

        // Start tracking
        KopsikTimeEntryViewItem *item = kopsik_time_entry_view_item_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_start(ctx, err, ERRLEN, "Test", item));
        ASSERT_EQ(std::string("Test"), std::string(item->Description));
        kopsik_time_entry_view_item_clear(item);

        // We should now have a running time entry
        int is_tracking = 0;
        KopsikTimeEntryViewItem *running = kopsik_time_entry_view_item_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_running_time_entry_view_item(
            ctx, err, ERRLEN, running, &is_tracking));
        ASSERT_TRUE(is_tracking);
        ASSERT_GT(0, running->DurationInSeconds);
        ASSERT_EQ(std::string("Test"), std::string(running->Description));
        ASSERT_FALSE(running->Project);
        ASSERT_TRUE(running->Duration);
        ASSERT_FALSE(running->Color);
        ASSERT_TRUE(running->GUID);
        std::string GUID(running->GUID);
        kopsik_time_entry_view_item_clear(running);

        // The running time entry should *not* be listed
        // among time entry view items.
        list = kopsik_time_entry_view_item_list_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_time_entry_view_items(
            ctx, err, ERRLEN, list));
        ASSERT_TRUE(list);
        ASSERT_EQ((unsigned int)number_of_items + 0, list->Length);
        kopsik_time_entry_view_item_list_clear(list);

        // Stop the time entry
        KopsikTimeEntryViewItem *stopped = kopsik_time_entry_view_item_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_stop(ctx, err, ERRLEN, stopped));
        ASSERT_EQ(std::string("Test"), std::string(stopped->Description));
        kopsik_time_entry_view_item_clear(stopped);

        // Now the stopped time entry should be listed
        // among time entry view items.
        list = kopsik_time_entry_view_item_list_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_time_entry_view_items(
            ctx, err, ERRLEN, list));
        ASSERT_TRUE(list);
        ASSERT_EQ((unsigned int)number_of_items + 1, list->Length);
        kopsik_time_entry_view_item_list_clear(list);

        // We should no longer get a running time entry from API.
        is_tracking = 0;
        running = kopsik_time_entry_view_item_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_running_time_entry_view_item(
            ctx, err, ERRLEN, running, &is_tracking));
        ASSERT_FALSE(is_tracking);
        kopsik_time_entry_view_item_clear(running);

        // We started and stopped one time entry.
        // This means we should have one dirty model now.
        KopsikDirtyModels dirty_models;
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_dirty_models(
            ctx, err, ERRLEN, &dirty_models));
        ASSERT_EQ((unsigned int)1, dirty_models.TimeEntries);

        // Push changes
        std::stringstream response_body;
        response_body << "["
            << "{\"status\": 200, \"body\": \""
                << "{\"data\": {\"id\": 123456789, \"ui_modified_at\": "
                << time(0) << "}}\""
            << "}"
            << "]";
        std::string response_json = response_body.str();
        EXPECT_CALL(*mock_client, PostJSON(
            std::string("/api/v8/batch_updates"),
            testing::An<std::string>(),
            std::string("30eb0ae954b536d2f6628f7fec47beb6"),
            std::string("api_token"),
            testing::_))
        .WillOnce(testing::DoAll(
            testing::SetArgPointee<4>(response_json),
            testing::Return("")));

        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_push(
            ctx, err, ERRLEN));

        // Check that no dirty models are left.
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_dirty_models(
            ctx, err, ERRLEN, &dirty_models));
        ASSERT_EQ((unsigned int)0, dirty_models.TimeEntries);

        // Continue the time entry we created in the start.
        KopsikTimeEntryViewItem *continued =
            kopsik_time_entry_view_item_init();
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_continue(
            ctx, err, ERRLEN, GUID.c_str(), continued));
        ASSERT_NE(std::string(GUID), std::string(continued->GUID));
        ASSERT_FALSE(std::string(continued->Duration).empty());
        ASSERT_GT(0, continued->DurationInSeconds);
        kopsik_time_entry_view_item_clear(continued);

        // We should now once again have a dirty model.
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_dirty_models(
            ctx, err, ERRLEN, &dirty_models));
        ASSERT_EQ((unsigned int)1, dirty_models.TimeEntries);

        /*

        // Sync, to get rid of it.
        ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_sync(ctx,
            err, ERRLEN, 1));

        // We should have not dirty models left.
         ASSERT_EQ(KOPSIK_API_SUCCESS, kopsik_dirty_models(
            ctx, err, ERRLEN, &dirty_models));
        ASSERT_EQ((unsigned int)0, dirty_models.TimeEntries);

        */

        // Log out
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_logout(ctx, err, ERRLEN));

        // Check that we have no API token after user logged out.
        ASSERT_EQ(KOPSIK_API_SUCCESS,
            kopsik_get_api_token(ctx, err, ERRLEN, str, kMaxStrLen));
        ASSERT_EQ("", std::string(str));

        kopsik_context_clear(ctx);
    }

    TEST(KopsikApiTest, kopsik_time_entry_view_item_init) {
        KopsikTimeEntryViewItem *te = kopsik_time_entry_view_item_init();
        ASSERT_TRUE(te);
        kopsik_time_entry_view_item_clear(te);
    }

    TEST(KopsikApiTest, kopsik_format_duration_in_seconds) {
        const int kMaxStrLen = 100;
        char str[kMaxStrLen];
        kopsik_format_duration_in_seconds(10, str, kMaxStrLen);
        ASSERT_EQ("10 sec", std::string(str));
        kopsik_format_duration_in_seconds(60, str, kMaxStrLen);
        ASSERT_EQ("01:00 min", std::string(str));
        kopsik_format_duration_in_seconds(65, str, kMaxStrLen);
        ASSERT_EQ("01:05 min", std::string(str));
        kopsik_format_duration_in_seconds(3600, str, kMaxStrLen);
        ASSERT_EQ("01:00:00", std::string(str));
        kopsik_format_duration_in_seconds(5400, str, kMaxStrLen);
        ASSERT_EQ("01:30:00", std::string(str));
        kopsik_format_duration_in_seconds(5410, str, kMaxStrLen);
        ASSERT_EQ("01:30:10", std::string(str));
    }

    TEST(KopsikApiTest, kopsik_time_entry_view_item_list_init) {
        KopsikTimeEntryViewItemList *list =
            kopsik_time_entry_view_item_list_init();
        ASSERT_TRUE(list);
        kopsik_time_entry_view_item_list_clear(list);
    }

}  // namespace kopsik
<|endoftext|>


<commit_before>#include "thread_renderer.h"
#include "audio_nodes.h"
#include "recorder_node.h"

#include <cinder/app/App.h>

namespace cieq {

ThreadRenderer::ThreadRenderer(AudioNodes& nodes, std::size_t frames_per_surface, std::size_t fft_size)
	: mFramesPerSurface(frames_per_surface)
	, mFftSize(fft_size)
	, mAudioNodes(nodes)
{
	mNumSurfaces = mAudioNodes.getBufferRecorderNode()->getMaxPossiblePops() / mFramesPerSurface;
	if (mAudioNodes.getBufferRecorderNode()->getMaxPossiblePops() % mFramesPerSurface != 0)
		mNumSurfaces += 1;

	mSurfaceTexturePool.resize(mNumSurfaces);
}

void ThreadRenderer::update()
{
	for (container_pair& pair : mSurfaceTexturePool)
	{
		if (pair.first && pair.first->allRowsTouched())
		{
			pair.second = ci::gl::Texture::create(*pair.first);
			pair.first.reset();
		}
	}
}

void ThreadRenderer::draw()
{
	ci::gl::pushMatrices();
	ci::gl::clear(ci::Color::black());
	ci::gl::rotate(90.0f); //rotate scene 90 degrees

	// just a convenience
	auto _recorder_node = mAudioNodes.getBufferRecorderNode();
	// copy how many frames are currently visible
	const int _viewable_surfaces = getCurrentViewableSurfaces();
	// get current record position
	const int _current_write_pos = _recorder_node->getWritePosition();
	// percentage of the entire audio done recording
	const float _percentage_done = static_cast<float>(_current_write_pos) / _recorder_node->getNumFrames();
	// get the index of last active surface for drawing
	int _current_last_surface = static_cast<int>(_percentage_done * mNumSurfaces);
	// if we are at the end of samples, subtract one from last active surface index
	if (_percentage_done == 1.0f || !_recorder_node->canQuery())
	{
		_current_last_surface -= 1;
	}
	// number of samples that will be empty drawn (last surface)
	const float _static_offset = static_cast<float>(_recorder_node->getNumFrames() % mFramesPerSurface);
	// guest-imate where the work manager is at currently, do note that this is an estimate!
	const float _current_index_in_surface = std::fmodf(_percentage_done * mNumSurfaces * mFramesPerSurface, static_cast<float>(mFramesPerSurface));
	// shift to left for OpenGL (we're moving textures upside-down, therefore we shift one entire surface to right + estimate index in surface + static offset)
	const float _shift_right = static_cast<float>(_current_index_in_surface - mFramesPerSurface - _static_offset);
	ci::gl::translate(0.0f,  _shift_right - ci::app::getWindowWidth()); //after rotation, moving x is like moving y

	for (int index = _current_last_surface, count = 0; count <= _viewable_surfaces && index >= 0; --index, ++count)
	{
		ci::Rectf draw_rect(mFftSize, (count + 1) * mFramesPerSurface, 0, count * mFramesPerSurface);

		if (mSurfaceTexturePool[index].second)
		{
			// draw texture
			ci::gl::draw(mSurfaceTexturePool[index].second, draw_rect);
		}
		else if (mSurfaceTexturePool[index].first)
		{
			// draw surface
			ci::gl::draw(*mSurfaceTexturePool[index].first, draw_rect);
		}
	}

	ci::gl::popMatrices();
}

SpectralSurface& ThreadRenderer::getSurface(int index)
{
	// if surface does not exist
	if (!mSurfaceTexturePool[index].first)
	{
		// lock and construct the surface
		std::lock_guard<std::mutex> _lock(mPoolLock);
		if (!mSurfaceTexturePool[index].first) //double check
		{
			mSurfaceTexturePool[index].first = std::make_unique<SpectralSurface>(mFftSize, getFramesPerSurface());
		}
	}

	return *(mSurfaceTexturePool[index].first);
}

std::size_t ThreadRenderer::getFramesPerSurface() const
{
	return mFramesPerSurface;
}

std::size_t ThreadRenderer::getSurfaceIndexByQueryPos(std::size_t pos) const
{
	const auto pop_index = mAudioNodes.getBufferRecorderNode()->getQueryIndexByQueryPos(pos);
	return pop_index / getFramesPerSurface();
}

std::size_t ThreadRenderer::getIndexInSurfaceByQueryPos(std::size_t pos) const
{
	const auto pop_index = mAudioNodes.getBufferRecorderNode()->getQueryIndexByQueryPos(pos);
	return pop_index % getFramesPerSurface();
}

std::size_t ThreadRenderer::getCurrentViewableSurfaces() const
{
	return (ci::app::getWindowWidth() / mFramesPerSurface) + ((ci::app::getWindowWidth() % mFramesPerSurface == 0) ? 0 : 1);
}

} //!cieq<commit_msg>applying scaling to the frequency range<commit_after>#include "thread_renderer.h"
#include "audio_nodes.h"
#include "recorder_node.h"

#include <cinder/app/App.h>

namespace cieq {

ThreadRenderer::ThreadRenderer(AudioNodes& nodes, std::size_t frames_per_surface, std::size_t fft_size)
	: mFramesPerSurface(frames_per_surface)
	, mFftSize(fft_size)
	, mAudioNodes(nodes)
{
	mNumSurfaces = mAudioNodes.getBufferRecorderNode()->getMaxPossiblePops() / mFramesPerSurface;
	if (mAudioNodes.getBufferRecorderNode()->getMaxPossiblePops() % mFramesPerSurface != 0)
		mNumSurfaces += 1;

	mSurfaceTexturePool.resize(mNumSurfaces);
}

void ThreadRenderer::update()
{
	for (container_pair& pair : mSurfaceTexturePool)
	{
		if (pair.first && pair.first->allRowsTouched())
		{
			pair.second = ci::gl::Texture::create(*pair.first);
			pair.first.reset();
		}
	}
}

void ThreadRenderer::draw()
{
	ci::gl::pushMatrices();
	ci::gl::clear(ci::Color::black());
	ci::gl::rotate(90.0f); //rotate scene 90 degrees

	const float _x_scale = 3.0f;
	ci::gl::scale(_x_scale, 1.0f);
	// just a convenience
	auto _recorder_node = mAudioNodes.getBufferRecorderNode();
	// copy how many frames are currently visible
	const int _viewable_surfaces = getCurrentViewableSurfaces();
	// get current record position
	const int _current_write_pos = _recorder_node->getWritePosition();
	// percentage of the entire audio done recording
	const float _percentage_done = static_cast<float>(_current_write_pos) / _recorder_node->getNumFrames();
	// get the index of last active surface for drawing
	int _current_last_surface = static_cast<int>(_percentage_done * mNumSurfaces);
	// if we are at the end of samples, subtract one from last active surface index
	if (_percentage_done == 1.0f || !_recorder_node->canQuery())
	{
		_current_last_surface -= 1;
	}
	// number of samples that will be empty drawn (last surface)
	const float _static_offset = static_cast<float>(_recorder_node->getNumFrames() % mFramesPerSurface);
	// guest-imate where the work manager is at currently, do note that this is an estimate!
	const float _current_index_in_surface = std::fmodf(_percentage_done * mNumSurfaces * mFramesPerSurface, static_cast<float>(mFramesPerSurface));
	// shift to left for OpenGL (we're moving textures upside-down, therefore we shift one entire surface to right + estimate index in surface + static offset)
	const float _shift_right = static_cast<float>(_current_index_in_surface - mFramesPerSurface - _static_offset);
	const float _shift_up = (_x_scale - 1.0f) * ci::app::getWindowHeight();
	ci::gl::translate(-_shift_up / _x_scale, _shift_right - ci::app::getWindowWidth()); //after rotation, moving x is like moving y

	for (int index = _current_last_surface, count = 0; count <= _viewable_surfaces && index >= 0; --index, ++count)
	{
		ci::Rectf draw_rect(mFftSize, (count + 1) * mFramesPerSurface, 0, count * mFramesPerSurface);

		if (mSurfaceTexturePool[index].second)
		{
			// draw texture
			ci::gl::draw(mSurfaceTexturePool[index].second, draw_rect);
		}
		else if (mSurfaceTexturePool[index].first)
		{
			// draw surface
			ci::gl::draw(*mSurfaceTexturePool[index].first, draw_rect);
		}
	}

	ci::gl::popMatrices();
}

SpectralSurface& ThreadRenderer::getSurface(int index)
{
	// if surface does not exist
	if (!mSurfaceTexturePool[index].first)
	{
		// lock and construct the surface
		std::lock_guard<std::mutex> _lock(mPoolLock);
		if (!mSurfaceTexturePool[index].first) //double check
		{
			mSurfaceTexturePool[index].first = std::make_unique<SpectralSurface>(mFftSize, getFramesPerSurface());
		}
	}

	return *(mSurfaceTexturePool[index].first);
}

std::size_t ThreadRenderer::getFramesPerSurface() const
{
	return mFramesPerSurface;
}

std::size_t ThreadRenderer::getSurfaceIndexByQueryPos(std::size_t pos) const
{
	const auto pop_index = mAudioNodes.getBufferRecorderNode()->getQueryIndexByQueryPos(pos);
	return pop_index / getFramesPerSurface();
}

std::size_t ThreadRenderer::getIndexInSurfaceByQueryPos(std::size_t pos) const
{
	const auto pop_index = mAudioNodes.getBufferRecorderNode()->getQueryIndexByQueryPos(pos);
	return pop_index % getFramesPerSurface();
}

std::size_t ThreadRenderer::getCurrentViewableSurfaces() const
{
	return (ci::app::getWindowWidth() / mFramesPerSurface) + ((ci::app::getWindowWidth() % mFramesPerSurface == 0) ? 0 : 1);
}

} //!cieq<|endoftext|>


<commit_before>#include "core.h"
#include "mpicontroller.h"
#include "move.h"
#include "montecarlo.h"
#include "analysis.h"
#include "docopt.h"
#include "progress_tracker.h"
#include <cstdlib>
#include "spdlog/spdlog.h"
#include <spdlog/sinks/null_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <iomanip>
#include <unistd.h>

#ifdef ENABLE_SID
#include "cppsid.h"
#include <thread>
using namespace std::this_thread;     // sleep_for, sleep_until
using namespace std::chrono_literals; // ns, us, ms, s, h, etc.
using std::chrono::system_clock;
#endif

using namespace Faunus;
using namespace std;

#define Q(x) #x
#define QUOTE(x) Q(x)

#ifndef FAUNUS_TIPSFILE
#define FAUNUS_TIPSFILE ""
#endif

static const char USAGE[] =
    R"(Faunus - the Monte Carlo code you're looking for!

    http://github.com/mlund/faunus

    Usage:
      faunus [-q] [--verbosity <N>] [--nobar] [--nopfx] [--notips] [--nofun] [--state=<file>] [--input=<file>] [--output=<file>]
      faunus (-h | --help)
      faunus --version

    Options:
      -i <file> --input <file>   Input file [default: /dev/stdin].
      -o <file> --output <file>  Output file [default: out.json].
      -s <file> --state <file>   State file to start from (.json/.ubj).
      -v <N> --verbosity <N>     Log verbosity level (0 = off, 1 = critical, ..., 6 = trace) [default: 3]
      -q --quiet                 Less verbose output.
      -h --help                  Show this screen.
      --nobar                    No progress bar.
      --nopfx                    Do not prefix input file with MPI rank.
      --notips                   Do not give input assistance
      --nofun                    No fun
      --version                  Show version.

    Multiple processes using MPI:

    1. input and output files are prefixed with "mpi{rank}."
    2. standard output is redirected to "mpi{rank}.stdout"
    3. Input prefixing can be suppressed with --nopfx
)";

using ProgressIndicator::ProgressTracker;
std::shared_ptr<ProgressTracker> createProgressTracker(bool, unsigned int);

int main(int argc, char **argv) {
    using namespace Faunus::MPI;
    bool nofun;
    try {
        std::string version = "Faunus";
#ifdef GIT_LATEST_TAG
        version += " "s + QUOTE(GIT_LATEST_TAG);
#endif
#ifdef GIT_COMMIT_HASH
        version += " git " + std::string(GIT_COMMIT_HASH);
#endif
#ifdef ENABLE_SID
        version += " [sid]";
#endif
#ifdef ENABLE_MPI
        version += " [mpi]";
#endif
#ifdef _OPENMP
        version += " [openmp]";
#endif
        auto args = docopt::docopt(USAGE, {argv + 1, argv + argc}, true, version);

        mpi.init(); // initialize MPI, if available

        // prepare loggers
        // TODO refactor to a standalone function and use cmd line options for different sinks, etc.
        faunus_logger = spdlog::stderr_color_mt("faunus");
        faunus_logger->set_pattern("[%n %P] %^%L: %v%$");
        mcloop_logger = spdlog::stderr_color_mt("mcloop");
        mcloop_logger->set_pattern("[%n %P] [%E.%f] %L: %v");

        // --verbosity (log level)
        long log_level = spdlog::level::off - args["--verbosity"].asLong(); // reverse sequence 0 → 6 to 6 → 0
        spdlog::set_level(static_cast<spdlog::level::level_enum>(log_level));

        // --notips
        if (not args["--notips"].asBool())
            usageTip.load({FAUNUS_TIPSFILE});

        // --nobar
        bool show_progress = !args["--nobar"].asBool();

        // --quiet
        bool quiet = args["--quiet"].asBool();
        if (quiet) {
            cout.setstate(std::ios_base::failbit); // hold kæft
            show_progress = false;
        }

        // --nofun
        nofun = args["--nofun"].asBool();
        if (nofun or quiet)
            usageTip.asciiart = false;
#ifdef ENABLE_SID
        usageTip.asciiart = false; // if SID is enabled, disable ascii
#endif

        // --nopfx
        bool prefix = !args["--nopfx"].asBool();

        // --input
        json json_in;
        auto input = args["--input"].asString();
        if (input == "/dev/stdin")
            std::cin >> json_in;
        else {
            if (prefix)
                input = Faunus::MPI::prefix + input;
            json_in = openjson(input);
        }

        {
            pc::temperature = json_in.at("temperature").get<double>() * 1.0_K;
            MCSimulation sim(json_in, mpi);

            // --state
            if (args["--state"]) {
                std::ifstream f;
                std::string state = Faunus::MPI::prefix + args["--state"].asString();
                std::string suffix = state.substr(state.find_last_of(".") + 1);
                bool binary = (suffix == "ubj");
                auto mode = std::ios::in;
                if (binary)
                    mode = std::ifstream::ate | std::ios::binary; // ate = open at end
                f.open(state, mode);
                if (f) {
                    json json_state;
                    if (not quiet)
                        faunus_logger->info("loading state file {}", state);
                    if (binary) {
                        size_t size = f.tellg(); // get file size
                        std::vector<std::uint8_t> v(size / sizeof(std::uint8_t));
                        f.seekg(0, f.beg); // go back to start
                        f.read((char *)v.data(), size);
                        json_state = json::from_ubjson(v);
                    } else
                        f >> json_state;
                    sim.restore(json_state);
                } else
                    throw std::runtime_error("state file error: " + state);
            }

            Analysis::CombinedAnalysis analysis(json_in.at("analysis"), sim.space(), sim.pot());

            auto &loop = json_in.at("mcloop");
            int macro = loop.at("macro");
            int micro = loop.at("micro");

            auto progress_tracker = createProgressTracker(show_progress, macro * micro);
            for (int i = 0; i < macro; i++) {
                for (int j = 0; j < micro; j++) {
                    if (progress_tracker && mpi.isMaster()) {
                        if(++(*progress_tracker) % 10 == 0) {
                            progress_tracker->display();
                        }
                    }
                    sim.move();
                    analysis.sample();
                }
            }
            if (progress_tracker && mpi.isMaster()) {
                progress_tracker->done();

            if (not quiet) {
                faunus_logger->log((sim.drift() < 1E-9) ? spdlog::level::info : spdlog::level::warn,
                                   "relative drift = {}", sim.drift());
            }

            // --output
            std::ofstream f(Faunus::MPI::prefix + args["--output"].asString());
            if (f) {
                json json_out;
                Faunus::to_json(json_out, sim);
                json_out["relative drift"] = sim.drift();
                json_out["analysis"] = analysis;
                if (mpi.nproc() > 1) {
                    json_out["mpi"] = mpi;
                }
#ifdef GIT_COMMIT_HASH
                json_out["git revision"] = GIT_COMMIT_HASH;
#endif
#ifdef __VERSION__
                json_out["compiler"] = __VERSION__;
#endif
                f << std::setw(4) << json_out << endl;
            }
        }

        mpi.finalize();

    } catch (std::exception &e) {
        faunus_logger->error(e.what());

        if (not usageTip.buffer.empty())
            faunus_logger->error(usageTip.buffer);
#ifdef ENABLE_SID
        // easter egg...
        if (not nofun and mpi.isMaster()) { // -> fun
            try {
                // look for json file with hvsc sid tune names
                std::string pfx;
                json json_music;
                for (std::string dir : {FAUNUS_BINARY_DIR, FAUNUS_INSTALL_PREFIX
                                        "/share/faunus/"}) { // installed and uninstalled cmake builds
                    json_music = Faunus::openjson(dir + "/sids/music.json", false);
                    if (!json_music.empty()) {
                        pfx = dir + "/";
                        break;
                    }
                }
                if (!json_music.empty()) {
                    json_music = json_music.at("songs"); // load playlist

                    std::vector<size_t> weight; // weight for each tune (= number of subsongs)
                    for (auto &i : json_music)
                        weight.push_back(i.at("subsongs").size());
                    std::discrete_distribution<size_t> dist(weight.begin(), weight.end());

                    Faunus::random.seed();                             // give global random a hardware seed
                    auto it = json_music.begin() + dist(Faunus::random.engine); // pick random tune weighted by subsongs
                    auto subsongs = (*it).at("subsongs").get<std::vector<int>>();                 // all subsongs
                    int subsong = *(Faunus::random.sample(subsongs.begin(), subsongs.end())) - 1; // random subsong

                    CPPSID::Player player; // let's emulate a Commodore 64...

                    if (player.load(pfx + it->at("file").get<std::string>(), subsong)) {
                        faunus_logger->info("error message music '{}' by {}, {} (6502/SID emulation)", player.title(),
                                            player.author(), player.info());
                        faunus_logger->info("\033[1mpress ctrl-c to quit\033[0m");
                        player.start();                          // start music
                        sleep_for(10ns);                         // short delay
                        sleep_until(system_clock::now() + 240s); // play for 4 minutes, then exit
                        player.stop();
                        std::cout << std::endl;
                    }
                }
            } catch (const std::exception &) {
                // silently ignore if something fails; it's just for fun
            }
        } // end of fun
#endif
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

std::shared_ptr<ProgressTracker> createProgressTracker(bool show_progress, unsigned int steps) {
    using namespace ProgressIndicator;
    using namespace std::chrono;
    std::shared_ptr<ProgressTracker> tracker = nullptr;
    if(show_progress) {
        if (isatty(fileno(stdout))) {
            // show a progress bar on the console
            tracker = std::make_shared<ProgressBar>(steps);
        } else {
            // not in a console
            tracker = std::make_shared<TaciturnDecorator>(
                // hence print a new line
                std::make_shared<ProgressLog>(steps),
                // at most every 10 minutes or after 0.5% of progress, whatever comes first
                duration_cast<milliseconds>(minutes(10)), 0.005);
        }
    }
    return tracker;
}
<commit_msg>Make --quiet option consistent<commit_after>#include "core.h"
#include "mpicontroller.h"
#include "move.h"
#include "montecarlo.h"
#include "analysis.h"
#include "docopt.h"
#include "progress_tracker.h"
#include <cstdlib>
#include "spdlog/spdlog.h"
#include <spdlog/sinks/null_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <iomanip>
#include <unistd.h>

#ifdef ENABLE_SID
#include "cppsid.h"
#include <thread>
using namespace std::this_thread;     // sleep_for, sleep_until
using namespace std::chrono_literals; // ns, us, ms, s, h, etc.
using std::chrono::system_clock;
#endif

using namespace Faunus;
using namespace std;

#define Q(x) #x
#define QUOTE(x) Q(x)

#ifndef FAUNUS_TIPSFILE
#define FAUNUS_TIPSFILE ""
#endif

static const char USAGE[] =
    R"(Faunus - the Monte Carlo code you're looking for!

    http://github.com/mlund/faunus

    Usage:
      faunus [-q] [--verbosity <N>] [--nobar] [--nopfx] [--notips] [--nofun] [--state=<file>] [--input=<file>] [--output=<file>]
      faunus (-h | --help)
      faunus --version

    Options:
      -i <file> --input <file>   Input file [default: /dev/stdin].
      -o <file> --output <file>  Output file [default: out.json].
      -s <file> --state <file>   State file to start from (.json/.ubj).
      -v <N> --verbosity <N>     Log verbosity level (0 = off, 1 = critical, ..., 6 = trace) [default: 3]
      -q --quiet                 Less verbose output. It implicates -v0 --nobar --notips --nofun.
      -h --help                  Show this screen.
      --nobar                    No progress bar.
      --nopfx                    Do not prefix input file with MPI rank.
      --notips                   Do not give input assistance
      --nofun                    No fun
      --version                  Show version.

    Multiple processes using MPI:

    1. input and output files are prefixed with "mpi{rank}."
    2. standard output is redirected to "mpi{rank}.stdout"
    3. Input prefixing can be suppressed with --nopfx
)";

using ProgressIndicator::ProgressTracker;
std::shared_ptr<ProgressTracker> createProgressTracker(bool, unsigned int);

int main(int argc, char **argv) {
    using namespace Faunus::MPI;
    bool quiet = false, nofun = true; // conservative defaults
    try {
        std::string version = "Faunus";
#ifdef GIT_LATEST_TAG
        version += " "s + QUOTE(GIT_LATEST_TAG);
#endif
#ifdef GIT_COMMIT_HASH
        version += " git " + std::string(GIT_COMMIT_HASH);
#endif
#ifdef ENABLE_SID
        version += " [sid]";
#endif
#ifdef ENABLE_MPI
        version += " [mpi]";
#endif
#ifdef _OPENMP
        version += " [openmp]";
#endif
        auto args = docopt::docopt(USAGE, {argv + 1, argv + argc}, true, version);

        // --quiet; set quiet first as it also affects exception handling
        quiet = args["--quiet"].asBool();
        if (quiet) {
            cout.setstate(std::ios_base::failbit); // hold kæft
        }

        mpi.init(); // initialize MPI, if available

        // prepare loggers
        // TODO refactor to a standalone function and use cmd line options for different sinks, etc.
        faunus_logger = spdlog::stderr_color_mt("faunus");
        faunus_logger->set_pattern("[%n %P] %^%L: %v%$");
        mcloop_logger = spdlog::stderr_color_mt("mcloop");
        mcloop_logger->set_pattern("[%n %P] [%E.%f] %L: %v");

        // --verbosity (log level)
        long log_level = quiet ? 0 : spdlog::level::off - args["--verbosity"].asLong(); // reverse sequence 0 → 6 to 6 → 0
        spdlog::set_level(static_cast<spdlog::level::level_enum>(log_level));

        // --notips
        if (!quiet && !args["--notips"].asBool()) {
            usageTip.load({FAUNUS_TIPSFILE});
        }

        // --nobar
        bool show_progress = !quiet && !args["--nobar"].asBool();

        // --nofun
        nofun = args["--nofun"].asBool();
        usageTip.asciiart = !quiet && !nofun;
#ifdef ENABLE_SID
        usageTip.asciiart = false; // if SID is enabled, disable ascii
#endif

        // --nopfx
        bool prefix = !args["--nopfx"].asBool();

        // --input
        json json_in;
        auto input = args["--input"].asString();
        if (input == "/dev/stdin") {
            std::cin >> json_in;
        } else {
            if (prefix) {
                input = Faunus::MPI::prefix + input;
            }
            json_in = openjson(input);
        }

        {
            pc::temperature = json_in.at("temperature").get<double>() * 1.0_K;
            MCSimulation sim(json_in, mpi);

            // --state
            if (args["--state"]) {
                std::ifstream f;
                std::string state = Faunus::MPI::prefix + args["--state"].asString();
                std::string suffix = state.substr(state.find_last_of(".") + 1);
                bool binary = (suffix == "ubj");
                auto mode = std::ios::in;
                if (binary)
                    mode = std::ifstream::ate | std::ios::binary; // ate = open at end
                f.open(state, mode);
                if (f) {
                    json json_state;
                    faunus_logger->info("loading state file {}", state);
                    if (binary) {
                        size_t size = f.tellg(); // get file size
                        std::vector<std::uint8_t> v(size / sizeof(std::uint8_t));
                        f.seekg(0, f.beg); // go back to start
                        f.read((char *)v.data(), size);
                        json_state = json::from_ubjson(v);
                    } else {
                        f >> json_state;
                    }
                    sim.restore(json_state);
                } else {
                    throw std::runtime_error("state file error: " + state);
                }
            }

            Analysis::CombinedAnalysis analysis(json_in.at("analysis"), sim.space(), sim.pot());

            auto &loop = json_in.at("mcloop");
            int macro = loop.at("macro");
            int micro = loop.at("micro");

            auto progress_tracker = createProgressTracker(show_progress, macro * micro);
            for (int i = 0; i < macro; i++) {
                for (int j = 0; j < micro; j++) {
                    if (progress_tracker && mpi.isMaster()) {
                        if(++(*progress_tracker) % 10 == 0) {
                            progress_tracker->display();
                        }
                    }
                    sim.move();
                    analysis.sample();
                }
            }
            if (progress_tracker && mpi.isMaster()) {
                progress_tracker->done();
            }

            faunus_logger->log((sim.drift() < 1E-9) ? spdlog::level::info : spdlog::level::warn,
                               "relative drift = {}", sim.drift());

            // --output
            std::ofstream f(Faunus::MPI::prefix + args["--output"].asString());
            if (f) {
                json json_out;
                Faunus::to_json(json_out, sim);
                json_out["relative drift"] = sim.drift();
                json_out["analysis"] = analysis;
                if (mpi.nproc() > 1) {
                    json_out["mpi"] = mpi;
                }
#ifdef GIT_COMMIT_HASH
                json_out["git revision"] = GIT_COMMIT_HASH;
#endif
#ifdef __VERSION__
                json_out["compiler"] = __VERSION__;
#endif
                f << std::setw(4) << json_out << endl;
            }
        }

        mpi.finalize();

    } catch (std::exception &e) {
        faunus_logger->error(e.what());

        if (!usageTip.buffer.empty()) {
            faunus_logger->error(usageTip.buffer);
        }
#ifdef ENABLE_SID
        // easter egg...
        if (!quiet && !nofun && mpi.isMaster()) { // -> fun
            try {
                // look for json file with hvsc sid tune names
                std::string pfx;
                json json_music;
                for (std::string dir : {FAUNUS_BINARY_DIR, FAUNUS_INSTALL_PREFIX
                                        "/share/faunus/"}) { // installed and uninstalled cmake builds
                    json_music = Faunus::openjson(dir + "/sids/music.json", false);
                    if (!json_music.empty()) {
                        pfx = dir + "/";
                        break;
                    }
                }
                if (!json_music.empty()) {
                    json_music = json_music.at("songs"); // load playlist

                    std::vector<size_t> weight; // weight for each tune (= number of subsongs)
                    for (auto &i : json_music)
                        weight.push_back(i.at("subsongs").size());
                    std::discrete_distribution<size_t> dist(weight.begin(), weight.end());

                    Faunus::random.seed();                                      // give global random a hardware seed
                    auto it = json_music.begin() + dist(Faunus::random.engine); // pick random tune weighted by subsongs
                    auto subsongs = (*it).at("subsongs").get<std::vector<int>>();                 // all subsongs
                    int subsong = *(Faunus::random.sample(subsongs.begin(), subsongs.end())) - 1; // random subsong

                    CPPSID::Player player; // let's emulate a Commodore 64...

                    if (player.load(pfx + it->at("file").get<std::string>(), subsong)) {
                        faunus_logger->info("error message music '{}' by {}, {} (6502/SID emulation)", player.title(),
                                            player.author(), player.info());
                        faunus_logger->info("\033[1mpress ctrl-c to quit\033[0m");
                        player.start();                          // start music
                        sleep_for(10ns);                         // short delay
                        sleep_until(system_clock::now() + 240s); // play for 4 minutes, then exit
                        player.stop();
                        std::cout << std::endl;
                    }
                }
            } catch (const std::exception &) {
                // silently ignore if something fails; it's just for fun
            }
        } // end of fun
#endif
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

std::shared_ptr<ProgressTracker> createProgressTracker(bool show_progress, unsigned int steps) {
    using namespace ProgressIndicator;
    using namespace std::chrono;
    std::shared_ptr<ProgressTracker> tracker = nullptr;
    if(show_progress) {
        if (isatty(fileno(stdout))) {
            // show a progress bar on the console
            tracker = std::make_shared<ProgressBar>(steps);
        } else {
            // not in a console
            tracker = std::make_shared<TaciturnDecorator>(
                // hence print a new line
                std::make_shared<ProgressLog>(steps),
                // at most every 10 minutes or after 0.5% of progress, whatever comes first
                duration_cast<milliseconds>(minutes(10)), 0.005);
        }
    }
    return tracker;
}
<|endoftext|>


<commit_before>#pragma once

#include <random>
#include <functional>

namespace dust
{
    template<class State, class Observation>
    class filter
    {
    public:
        using motion_model = std::function<std::pair<float, State>(const State&,
                                                                   const Observation&,
                                                                   float)>;
        using uniform_state = std::function<State()>;

        filter(unsigned int num_particles)
            : num_particles(num_particles),
              resample_dist(0, 1.0f / num_particles)
        {
            reset();
            sampled_particles.resize(num_particles);
        }
        filter(unsigned int num_particles, const State& init)
            : num_particles(num_particles),
              resample_dist(0, 1.0f / num_particles),
              particles(num_particles, init)
        {
            sampled_particles.resize(num_particles);
        }

        void reset()
        {
            particles.clear();
            particles.reserve(num_particles);
            std::generate_n(std::back_inserter(particles), num_particles,
                            [&]{ return uniform(); });
        }

        void update(const Observation& z, float dt)
        {
            sampled_particles.clear();
            sampled_particles.reserve(num_particles);
            std::transform(particles.begin(), particles.end(),
                           std::back_inserter(sampled_particles),
                           [&](const State& particle) {
                               return motion(particle, z, dt);
                           });

            particles.clear();
            auto r = resample_dist(gen);
            auto c = sampled_particles.front().first;
            unsigned int i = 0;
            for (unsigned int m = 0; m < num_particles; m++)
            {
                auto U = r + (m - 1.0f) / num_particles;
                while (U > c)
                {
                    i++;
                    c += sampled_particles[i].first;
                }
                particles.push_back(sampled_particles[i].second);
            }

            largest_weight_dirty = true;
        }

        State& estimate_largest_weight()
        {
            if (largest_weight_dirty)
            {
                largest_weight_dirty = false;
                float largest = 0;
                for (auto& p : sampled_particles)
                {
                    if (p.first > largest)
                    {
                        largest = p.first;
                        largest_weight = p.second;
                    }
                }
            }

            return largest_weight;
        }

        const std::vector<std::pair<float, State>>& get_sampled_particles() const
        {
            return sampled_particles;
        }

    protected:
        virtual std::pair<float, State> motion(const State& state,
                                               const Observation& obs,
                                               float dt) const = 0;
        virtual State uniform() const = 0;
        mutable std::mt19937 gen;

    private:
        unsigned int num_particles;
        std::vector<State> particles;
        std::vector<std::pair<float, State>> sampled_particles;
        std::uniform_real_distribution<float> resample_dist;

        bool largest_weight_dirty = true;
        State largest_weight;
    };
}
<commit_msg>removed memoization of largest_weight<commit_after>#pragma once

#include <random>
#include <functional>

namespace dust
{
    template<class State, class Observation>
    class filter
    {
    public:
        using motion_model = std::function<std::pair<float, State>(const State&,
                                                                   const Observation&,
                                                                   float)>;
        using uniform_state = std::function<State()>;

        filter(unsigned int num_particles)
            : num_particles(num_particles),
              resample_dist(0, 1.0f / num_particles)
        {
            reset();
            sampled_particles.resize(num_particles);
        }
        filter(unsigned int num_particles, const State& init)
            : num_particles(num_particles),
              resample_dist(0, 1.0f / num_particles),
              particles(num_particles, init)
        {
            sampled_particles.resize(num_particles);
        }

        void reset()
        {
            particles.clear();
            particles.reserve(num_particles);
            std::generate_n(std::back_inserter(particles), num_particles,
                            [&]{ return uniform(); });
        }

        void update(const Observation& z, float dt)
        {
            sampled_particles.clear();
            sampled_particles.reserve(num_particles);
            std::transform(particles.begin(), particles.end(),
                           std::back_inserter(sampled_particles),
                           [&](const State& particle) {
                               return motion(particle, z, dt);
                           });

            particles.clear();
            auto r = resample_dist(gen);
            auto c = sampled_particles.front().first;
            unsigned int i = 0;
            for (unsigned int m = 0; m < num_particles; m++)
            {
                auto U = r + (m - 1.0f) / num_particles;
                while (U > c)
                {
                    i++;
                    c += sampled_particles[i].first;
                }
                particles.push_back(sampled_particles[i].second);
            }
        }

        std::pair<float, State> estimate_largest_weight()
        {
            std::pair<float, State> largest = { 0, State() };
            for (auto& p : sampled_particles)
            {
                if (p.first > largest.first)
                {
                    largest = p;
                }
            }

            return largest;
        }

        const std::vector<std::pair<float, State>>& get_sampled_particles() const
        {
            return sampled_particles;
        }

    protected:
        virtual std::pair<float, State> motion(const State& state,
                                               const Observation& obs,
                                               float dt) const = 0;
        virtual State uniform() const = 0;
        mutable std::mt19937 gen;

    private:
        unsigned int num_particles;
        std::vector<State> particles;
        std::vector<std::pair<float, State>> sampled_particles;
        std::uniform_real_distribution<float> resample_dist;
    };
}
<|endoftext|>