<commit_before>/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrConvolutionEffect.h"
#include "gl/GrGLEffect.h"
#include "gl/GrGLEffectMatrix.h"
#include "gl/GrGLSL.h"
#include "gl/GrGLTexture.h"
#include "GrTBackendEffectFactory.h"

// For brevity
typedef GrGLUniformManager::UniformHandle UniformHandle;

class GrGLConvolutionEffect : public GrGLEffect {
public:
    GrGLConvolutionEffect(const GrBackendEffectFactory&, const GrDrawEffect&);

    virtual void emitCode(GrGLShaderBuilder*,
                          const GrDrawEffect&,
                          EffectKey,
                          const char* outputColor,
                          const char* inputColor,
                          const TextureSamplerArray&) SK_OVERRIDE;

    virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE;

    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);

private:
    int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); }
    bool useBounds() const { return fUseBounds; }
    Gr1DKernelEffect::Direction direction() const { return fDirection; }

    int                 fRadius;
    bool                fUseBounds;
    Gr1DKernelEffect::Direction    fDirection;
    UniformHandle       fKernelUni;
    UniformHandle       fImageIncrementUni;
    UniformHandle       fBoundsUni;
    GrGLEffectMatrix    fEffectMatrix;

    typedef GrGLEffect INHERITED;
};

GrGLConvolutionEffect::GrGLConvolutionEffect(const GrBackendEffectFactory& factory,
                                             const GrDrawEffect& drawEffect)
    : INHERITED(factory)
    , fEffectMatrix(drawEffect.castEffect<GrConvolutionEffect>().coordsType()) {
    const GrConvolutionEffect& c = drawEffect.castEffect<GrConvolutionEffect>();
    fRadius = c.radius();
    fUseBounds = c.useBounds();
    fDirection = c.direction();
}

void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
                                     const GrDrawEffect&,
                                     EffectKey key,
                                     const char* outputColor,
                                     const char* inputColor,
                                     const TextureSamplerArray& samplers) {
    const char* coords;
    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &coords);
    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
                                             kVec2f_GrSLType, "ImageIncrement");
    if (this->useBounds()) {
        fBoundsUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
                                         kVec2f_GrSLType, "Bounds");
    }
    fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
                                          kFloat_GrSLType, "Kernel", this->width());

    builder->fsCodeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);

    int width = this->width();
    const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni);
    const char* imgInc = builder->getUniformCStr(fImageIncrementUni);

    builder->fsCodeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords, fRadius, imgInc);

    // Manually unroll loop because some drivers don't; yields 20-30% speedup.
    for (int i = 0; i < width; i++) {
        SkString index;
        SkString kernelIndex;
        index.appendS32(i);
        kernel.appendArrayAccess(index.c_str(), &kernelIndex);
        builder->fsCodeAppendf("\t\t%s += ", outputColor);
        builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType, samplers[0], "coord");
        if (this->useBounds()) {
            const char* bounds = builder->getUniformCStr(fBoundsUni);
            const char* component = this->direction() == Gr1DKernelEffect::kY_Direction ? "y" : "x";
            builder->fsCodeAppendf(" * float(coord.%s >= %s.x && coord.%s <= %s.y)",
                component, bounds, component, bounds);
        }
        builder->fsCodeAppendf(" * %s;\n", kernelIndex.c_str());
        builder->fsCodeAppendf("\t\tcoord += %s;\n", imgInc);
    }

    SkString modulate;
    GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
    builder->fsCodeAppend(modulate.c_str());
}

void GrGLConvolutionEffect::setData(const GrGLUniformManager& uman,
                                    const GrDrawEffect& drawEffect) {
    const GrConvolutionEffect& conv = drawEffect.castEffect<GrConvolutionEffect>();
    GrTexture& texture = *conv.texture(0);
    // the code we generated was for a specific kernel radius
    SkASSERT(conv.radius() == fRadius);
    float imageIncrement[2] = { 0 };
    float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
    switch (conv.direction()) {
        case Gr1DKernelEffect::kX_Direction:
            imageIncrement[0] = 1.0f / texture.width();
            break;
        case Gr1DKernelEffect::kY_Direction:
            imageIncrement[1] = ySign / texture.height();
            break;
        default:
            GrCrash("Unknown filter direction.");
    }
    uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
    if (conv.useBounds()) {
        const float* bounds = conv.bounds();
        if (Gr1DKernelEffect::kY_Direction == conv.direction() &&
            texture.origin() != kTopLeft_GrSurfaceOrigin) {
            uman.set2f(fBoundsUni, 1.0f - bounds[1], 1.0f - bounds[0]);
        } else {
            uman.set2f(fBoundsUni, bounds[0], bounds[1]);
        }
    }
    uman.set1fv(fKernelUni, 0, this->width(), conv.kernel());
    fEffectMatrix.setData(uman, conv.getMatrix(), drawEffect, conv.texture(0));
}

GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrDrawEffect& drawEffect,
                                                    const GrGLCaps&) {
    const GrConvolutionEffect& conv = drawEffect.castEffect<GrConvolutionEffect>();
    EffectKey key = conv.radius();
    key <<= 2;
    if (conv.useBounds()) {
        key |= 0x2;
        key |= GrConvolutionEffect::kY_Direction == conv.direction() ? 0x1 : 0x0;
    }
    key <<= GrGLEffectMatrix::kKeyBits;
    EffectKey matrixKey = GrGLEffectMatrix::GenKey(conv.getMatrix(),
                                                   drawEffect,
                                                   conv.coordsType(),
                                                   conv.texture(0));
    return key | matrixKey;
}

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

GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
                                         Direction direction,
                                         int radius,
                                         const float* kernel,
                                         bool useBounds,
                                         float bounds[2])
    : Gr1DKernelEffect(texture, direction, radius), fUseBounds(useBounds) {
    SkASSERT(radius <= kMaxKernelRadius);
    SkASSERT(NULL != kernel);
    int width = this->width();
    for (int i = 0; i < width; i++) {
        fKernel[i] = kernel[i];
    }
    memcpy(fBounds, bounds, sizeof(fBounds));
}

GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
                                         Direction direction,
                                         int radius,
                                         float gaussianSigma,
                                         bool useBounds,
                                         float bounds[2])
    : Gr1DKernelEffect(texture, direction, radius), fUseBounds(useBounds) {
    SkASSERT(radius <= kMaxKernelRadius);
    int width = this->width();

    float sum = 0.0f;
    float denom = 1.0f / (2.0f * gaussianSigma * gaussianSigma);
    for (int i = 0; i < width; ++i) {
        float x = static_cast<float>(i - this->radius());
        // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
        // is dropped here, since we renormalize the kernel below.
        fKernel[i] = sk_float_exp(- x * x * denom);
        sum += fKernel[i];
    }
    // Normalize the kernel
    float scale = 1.0f / sum;
    for (int i = 0; i < width; ++i) {
        fKernel[i] *= scale;
    }
    memcpy(fBounds, bounds, sizeof(fBounds));
}

GrConvolutionEffect::~GrConvolutionEffect() {
}

const GrBackendEffectFactory& GrConvolutionEffect::getFactory() const {
    return GrTBackendEffectFactory<GrConvolutionEffect>::getInstance();
}

bool GrConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
    const GrConvolutionEffect& s = CastEffect<GrConvolutionEffect>(sBase);
    return (this->texture(0) == s.texture(0) &&
            this->radius() == s.radius() &&
            this->direction() == s.direction() &&
            this->useBounds() == s.useBounds() &&
            0 == memcmp(fBounds, s.fBounds, sizeof(fBounds)) &&
            0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
}

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

GR_DEFINE_EFFECT_TEST(GrConvolutionEffect);

GrEffectRef* GrConvolutionEffect::TestCreate(SkMWCRandom* random,
                                             GrContext*,
                                             const GrDrawTargetCaps&,
                                             GrTexture* textures[]) {
    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
                                      GrEffectUnitTest::kAlphaTextureIdx;
    Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
    int radius = random->nextRangeU(1, kMaxKernelRadius);
    float kernel[kMaxKernelRadius];
    for (int i = 0; i < kMaxKernelRadius; ++i) {
        kernel[i] = random->nextSScalar1();
    }
    float bounds[2];
    for (int i = 0; i < 2; ++i) {
        bounds[i] = random->nextF();
    }

    bool useBounds = random->nextBool();
    return GrConvolutionEffect::Create(textures[texIdx],
                                       dir,
                                       radius,
                                       kernel,
                                       useBounds,
                                       bounds);
}
<commit_msg>Fix stack buffer read overflow in GrConvolutionEffect::TestCreate.<commit_after>/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrConvolutionEffect.h"
#include "gl/GrGLEffect.h"
#include "gl/GrGLEffectMatrix.h"
#include "gl/GrGLSL.h"
#include "gl/GrGLTexture.h"
#include "GrTBackendEffectFactory.h"

// For brevity
typedef GrGLUniformManager::UniformHandle UniformHandle;

class GrGLConvolutionEffect : public GrGLEffect {
public:
    GrGLConvolutionEffect(const GrBackendEffectFactory&, const GrDrawEffect&);

    virtual void emitCode(GrGLShaderBuilder*,
                          const GrDrawEffect&,
                          EffectKey,
                          const char* outputColor,
                          const char* inputColor,
                          const TextureSamplerArray&) SK_OVERRIDE;

    virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE;

    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);

private:
    int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); }
    bool useBounds() const { return fUseBounds; }
    Gr1DKernelEffect::Direction direction() const { return fDirection; }

    int                 fRadius;
    bool                fUseBounds;
    Gr1DKernelEffect::Direction    fDirection;
    UniformHandle       fKernelUni;
    UniformHandle       fImageIncrementUni;
    UniformHandle       fBoundsUni;
    GrGLEffectMatrix    fEffectMatrix;

    typedef GrGLEffect INHERITED;
};

GrGLConvolutionEffect::GrGLConvolutionEffect(const GrBackendEffectFactory& factory,
                                             const GrDrawEffect& drawEffect)
    : INHERITED(factory)
    , fEffectMatrix(drawEffect.castEffect<GrConvolutionEffect>().coordsType()) {
    const GrConvolutionEffect& c = drawEffect.castEffect<GrConvolutionEffect>();
    fRadius = c.radius();
    fUseBounds = c.useBounds();
    fDirection = c.direction();
}

void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
                                     const GrDrawEffect&,
                                     EffectKey key,
                                     const char* outputColor,
                                     const char* inputColor,
                                     const TextureSamplerArray& samplers) {
    const char* coords;
    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &coords);
    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
                                             kVec2f_GrSLType, "ImageIncrement");
    if (this->useBounds()) {
        fBoundsUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
                                         kVec2f_GrSLType, "Bounds");
    }
    fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
                                          kFloat_GrSLType, "Kernel", this->width());

    builder->fsCodeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);

    int width = this->width();
    const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni);
    const char* imgInc = builder->getUniformCStr(fImageIncrementUni);

    builder->fsCodeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords, fRadius, imgInc);

    // Manually unroll loop because some drivers don't; yields 20-30% speedup.
    for (int i = 0; i < width; i++) {
        SkString index;
        SkString kernelIndex;
        index.appendS32(i);
        kernel.appendArrayAccess(index.c_str(), &kernelIndex);
        builder->fsCodeAppendf("\t\t%s += ", outputColor);
        builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType, samplers[0], "coord");
        if (this->useBounds()) {
            const char* bounds = builder->getUniformCStr(fBoundsUni);
            const char* component = this->direction() == Gr1DKernelEffect::kY_Direction ? "y" : "x";
            builder->fsCodeAppendf(" * float(coord.%s >= %s.x && coord.%s <= %s.y)",
                component, bounds, component, bounds);
        }
        builder->fsCodeAppendf(" * %s;\n", kernelIndex.c_str());
        builder->fsCodeAppendf("\t\tcoord += %s;\n", imgInc);
    }

    SkString modulate;
    GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
    builder->fsCodeAppend(modulate.c_str());
}

void GrGLConvolutionEffect::setData(const GrGLUniformManager& uman,
                                    const GrDrawEffect& drawEffect) {
    const GrConvolutionEffect& conv = drawEffect.castEffect<GrConvolutionEffect>();
    GrTexture& texture = *conv.texture(0);
    // the code we generated was for a specific kernel radius
    SkASSERT(conv.radius() == fRadius);
    float imageIncrement[2] = { 0 };
    float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
    switch (conv.direction()) {
        case Gr1DKernelEffect::kX_Direction:
            imageIncrement[0] = 1.0f / texture.width();
            break;
        case Gr1DKernelEffect::kY_Direction:
            imageIncrement[1] = ySign / texture.height();
            break;
        default:
            GrCrash("Unknown filter direction.");
    }
    uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
    if (conv.useBounds()) {
        const float* bounds = conv.bounds();
        if (Gr1DKernelEffect::kY_Direction == conv.direction() &&
            texture.origin() != kTopLeft_GrSurfaceOrigin) {
            uman.set2f(fBoundsUni, 1.0f - bounds[1], 1.0f - bounds[0]);
        } else {
            uman.set2f(fBoundsUni, bounds[0], bounds[1]);
        }
    }
    uman.set1fv(fKernelUni, 0, this->width(), conv.kernel());
    fEffectMatrix.setData(uman, conv.getMatrix(), drawEffect, conv.texture(0));
}

GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrDrawEffect& drawEffect,
                                                    const GrGLCaps&) {
    const GrConvolutionEffect& conv = drawEffect.castEffect<GrConvolutionEffect>();
    EffectKey key = conv.radius();
    key <<= 2;
    if (conv.useBounds()) {
        key |= 0x2;
        key |= GrConvolutionEffect::kY_Direction == conv.direction() ? 0x1 : 0x0;
    }
    key <<= GrGLEffectMatrix::kKeyBits;
    EffectKey matrixKey = GrGLEffectMatrix::GenKey(conv.getMatrix(),
                                                   drawEffect,
                                                   conv.coordsType(),
                                                   conv.texture(0));
    return key | matrixKey;
}

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

GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
                                         Direction direction,
                                         int radius,
                                         const float* kernel,
                                         bool useBounds,
                                         float bounds[2])
    : Gr1DKernelEffect(texture, direction, radius), fUseBounds(useBounds) {
    SkASSERT(radius <= kMaxKernelRadius);
    SkASSERT(NULL != kernel);
    int width = this->width();
    for (int i = 0; i < width; i++) {
        fKernel[i] = kernel[i];
    }
    memcpy(fBounds, bounds, sizeof(fBounds));
}

GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
                                         Direction direction,
                                         int radius,
                                         float gaussianSigma,
                                         bool useBounds,
                                         float bounds[2])
    : Gr1DKernelEffect(texture, direction, radius), fUseBounds(useBounds) {
    SkASSERT(radius <= kMaxKernelRadius);
    int width = this->width();

    float sum = 0.0f;
    float denom = 1.0f / (2.0f * gaussianSigma * gaussianSigma);
    for (int i = 0; i < width; ++i) {
        float x = static_cast<float>(i - this->radius());
        // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
        // is dropped here, since we renormalize the kernel below.
        fKernel[i] = sk_float_exp(- x * x * denom);
        sum += fKernel[i];
    }
    // Normalize the kernel
    float scale = 1.0f / sum;
    for (int i = 0; i < width; ++i) {
        fKernel[i] *= scale;
    }
    memcpy(fBounds, bounds, sizeof(fBounds));
}

GrConvolutionEffect::~GrConvolutionEffect() {
}

const GrBackendEffectFactory& GrConvolutionEffect::getFactory() const {
    return GrTBackendEffectFactory<GrConvolutionEffect>::getInstance();
}

bool GrConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
    const GrConvolutionEffect& s = CastEffect<GrConvolutionEffect>(sBase);
    return (this->texture(0) == s.texture(0) &&
            this->radius() == s.radius() &&
            this->direction() == s.direction() &&
            this->useBounds() == s.useBounds() &&
            0 == memcmp(fBounds, s.fBounds, sizeof(fBounds)) &&
            0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
}

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

GR_DEFINE_EFFECT_TEST(GrConvolutionEffect);

GrEffectRef* GrConvolutionEffect::TestCreate(SkMWCRandom* random,
                                             GrContext*,
                                             const GrDrawTargetCaps&,
                                             GrTexture* textures[]) {
    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
                                      GrEffectUnitTest::kAlphaTextureIdx;
    Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
    int radius = random->nextRangeU(1, kMaxKernelRadius);
    float kernel[kMaxKernelWidth];
    for (size_t i = 0; i < SK_ARRAY_COUNT(kernel); ++i) {
        kernel[i] = random->nextSScalar1();
    }
    float bounds[2];
    for (size_t i = 0; i < SK_ARRAY_COUNT(bounds); ++i) {
        bounds[i] = random->nextF();
    }

    bool useBounds = random->nextBool();
    return GrConvolutionEffect::Create(textures[texIdx],
                                       dir,
                                       radius,
                                       kernel,
                                       useBounds,
                                       bounds);
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtSensors module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/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 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qmlsensorglobal.h"
#include <QSensor>

QT_BEGIN_NAMESPACE

/*!
    \qmltype SensorGlobal
    \instantiates QmlSensorGlobal
    \inqmlmodule QtSensors 5.0
    \since QtSensors 5.0
    \brief The SensorGlobal element provides the module API.

    The SensorGlobal element provides the module API.

    This element cannot be directly created. It can only be accessed via a namespace import.

    \code
    import QtSensors 5.0
    import QtSensors 5.0 as Sensors
    ...
        Component.onCompleted: {
            var types = Sensors.sensorTypes();
            console.log(types.join(", "));
        }
    \endcode
*/

QmlSensorGlobal::QmlSensorGlobal(QObject *parent)
    : QObject(parent)
    , m_sensor(new QSensor(QByteArray(), this))
{
    connect(m_sensor, SIGNAL(availableSensorsChanged()), this, SIGNAL(availableSensorsChanged()));
}

QmlSensorGlobal::~QmlSensorGlobal()
{
}

/*!
    \qmlmethod list<string> SensorGlobal::sensorTypes()
    Returns a list of the sensor types that have been registered.

    Please see QSensor::sensorTypes() for information.
*/
QStringList QmlSensorGlobal::sensorTypes() const
{
    QStringList ret;
    foreach (const QByteArray &type, QSensor::sensorTypes())
        ret << QString::fromLocal8Bit(type);
    return ret;
}

/*!
    \qmlmethod list<string> SensorGlobal::sensorsForType(type)
    Returns a list of the sensor identifiers that have been registered for \a type.

    Please see QSensor::sensorsForType() for information.
*/
QStringList QmlSensorGlobal::sensorsForType(const QString &type) const
{
    QStringList ret;
    foreach (const QByteArray &identifier, QSensor::sensorsForType(type.toLocal8Bit()))
        ret << QString::fromLocal8Bit(identifier);
    return ret;
}

/*!
    \qmlmethod string SensorGlobal::defaultSensorForType(type)
    Returns the default sensor identifier that has been registered for \a type.

    Please see QSensor::defaultSensorForType() for information.
*/
QString QmlSensorGlobal::defaultSensorForType(const QString &type) const
{
    return QString::fromLocal8Bit(QSensor::defaultSensorForType(type.toLocal8Bit()));
}

QT_END_NAMESPACE
<commit_msg>[Doc] Fix the SensorGlobal usage example<commit_after>/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtSensors module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/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 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qmlsensorglobal.h"
#include <QSensor>

QT_BEGIN_NAMESPACE

/*!
    \qmltype SensorGlobal
    \instantiates QmlSensorGlobal
    \inqmlmodule QtSensors 5.0
    \since QtSensors 5.0
    \brief The SensorGlobal element provides the module API.

    The SensorGlobal element provides the module API.

    This element cannot be directly created. It can only be accessed via a namespace import.

    \code
    import QtSensors 5.0
    import QtSensors 5.0 as Sensors
    ...
        Component.onCompleted: {
            var types = Sensors.QmlSensors.sensorTypes();
            console.log(types.join(", "));
        }
    \endcode
*/

QmlSensorGlobal::QmlSensorGlobal(QObject *parent)
    : QObject(parent)
    , m_sensor(new QSensor(QByteArray(), this))
{
    connect(m_sensor, SIGNAL(availableSensorsChanged()), this, SIGNAL(availableSensorsChanged()));
}

QmlSensorGlobal::~QmlSensorGlobal()
{
}

/*!
    \qmlmethod list<string> SensorGlobal::sensorTypes()
    Returns a list of the sensor types that have been registered.

    Please see QSensor::sensorTypes() for information.
*/
QStringList QmlSensorGlobal::sensorTypes() const
{
    QStringList ret;
    foreach (const QByteArray &type, QSensor::sensorTypes())
        ret << QString::fromLocal8Bit(type);
    return ret;
}

/*!
    \qmlmethod list<string> SensorGlobal::sensorsForType(type)
    Returns a list of the sensor identifiers that have been registered for \a type.

    Please see QSensor::sensorsForType() for information.
*/
QStringList QmlSensorGlobal::sensorsForType(const QString &type) const
{
    QStringList ret;
    foreach (const QByteArray &identifier, QSensor::sensorsForType(type.toLocal8Bit()))
        ret << QString::fromLocal8Bit(identifier);
    return ret;
}

/*!
    \qmlmethod string SensorGlobal::defaultSensorForType(type)
    Returns the default sensor identifier that has been registered for \a type.

    Please see QSensor::defaultSensorForType() for information.
*/
QString QmlSensorGlobal::defaultSensorForType(const QString &type) const
{
    return QString::fromLocal8Bit(QSensor::defaultSensorForType(type.toLocal8Bit()));
}

QT_END_NAMESPACE
<|endoftext|>


<commit_before>/*
    Flexisip, a flexible SIP proxy server with media capabilities.
    Copyright (C) 2010-2015  Belledonne Communications SARL, All rights reserved.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "module.hh"
#include "agent.hh"
#include "transaction.hh"
#include "etchosts.hh"
#include <sstream>

#include <sofia-sip/su_md5.h>
#include <sofia-sip/sip_status.h>
#include <sofia-sip/tport.h>

using namespace ::std;

class StatisticsCollector: public Module, ModuleToolbox {
public:
	StatisticsCollector(Agent *ag);
	~StatisticsCollector();
	virtual void onDeclare(GenericStruct * module_config);
	virtual void onLoad(const GenericStruct *root);
	virtual void onRequest(shared_ptr<RequestSipEvent> &ev);
	virtual void onResponse(shared_ptr<ResponseSipEvent> &ev);
private:
	int managePublishContent(const shared_ptr<RequestSipEvent> ev);
	bool containsMandatoryFields(char *data, usize_t len);

	static ModuleInfo<StatisticsCollector> sInfo;
	url_t *mCollectorAddress;
	su_home_t mHome;
};

StatisticsCollector::StatisticsCollector(Agent *ag) : Module(ag), mCollectorAddress(NULL) {
	su_home_init(&mHome);
}

StatisticsCollector::~StatisticsCollector(){
	su_home_deinit(&mHome);
}

void StatisticsCollector::onDeclare(GenericStruct * module_config) {
	ConfigItemDescriptor items[] = {
			{ String, "collector-address", "SIP URI of the statistics collector. "
			"Note that application/vq-rtcpxr messages for this address will be deleted by this module and thus not be delivered.", "" },

			config_item_end
	};
	module_config->addChildrenValues(items);

	/* modify the default value for "enabled" */
	module_config->get<ConfigBoolean>("enabled")->setDefault("false");
}

void StatisticsCollector::onLoad(const GenericStruct *mc) {
	string value = mc->get<ConfigString>("collector-address")->read();
	if (value.size()>0){
		mCollectorAddress=url_make(&mHome,value.c_str());
		if (mCollectorAddress==NULL){
			LOGF("StatisticsCollector: Invalid collector address '%s'",value.c_str());
		}
	}else{
		mCollectorAddress=NULL;
	}
	LOGI("StatisticsCollector: setup with collector address '%s'",value.c_str());
}

void StatisticsCollector::onRequest(shared_ptr<RequestSipEvent> &ev) {
	const shared_ptr<MsgSip> &ms = ev->getMsgSip();
	sip_t *sip = ms->getSip();
	url_t *url = sip->sip_request->rq_url;
	// verify collector address AND content type
	if (mCollectorAddress && (url_cmp(mCollectorAddress,url)==0)) {
		if ((strcmp("application/vq-rtcpxr", sip->sip_content_type->c_type) == 0)
			&& (strcmp("vq-rtcpxr", sip->sip_content_type->c_subtype) != 0)) {
			// some treatment
			int err = managePublishContent(ev);
			ev->reply(err, sip_status_phrase(err), SIPTAG_SERVER_STR(getAgent()->getServerString()), TAG_END());
		} else {
			LOGI("StatisticsCollector: received PUBLISH for collector %s with invalid type, ignoring", mCollectorAddress->url_user);
		}
	}
}

void StatisticsCollector::onResponse(shared_ptr<ResponseSipEvent> &ev) {
}

/*avoid crash if x is NULL on libc versions <4.5.26 */
#define __strstr(x, y) ((x==NULL)?NULL:strstr(x,y))

bool StatisticsCollector::containsMandatoryFields(char *body, usize_t len) {
	char * remote_metrics_start = __strstr(body, "RemoteMetrics:");

	if (!(
		__strstr(body, "VQIntervalReport\r\n")			== body ||
		__strstr(body, "VQSessionReport\r\n")			== body ||
		__strstr(body, "VQSessionReport: CallTerm\r\n") == body))
		return false;

	if (!(body=__strstr(body, "CallID:"))) return false;
	if (!(body=__strstr(body, "LocalID:"))) return false;
	if (!(body=__strstr(body, "RemoteID:"))) return false;
	if (!(body=__strstr(body, "OrigID:"))) return false;
	if (!(body=__strstr(body, "LocalGroup:"))) return false;
	if (!(body=__strstr(body, "RemoteGroup:"))) return false;
	if (!(body=__strstr(body, "LocalAddr:"))) return false;
		if (!(body=__strstr(body, "IP="))) return false;
		if (!(body=__strstr(body, "PORT="))) return false;
		if (!(body=__strstr(body, "SSRC="))) return false;
	if (!(body=__strstr(body, "RemoteAddr:"))) return false;
		if (!(body=__strstr(body, "IP="))) return false;
		if (!(body=__strstr(body, "PORT="))) return false;
		if (!(body=__strstr(body, "SSRC="))) return false;
	if (!(body=__strstr(body, "LocalMetrics:"))) return false;
	if (!(body=__strstr(body, "Timestamps:"))) return false;
		if (!(body=__strstr(body, "START="))) return false;
		if (!(body=__strstr(body, "STOP="))) return false;

	/* We should have not reached RemoteMetrics section yet */
	if (remote_metrics_start && body >= remote_metrics_start) return false;

	return true;
}

int StatisticsCollector::managePublishContent(const shared_ptr<RequestSipEvent> ev) {
	const shared_ptr<MsgSip> &ms = ev->getMsgSip();
	sip_t *sip = ms->getSip();
	int err = 200;
	std::string statusPhrase = "OK";

	if (! sip) {
		err = 400;
		statusPhrase = "Invalid SIP";
	}

	// verify that packet contains data
	if (! sip->sip_payload || sip->sip_payload->pl_len == 0 || ! sip->sip_payload->pl_data ) {
		err = 606;
		statusPhrase = "No data in packet payload";
	// verify that packet contains mandatory fields
	} else if (! containsMandatoryFields(sip->sip_payload->pl_data, sip->sip_payload->pl_len)) {
		err = 606;
		statusPhrase = "One or several mandatory fields missing";
	}

	auto log=make_shared<CallQualityStatisticsLog>(sip->sip_from, sip->sip_to,sip->sip_payload?sip->sip_payload->pl_data:NULL);
	log->setStatusCode(err,statusPhrase.c_str());
	if (sip->sip_user_agent) log->setUserAgent(sip->sip_user_agent);
	log->setCompleted();
	ev->setEventLog(log);

	return err;
}

ModuleInfo<StatisticsCollector> StatisticsCollector::sInfo("StatisticsCollector", "The purpose of the StatisticsCollector module is to "
		"collect call statistics (RFC 6035) and store them on the server.",
		ModuleInfoBase::ModuleOid::StatisticsCollector);
<commit_msg>module-statisics-collector.cc: oops, fix broken handling of report<commit_after>/*
    Flexisip, a flexible SIP proxy server with media capabilities.
    Copyright (C) 2010-2015  Belledonne Communications SARL, All rights reserved.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "module.hh"
#include "agent.hh"
#include "transaction.hh"
#include "etchosts.hh"
#include <sstream>

#include <sofia-sip/su_md5.h>
#include <sofia-sip/sip_status.h>
#include <sofia-sip/tport.h>

using namespace ::std;

class StatisticsCollector: public Module, ModuleToolbox {
public:
	StatisticsCollector(Agent *ag);
	~StatisticsCollector();
	virtual void onDeclare(GenericStruct * module_config);
	virtual void onLoad(const GenericStruct *root);
	virtual void onRequest(shared_ptr<RequestSipEvent> &ev);
	virtual void onResponse(shared_ptr<ResponseSipEvent> &ev);
private:
	int managePublishContent(const shared_ptr<RequestSipEvent> ev);
	bool containsMandatoryFields(char *data, usize_t len);

	static ModuleInfo<StatisticsCollector> sInfo;
	url_t *mCollectorAddress;
	su_home_t mHome;
};

StatisticsCollector::StatisticsCollector(Agent *ag) : Module(ag), mCollectorAddress(NULL) {
	su_home_init(&mHome);
}

StatisticsCollector::~StatisticsCollector(){
	su_home_deinit(&mHome);
}

void StatisticsCollector::onDeclare(GenericStruct * module_config) {
	ConfigItemDescriptor items[] = {
			{ String, "collector-address", "SIP URI of the statistics collector. "
			"Note that application/vq-rtcpxr messages for this address will be deleted by this module and thus not be delivered.", "" },

			config_item_end
	};
	module_config->addChildrenValues(items);

	/* modify the default value for "enabled" */
	module_config->get<ConfigBoolean>("enabled")->setDefault("false");
}

void StatisticsCollector::onLoad(const GenericStruct *mc) {
	string value = mc->get<ConfigString>("collector-address")->read();
	if (value.size()>0){
		mCollectorAddress=url_make(&mHome,value.c_str());
		if (mCollectorAddress==NULL){
			LOGF("StatisticsCollector: Invalid collector address '%s'",value.c_str());
		}
	}else{
		mCollectorAddress=NULL;
	}
	LOGI("StatisticsCollector: setup with collector address '%s'",value.c_str());
}

void StatisticsCollector::onRequest(shared_ptr<RequestSipEvent> &ev) {
	const shared_ptr<MsgSip> &ms = ev->getMsgSip();
	sip_t *sip = ms->getSip();
	url_t *url = sip->sip_request->rq_url;
	// verify collector address AND content type
	if (mCollectorAddress && (url_cmp(mCollectorAddress,url)==0)) {
		if ((strcmp("application/vq-rtcpxr", sip->sip_content_type->c_type) == 0)
			&& (strcmp("vq-rtcpxr", sip->sip_content_type->c_subtype) == 0)) {
			// some treatment
			int err = managePublishContent(ev);
			ev->reply(err, sip_status_phrase(err), SIPTAG_SERVER_STR(getAgent()->getServerString()), TAG_END());
		} else {
			LOGI("StatisticsCollector: received PUBLISH with invalid type, ignoring");
		}
	}
}

void StatisticsCollector::onResponse(shared_ptr<ResponseSipEvent> &ev) {
}

/*avoid crash if x is NULL on libc versions <4.5.26 */
#define __strstr(x, y) ((x==NULL)?NULL:strstr(x,y))

bool StatisticsCollector::containsMandatoryFields(char *body, usize_t len) {
	char * remote_metrics_start = __strstr(body, "RemoteMetrics:");

	if (!(
		__strstr(body, "VQIntervalReport\r\n")			== body ||
		__strstr(body, "VQSessionReport\r\n")			== body ||
		__strstr(body, "VQSessionReport: CallTerm\r\n") == body))
		return false;

	if (!(body=__strstr(body, "CallID:"))) return false;
	if (!(body=__strstr(body, "LocalID:"))) return false;
	if (!(body=__strstr(body, "RemoteID:"))) return false;
	if (!(body=__strstr(body, "OrigID:"))) return false;
	if (!(body=__strstr(body, "LocalGroup:"))) return false;
	if (!(body=__strstr(body, "RemoteGroup:"))) return false;
	if (!(body=__strstr(body, "LocalAddr:"))) return false;
		if (!(body=__strstr(body, "IP="))) return false;
		if (!(body=__strstr(body, "PORT="))) return false;
		if (!(body=__strstr(body, "SSRC="))) return false;
	if (!(body=__strstr(body, "RemoteAddr:"))) return false;
		if (!(body=__strstr(body, "IP="))) return false;
		if (!(body=__strstr(body, "PORT="))) return false;
		if (!(body=__strstr(body, "SSRC="))) return false;
	if (!(body=__strstr(body, "LocalMetrics:"))) return false;
	if (!(body=__strstr(body, "Timestamps:"))) return false;
		if (!(body=__strstr(body, "START="))) return false;
		if (!(body=__strstr(body, "STOP="))) return false;

	/* We should have not reached RemoteMetrics section yet */
	if (remote_metrics_start && body >= remote_metrics_start) return false;

	return true;
}

int StatisticsCollector::managePublishContent(const shared_ptr<RequestSipEvent> ev) {
	const shared_ptr<MsgSip> &ms = ev->getMsgSip();
	sip_t *sip = ms->getSip();
	int err = 200;
	std::string statusPhrase = "OK";

	if (! sip) {
		err = 400;
		statusPhrase = "Invalid SIP";
	}

	// verify that packet contains data
	if (! sip->sip_payload || sip->sip_payload->pl_len == 0 || ! sip->sip_payload->pl_data ) {
		err = 606;
		statusPhrase = "No data in packet payload";
	// verify that packet contains mandatory fields
	} else if (! containsMandatoryFields(sip->sip_payload->pl_data, sip->sip_payload->pl_len)) {
		err = 606;
		statusPhrase = "One or several mandatory fields missing";
	}

	auto log=make_shared<CallQualityStatisticsLog>(sip->sip_from, sip->sip_to,sip->sip_payload?sip->sip_payload->pl_data:NULL);
	log->setStatusCode(err,statusPhrase.c_str());
	if (sip->sip_user_agent) log->setUserAgent(sip->sip_user_agent);
	log->setCompleted();
	ev->setEventLog(log);

	return err;
}

ModuleInfo<StatisticsCollector> StatisticsCollector::sInfo("StatisticsCollector", "The purpose of the StatisticsCollector module is to "
		"collect call statistics (RFC 6035) and store them on the server.",
		ModuleInfoBase::ModuleOid::StatisticsCollector);
<|endoftext|>


<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2013 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/
/**
 * @file geofence.cpp
 * Provides functions for handling the geofence
 *
 * @author Jean Cyr <jean.m.cyr@gmail.com>
 * @author Thomas Gubler <thomasgubler@gmail.com>
 */
#include "geofence.h"

#include <uORB/topics/vehicle_global_position.h>
#include <string.h>
#include <dataman/dataman.h>
#include <systemlib/err.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <nuttx/config.h>
#include <unistd.h>

/* Oddly, ERROR is not defined for C++ */
#ifdef ERROR
# undef ERROR
#endif
static const int ERROR = -1;

Geofence::Geofence() :
		SuperBlock(NULL, "GF"),
		_fence_pub(-1),
		_altitude_min(0),
		_altitude_max(0),
		_verticesCount(0),
		param_geofence_on(this, "ON")
{
	/* Load initial params */
	updateParams();
}

Geofence::~Geofence()
{

}

bool Geofence::inside(const struct vehicle_global_position_s *vehicle)
{
	double lat = vehicle->lat / 1e7d;
	double lon = vehicle->lon / 1e7d;
	float	alt = vehicle->alt;

	return inside(lat, lon, vehicle->alt);
}

bool Geofence::inside(double lat, double lon, float altitude)
{
	/* Return true if geofence is disabled */
	if (param_geofence_on.get() != 1)
		return true;

	if (valid()) {

		if (!isEmpty()) {
			/* Vertical check */
			if (altitude > _altitude_max || altitude < _altitude_min)
				return false;

			/*Horizontal check */
			/* Adaptation of algorithm originally presented as
			 * PNPOLY - Point Inclusion in Polygon Test
			 * W. Randolph Franklin (WRF) */

			bool c = false;

			struct fence_vertex_s temp_vertex_i;
			struct fence_vertex_s temp_vertex_j;

			/* Red until fence is finished */
			for (unsigned i = 0, j = _verticesCount - 1; i < _verticesCount; j = i++) {
				if (dm_read(DM_KEY_FENCE_POINTS, i, &temp_vertex_i, sizeof(struct fence_vertex_s)) != sizeof(struct fence_vertex_s)) {
					break;
				}
				if (dm_read(DM_KEY_FENCE_POINTS, j, &temp_vertex_j, sizeof(struct fence_vertex_s)) != sizeof(struct fence_vertex_s)) {
					break;
				}

				// skip vertex 0 (return point)
				if (((temp_vertex_i.lon) >= lon != (temp_vertex_j.lon >= lon)) &&
							(lat <= (temp_vertex_j.lat - temp_vertex_i.lat) * (lon - temp_vertex_i.lon) /
							 (temp_vertex_j.lon - temp_vertex_i.lon) + temp_vertex_i.lat)) {
							c = !c;
				}

			}

			return c;
		} else {
			/* Empty fence --> accept all points */
			return true;
		}

	} else {
		/* Invalid fence --> accept all points */
		return true;
	}
}

bool
Geofence::valid()
{
	// NULL fence is valid
	if (isEmpty())
		return true;

	// Otherwise
	if ((_verticesCount < 4) || (_verticesCount > GEOFENCE_MAX_VERTICES)) {
		warnx("Fence must have at least 3 sides and not more than %d", GEOFENCE_MAX_VERTICES - 1);
		return false;
	}

	return true;
}

void
Geofence::addPoint(int argc, char *argv[])
{
	int ix, last;
	double lon, lat;
	struct fence_vertex_s vertex;
	char *end;

	if ((argc == 1) && (strcmp("-clear", argv[0]) == 0)) {
		dm_clear(DM_KEY_FENCE_POINTS);
		publishFence(0);
		return;
	}

	if (argc < 3)
		errx(1, "Specify: -clear | sequence latitude longitude [-publish]");

	ix = atoi(argv[0]);
	if (ix >= DM_KEY_FENCE_POINTS_MAX)
		errx(1, "Sequence must be less than %d", DM_KEY_FENCE_POINTS_MAX);

	lat = strtod(argv[1], &end);
	lon = strtod(argv[2], &end);

	last = 0;
	if ((argc > 3) && (strcmp(argv[3], "-publish") == 0))
		last = 1;

	vertex.lat = (float)lat;
	vertex.lon = (float)lon;

	if (dm_write(DM_KEY_FENCE_POINTS, ix, DM_PERSIST_POWER_ON_RESET, &vertex, sizeof(vertex)) == sizeof(vertex)) {
		if (last)
			publishFence((unsigned)ix + 1);
		return;
	}

	errx(1, "can't store fence point");
}

void
Geofence::publishFence(unsigned vertices)
{
	if (_fence_pub == -1)
		_fence_pub = orb_advertise(ORB_ID(fence), &vertices);
	else
		orb_publish(ORB_ID(fence), _fence_pub, &vertices);
}

int
Geofence::loadFromFile(const char *filename)
{
	FILE		*fp;
	char		line[120];
	int			pointCounter = 0;
	bool		gotVertical = false;
	const char commentChar = '#';

	/* Make sure no data is left in the datamanager */
	clearDm();

	/* open the mixer definition file */
	fp = fopen(GEOFENCE_FILENAME, "r");
	if (fp == NULL) {
		return ERROR;
	}

	/* create geofence points from valid lines and store in DM */
	for (;;) {

		/* get a line, bail on error/EOF */
		if (fgets(line, sizeof(line), fp) == NULL)
			break;

		/* Trim leading whitespace */
		size_t textStart = 0;
		while((textStart < sizeof(line)/sizeof(char)) && isspace(line[textStart])) textStart++;

		/* if the line starts with #, skip */
		if (line[textStart] == commentChar)
			continue;

		if (gotVertical) {
			/* Parse the line as a geofence point */
			struct fence_vertex_s vertex;

			/* if the line starts with DMS, this means that the coordinate is given as degree minute second instead of decimal degrees */
			if (line[textStart] == 'D' && line[textStart + 1] == 'M' && line[textStart + 2] == 'S') {
				/* Handle degree minute second format */
				float lat_d, lat_m, lat_s, lon_d, lon_m, lon_s;

				if (sscanf(line, "DMS %f %f %f %f %f %f", &lat_d, &lat_m, &lat_s, &lon_d, &lon_m, &lon_s) != 6)
					return ERROR;

//				warnx("Geofence DMS: %.5f %.5f %.5f ; %.5f %.5f %.5f", (double)lat_d, (double)lat_m, (double)lat_s, (double)lon_d, (double)lon_m, (double)lon_s);

				vertex.lat = lat_d + lat_m/60.0f + lat_s/3600.0f;
				vertex.lon = lon_d + lon_m/60.0f + lon_s/3600.0f;

			} else {
				/* Handle decimal degree format */

				if (sscanf(line, "%f %f", &(vertex.lat), &(vertex.lon)) != 2)
					return ERROR;
			}

			if (dm_write(DM_KEY_FENCE_POINTS, pointCounter, DM_PERSIST_POWER_ON_RESET, &vertex, sizeof(vertex)) != sizeof(vertex))
								return ERROR;

			warnx("Geofence: point: %d, lat %.5f: lon: %.5f", pointCounter,  (double)vertex.lat, (double)vertex.lon);

			pointCounter++;
		} else {
			/* Parse the line as the vertical limits */
			if (sscanf(line, "%f %f", &_altitude_min, &_altitude_max) != 2)
				return ERROR;

			warnx("Geofence: alt min: %.4f, alt_max: %.4f", (double)_altitude_min, (double)_altitude_max);
			gotVertical = true;
		}

	}

	fclose(fp);

	/* Check if import was successful */
	if(gotVertical && pointCounter > 0)
	{
		_verticesCount = pointCounter;
		warnx("Geofence: imported successfully");
	} else {
		warnx("Geofence: import error");
	}

	return ERROR;
}

int Geofence::clearDm()
{
	dm_clear(DM_KEY_FENCE_POINTS);
}
<commit_msg>geofence: fixed warnings<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2013 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/
/**
 * @file geofence.cpp
 * Provides functions for handling the geofence
 *
 * @author Jean Cyr <jean.m.cyr@gmail.com>
 * @author Thomas Gubler <thomasgubler@gmail.com>
 */
#include "geofence.h"

#include <uORB/topics/vehicle_global_position.h>
#include <string.h>
#include <dataman/dataman.h>
#include <systemlib/err.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <nuttx/config.h>
#include <unistd.h>

/* Oddly, ERROR is not defined for C++ */
#ifdef ERROR
# undef ERROR
#endif
static const int ERROR = -1;

Geofence::Geofence() :
		SuperBlock(NULL, "GF"),
		_fence_pub(-1),
		_altitude_min(0),
		_altitude_max(0),
		_verticesCount(0),
		param_geofence_on(this, "ON")
{
	/* Load initial params */
	updateParams();
}

Geofence::~Geofence()
{

}

bool Geofence::inside(const struct vehicle_global_position_s *vehicle)
{
	double lat = vehicle->lat / 1e7d;
	double lon = vehicle->lon / 1e7d;
	//float	alt = vehicle->alt;

	return inside(lat, lon, vehicle->alt);
}

bool Geofence::inside(double lat, double lon, float altitude)
{
	/* Return true if geofence is disabled */
	if (param_geofence_on.get() != 1)
		return true;

	if (valid()) {

		if (!isEmpty()) {
			/* Vertical check */
			if (altitude > _altitude_max || altitude < _altitude_min)
				return false;

			/*Horizontal check */
			/* Adaptation of algorithm originally presented as
			 * PNPOLY - Point Inclusion in Polygon Test
			 * W. Randolph Franklin (WRF) */

			bool c = false;

			struct fence_vertex_s temp_vertex_i;
			struct fence_vertex_s temp_vertex_j;

			/* Red until fence is finished */
			for (unsigned i = 0, j = _verticesCount - 1; i < _verticesCount; j = i++) {
				if (dm_read(DM_KEY_FENCE_POINTS, i, &temp_vertex_i, sizeof(struct fence_vertex_s)) != sizeof(struct fence_vertex_s)) {
					break;
				}
				if (dm_read(DM_KEY_FENCE_POINTS, j, &temp_vertex_j, sizeof(struct fence_vertex_s)) != sizeof(struct fence_vertex_s)) {
					break;
				}

				// skip vertex 0 (return point)
				if (((double)temp_vertex_i.lon >= lon) != ((double)temp_vertex_j.lon >= lon) &&
							(lat <= (double)(temp_vertex_j.lat - temp_vertex_i.lat) * (lon - (double)temp_vertex_i.lon) /
							 (double)(temp_vertex_j.lon - temp_vertex_i.lon) + (double)temp_vertex_i.lat)) {
							c = !c;
				}

			}

			return c;
		} else {
			/* Empty fence --> accept all points */
			return true;
		}

	} else {
		/* Invalid fence --> accept all points */
		return true;
	}
}

bool
Geofence::valid()
{
	// NULL fence is valid
	if (isEmpty())
		return true;

	// Otherwise
	if ((_verticesCount < 4) || (_verticesCount > GEOFENCE_MAX_VERTICES)) {
		warnx("Fence must have at least 3 sides and not more than %d", GEOFENCE_MAX_VERTICES - 1);
		return false;
	}

	return true;
}

void
Geofence::addPoint(int argc, char *argv[])
{
	int ix, last;
	double lon, lat;
	struct fence_vertex_s vertex;
	char *end;

	if ((argc == 1) && (strcmp("-clear", argv[0]) == 0)) {
		dm_clear(DM_KEY_FENCE_POINTS);
		publishFence(0);
		return;
	}

	if (argc < 3)
		errx(1, "Specify: -clear | sequence latitude longitude [-publish]");

	ix = atoi(argv[0]);
	if (ix >= DM_KEY_FENCE_POINTS_MAX)
		errx(1, "Sequence must be less than %d", DM_KEY_FENCE_POINTS_MAX);

	lat = strtod(argv[1], &end);
	lon = strtod(argv[2], &end);

	last = 0;
	if ((argc > 3) && (strcmp(argv[3], "-publish") == 0))
		last = 1;

	vertex.lat = (float)lat;
	vertex.lon = (float)lon;

	if (dm_write(DM_KEY_FENCE_POINTS, ix, DM_PERSIST_POWER_ON_RESET, &vertex, sizeof(vertex)) == sizeof(vertex)) {
		if (last)
			publishFence((unsigned)ix + 1);
		return;
	}

	errx(1, "can't store fence point");
}

void
Geofence::publishFence(unsigned vertices)
{
	if (_fence_pub == -1)
		_fence_pub = orb_advertise(ORB_ID(fence), &vertices);
	else
		orb_publish(ORB_ID(fence), _fence_pub, &vertices);
}

int
Geofence::loadFromFile(const char *filename)
{
	FILE		*fp;
	char		line[120];
	int			pointCounter = 0;
	bool		gotVertical = false;
	const char commentChar = '#';

	/* Make sure no data is left in the datamanager */
	clearDm();

	/* open the mixer definition file */
	fp = fopen(GEOFENCE_FILENAME, "r");
	if (fp == NULL) {
		return ERROR;
	}

	/* create geofence points from valid lines and store in DM */
	for (;;) {

		/* get a line, bail on error/EOF */
		if (fgets(line, sizeof(line), fp) == NULL)
			break;

		/* Trim leading whitespace */
		size_t textStart = 0;
		while((textStart < sizeof(line)/sizeof(char)) && isspace(line[textStart])) textStart++;

		/* if the line starts with #, skip */
		if (line[textStart] == commentChar)
			continue;

		if (gotVertical) {
			/* Parse the line as a geofence point */
			struct fence_vertex_s vertex;

			/* if the line starts with DMS, this means that the coordinate is given as degree minute second instead of decimal degrees */
			if (line[textStart] == 'D' && line[textStart + 1] == 'M' && line[textStart + 2] == 'S') {
				/* Handle degree minute second format */
				float lat_d, lat_m, lat_s, lon_d, lon_m, lon_s;

				if (sscanf(line, "DMS %f %f %f %f %f %f", &lat_d, &lat_m, &lat_s, &lon_d, &lon_m, &lon_s) != 6)
					return ERROR;

//				warnx("Geofence DMS: %.5f %.5f %.5f ; %.5f %.5f %.5f", (double)lat_d, (double)lat_m, (double)lat_s, (double)lon_d, (double)lon_m, (double)lon_s);

				vertex.lat = lat_d + lat_m/60.0f + lat_s/3600.0f;
				vertex.lon = lon_d + lon_m/60.0f + lon_s/3600.0f;

			} else {
				/* Handle decimal degree format */

				if (sscanf(line, "%f %f", &(vertex.lat), &(vertex.lon)) != 2)
					return ERROR;
			}

			if (dm_write(DM_KEY_FENCE_POINTS, pointCounter, DM_PERSIST_POWER_ON_RESET, &vertex, sizeof(vertex)) != sizeof(vertex))
								return ERROR;

			warnx("Geofence: point: %d, lat %.5f: lon: %.5f", pointCounter,  (double)vertex.lat, (double)vertex.lon);

			pointCounter++;
		} else {
			/* Parse the line as the vertical limits */
			if (sscanf(line, "%f %f", &_altitude_min, &_altitude_max) != 2)
				return ERROR;

			warnx("Geofence: alt min: %.4f, alt_max: %.4f", (double)_altitude_min, (double)_altitude_max);
			gotVertical = true;
		}

	}

	fclose(fp);

	/* Check if import was successful */
	if(gotVertical && pointCounter > 0)
	{
		_verticesCount = pointCounter;
		warnx("Geofence: imported successfully");
	} else {
		warnx("Geofence: import error");
	}

	return ERROR;
}

int Geofence::clearDm()
{
	dm_clear(DM_KEY_FENCE_POINTS);
	return OK;
}
<|endoftext|>


<commit_before>#include "PosTexShader.hpp"

using namespace morda;

decltype(PosTexShader::quadFanTexCoords) PosTexShader::quadFanTexCoords = {{
	Vec2f(0, 0), Vec2f(1, 0), Vec2f(1, 1), Vec2f(0, 1)
}};

PosTexShader::PosTexShader() :
		Shader(
				R"qwertyuiop(
						UNIFORM MAT4F matrix;

						ATTRIB_BEGIN
							ATTRIB VEC4F pos;
							ATTRIB VEC2F texCoord0;
						ATTRIB_END

						VARYING_BEGIN
							VARYING VEC2F tc0;
						VARYING_END

						VERTEX_MAIN_BEGIN
							gl_Position = matrix * pos;
							tc0 = texCoord0;
						VERTEX_MAIN_END
					)qwertyuiop",
				R"qwertyuiop(
						UNIFORM sampler2D textureNumber;

						VARYING VEC2F tc0;

						FRAG_MAIN_BEGIN
							gl_FragColor = texture2D(textureNumber, tc0);
						FRAG_MAIN_END
					)qwertyuiop"
			),
		texCoordAttr(this->getAttribute("texCoord0")),
		texNumberUniform(this->getUniform("textureNumber"))
{}
<commit_msg>stuff<commit_after>#include "PosTexShader.hpp"

using namespace morda;

decltype(PosTexShader::quadFanTexCoords) PosTexShader::quadFanTexCoords = {{
	Vec2f(0, 0), Vec2f(1, 0), Vec2f(1, 1), Vec2f(0, 1)
}};

PosTexShader::PosTexShader() :
		Shader(
				R"qwertyuiop(
						UNIFORM MAT4F matrix;

						ATTRIB_BEGIN
							ATTRIB VEC4F pos;
							ATTRIB VEC2F texCoord0;
						ATTRIB_END

						VARYING_BEGIN
							VARYING VEC2F tc0;
						VARYING_END

						VERTEX_MAIN_BEGIN
							gl_Position = matrix * pos;
							tc0 = texCoord0;
						VERTEX_MAIN_END
					)qwertyuiop",
				R"qwertyuiop(
						UNIFORM sampler2D textureNumber;

						VARYING_BEGIN
							VARYING VEC2F tc0;
						VARYING_END

						FRAG_MAIN_BEGIN
							gl_FragColor = texture2D(textureNumber, tc0);
						FRAG_MAIN_END
					)qwertyuiop"
			),
		texCoordAttr(this->getAttribute("texCoord0")),
		texNumberUniform(this->getUniform("textureNumber"))
{}
<|endoftext|>


<commit_before>//
// This file is part of the Marble Virtual Globe.
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2010      Dennis Nienhüser <earthwings@gentoo.org>
//

#include "RouteRequest.h"

#include "GeoDataLineString.h"
#include "GeoDataPlacemark.h"
#include "GeoDataData.h"
#include "GeoDataExtendedData.h"
#include "MarbleDirs.h"

#include <QMap>
#include <QPainter>

namespace Marble
{

struct PixmapElement
{
    int index;

    int size;

    explicit PixmapElement( int index=-1, int size=0 );

    bool operator < ( const PixmapElement &other ) const;
};

class RouteRequestPrivate
{
public:
    QVector<GeoDataPlacemark> m_route;

    QMap<PixmapElement, QPixmap> m_pixmapCache;

    RoutingProfile m_routingProfile;

    /** Determines a suitable index for inserting a via point */
    int viaIndex( const GeoDataCoordinates &position ) const;
};

PixmapElement::PixmapElement( int index_, int size_ ) :
    index( index_ ), size( size_ )
{
    // nothing to do
}

bool PixmapElement::operator <(const PixmapElement &other) const
{
    return index < other.index || size < other.size;
}

int RouteRequestPrivate::viaIndex( const GeoDataCoordinates &position ) const
{
    /** @todo: Works, but does not look elegant at all */

    // Iterates over all ordered trip point pairs (P,Q) and finds the triple
    // (P,position,Q) or (P,Q,position) with minimum length
    qreal minLength = -1.0;
    int result = 0;
    GeoDataLineString viaFirst;
    GeoDataLineString viaSecond;
    for ( int i = 0; i < m_route.size(); ++i ) {
        Q_ASSERT( viaFirst.size() < 4 && viaSecond.size() < 4 );
        if ( viaFirst.size() == 3 ) {
            viaFirst.remove( 0 );
            viaFirst.remove( 0 );
        }

        if ( viaSecond.size() == 3 ) {
            viaSecond.remove( 0 );
            viaSecond.remove( 0 );
        }

        if ( viaFirst.size() == 1 ) {
            viaFirst.append( position );
        }

        viaFirst.append( m_route[i].coordinate() );
        viaSecond.append( m_route[i].coordinate() );

        if ( viaSecond.size() == 2 ) {
            viaSecond.append( position );
        }

        if ( viaFirst.size() == 3 ) {
            qreal len = viaFirst.length( EARTH_RADIUS );
            if ( minLength < 0.0 || len < minLength ) {
                minLength = len;
                result = i;
            }
        }

        /** @todo: Assumes that destination is the last point */
        if ( viaSecond.size() == 3 && i + 1 < m_route.size() ) {
            qreal len = viaSecond.length( EARTH_RADIUS );
            if ( minLength < 0.0 || len < minLength ) {
                minLength = len;
                result = i + 1;
            }
        }
    }

    Q_ASSERT( 0 <= result && result <= m_route.size() );
    return result;
}

RouteRequest::RouteRequest( QObject *parent ) :
        QObject( parent ), d( new RouteRequestPrivate )
{
    // nothing to do
}

RouteRequest::~RouteRequest()
{
    delete d;
}

int RouteRequest::size() const
{
    return d->m_route.size();
}

GeoDataCoordinates RouteRequest::source() const
{
    GeoDataCoordinates result;
    if ( d->m_route.size() ) {
        result = d->m_route.first().coordinate();
    }
    return result;
}

GeoDataCoordinates RouteRequest::destination() const
{
    GeoDataCoordinates result;
    if ( d->m_route.size() ) {
        result = d->m_route.last().coordinate();
    }
    return result;
}

GeoDataCoordinates RouteRequest::at( int position ) const
{
    return d->m_route.at( position ).coordinate();
}

QPixmap RouteRequest::pixmap(int position, int size, int margin ) const
{
    PixmapElement const element( position, size );
    if ( d->m_pixmapCache.contains( element ) ) {
        return d->m_pixmapCache[element];
    }

    // Transparent background
    bool smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
    int const imageSize = size > 0 ? size : ( smallScreen ? 32 : 16 );
    QImage result( imageSize, imageSize, QImage::Format_ARGB32_Premultiplied );
    result.fill( qRgba( 0, 0, 0, 0 ) );

    // Paint a colored circle
    QPainter painter( &result );
    painter.setRenderHint( QPainter::Antialiasing, true );
    painter.setPen( QColor( Qt::black ) );
    bool const isVisited = visited( position );
    QColor const backgroundColor = isVisited ? Oxygen::aluminumGray4 : Oxygen::forestGreen4;
    painter.setBrush( QBrush( backgroundColor ) );
    painter.setPen( Qt::black );
    int const iconSize = imageSize - 2 * margin;
    painter.drawEllipse( margin, margin, iconSize, iconSize );

    char text = char( 'A' + position );

    // Choose a suitable font size
    QFont font = painter.font();
    int fontSize = 20;
    while ( fontSize-- > 0 ) {
        font.setPointSize( fontSize );
        QFontMetrics fontMetric( font );
        if ( fontMetric.width( text ) <= iconSize && fontMetric.height( ) <= iconSize ) {
            break;
        }
    }

    Q_ASSERT( fontSize );
    font.setPointSize( fontSize );
    painter.setFont( font );

    // Paint a character denoting the position (0=A, 1=B, 2=C, ...)
    painter.drawText( 0, 0, imageSize, imageSize, Qt::AlignCenter, QString( text ) );

    d->m_pixmapCache.insert( element, QPixmap::fromImage( result ) );
    return pixmap( position, size );
}

void RouteRequest::clear()
{
    for ( int i=d->m_route.size()-1; i>=0; --i ) {
        remove( i );
    }
}

void RouteRequest::insert( int index, const GeoDataCoordinates &coordinates, const QString &name )
{
    GeoDataPlacemark placemark;
    placemark.setCoordinate( coordinates );
    d->m_route.insert( index, placemark );
    setName( index, name );
    emit positionAdded( index );
}

void RouteRequest::append( const GeoDataCoordinates &coordinates, const QString &name )
{
    GeoDataPlacemark placemark;
    placemark.setCoordinate( coordinates );
    placemark.setName( name );
    append( placemark );
}

void RouteRequest::append( const GeoDataPlacemark &placemark )
{
    d->m_route.append( placemark );
    emit positionAdded( d->m_route.size()-1 );
}

void RouteRequest::remove( int index )
{
    if ( index >= 0 && index < d->m_route.size() ) {
        d->m_route.remove( index );
        emit positionRemoved( index );
    }
}

void RouteRequest::addVia( const GeoDataCoordinates &position )
{
    int index = d->viaIndex( position );
    GeoDataPlacemark placemark;
    placemark.setCoordinate( position );
    d->m_route.insert( index, placemark );
    emit positionAdded( index );
}

void RouteRequest::setPosition( int index, const GeoDataCoordinates &position, const QString &name )
{
    if ( index >= 0 && index < d->m_route.size() ) {
        GeoDataPlacemark placemark;
        placemark.setCoordinate( position );
        d->m_route[index] = placemark;
        setName( index, name );
        setVisited( index, false );
        emit positionChanged( index, position );
    }
}

void RouteRequest::setName( int index, const QString &name )
{
    if ( index >= 0 && index < d->m_route.size() ) {
        d->m_route[index].setName( name );
    }
}

QString RouteRequest::name( int index ) const
{
    QString result;
    if ( index >= 0 && index < d->m_route.size() ) {
        result = d->m_route[index].name();
    }
    return result;
}

void RouteRequest::setVisited( int index, bool visited )
{
    if ( index >= 0 && index < d->m_route.size() ) {
        d->m_route[index].extendedData().addValue( GeoDataData( "routingVisited", visited ) );
        QMap<PixmapElement, QPixmap>::iterator iter = d->m_pixmapCache.begin();
        while ( iter != d->m_pixmapCache.end() ) {
             if ( iter.key().index == index ) {
                 iter = d->m_pixmapCache.erase( iter );
             } else {
                 ++iter;
             }
         }
        emit positionChanged( index, d->m_route[index].coordinate() );
    }
}

bool RouteRequest::visited( int index ) const
{
    bool visited = false;
    if ( index >= 0 && index < d->m_route.size() ) {
        if ( d->m_route[index].extendedData().contains( "routingVisited" ) ) {
            visited = d->m_route[index].extendedData().value( "routingVisited" ).value().toBool();
        }
    }
    return visited;
}

void RouteRequest::reverse()
{
    int const total = d->m_route.size();
    int const upper = total / 2;
    for( int i=0; i<upper; ++i ) {
        qSwap( d->m_route[i], d->m_route[total-i-1] );
        setVisited( i, false );
        setVisited( total-i-1, false );
    }
}

void RouteRequest::setRoutingProfile( const RoutingProfile &profile )
{
    d->m_routingProfile = profile;
    emit routingProfileChanged();
}

RoutingProfile RouteRequest::routingProfile() const
{
    return d->m_routingProfile;
}

GeoDataPlacemark &RouteRequest::operator []( int index )
{
    return d->m_route[index];
}

const GeoDataPlacemark &RouteRequest::operator [](int index) const
{
    return d->m_route[index];
}

} // namespace Marble

#include "RouteRequest.moc"
<commit_msg>avoid RouteRequest::pixmap() calling itself<commit_after>//
// This file is part of the Marble Virtual Globe.
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2010      Dennis Nienhüser <earthwings@gentoo.org>
//

#include "RouteRequest.h"

#include "GeoDataLineString.h"
#include "GeoDataPlacemark.h"
#include "GeoDataData.h"
#include "GeoDataExtendedData.h"
#include "MarbleDirs.h"

#include <QMap>
#include <QPainter>

namespace Marble
{

struct PixmapElement
{
    int index;

    int size;

    explicit PixmapElement( int index=-1, int size=0 );

    bool operator < ( const PixmapElement &other ) const;
};

class RouteRequestPrivate
{
public:
    QVector<GeoDataPlacemark> m_route;

    QMap<PixmapElement, QPixmap> m_pixmapCache;

    RoutingProfile m_routingProfile;

    /** Determines a suitable index for inserting a via point */
    int viaIndex( const GeoDataCoordinates &position ) const;
};

PixmapElement::PixmapElement( int index_, int size_ ) :
    index( index_ ), size( size_ )
{
    // nothing to do
}

bool PixmapElement::operator <(const PixmapElement &other) const
{
    return index < other.index || size < other.size;
}

int RouteRequestPrivate::viaIndex( const GeoDataCoordinates &position ) const
{
    /** @todo: Works, but does not look elegant at all */

    // Iterates over all ordered trip point pairs (P,Q) and finds the triple
    // (P,position,Q) or (P,Q,position) with minimum length
    qreal minLength = -1.0;
    int result = 0;
    GeoDataLineString viaFirst;
    GeoDataLineString viaSecond;
    for ( int i = 0; i < m_route.size(); ++i ) {
        Q_ASSERT( viaFirst.size() < 4 && viaSecond.size() < 4 );
        if ( viaFirst.size() == 3 ) {
            viaFirst.remove( 0 );
            viaFirst.remove( 0 );
        }

        if ( viaSecond.size() == 3 ) {
            viaSecond.remove( 0 );
            viaSecond.remove( 0 );
        }

        if ( viaFirst.size() == 1 ) {
            viaFirst.append( position );
        }

        viaFirst.append( m_route[i].coordinate() );
        viaSecond.append( m_route[i].coordinate() );

        if ( viaSecond.size() == 2 ) {
            viaSecond.append( position );
        }

        if ( viaFirst.size() == 3 ) {
            qreal len = viaFirst.length( EARTH_RADIUS );
            if ( minLength < 0.0 || len < minLength ) {
                minLength = len;
                result = i;
            }
        }

        /** @todo: Assumes that destination is the last point */
        if ( viaSecond.size() == 3 && i + 1 < m_route.size() ) {
            qreal len = viaSecond.length( EARTH_RADIUS );
            if ( minLength < 0.0 || len < minLength ) {
                minLength = len;
                result = i + 1;
            }
        }
    }

    Q_ASSERT( 0 <= result && result <= m_route.size() );
    return result;
}

RouteRequest::RouteRequest( QObject *parent ) :
        QObject( parent ), d( new RouteRequestPrivate )
{
    // nothing to do
}

RouteRequest::~RouteRequest()
{
    delete d;
}

int RouteRequest::size() const
{
    return d->m_route.size();
}

GeoDataCoordinates RouteRequest::source() const
{
    GeoDataCoordinates result;
    if ( d->m_route.size() ) {
        result = d->m_route.first().coordinate();
    }
    return result;
}

GeoDataCoordinates RouteRequest::destination() const
{
    GeoDataCoordinates result;
    if ( d->m_route.size() ) {
        result = d->m_route.last().coordinate();
    }
    return result;
}

GeoDataCoordinates RouteRequest::at( int position ) const
{
    return d->m_route.at( position ).coordinate();
}

QPixmap RouteRequest::pixmap(int position, int size, int margin ) const
{
    PixmapElement const element( position, size );

    if ( !d->m_pixmapCache.contains( element ) ) {
        // Transparent background
        bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
        int const imageSize = size > 0 ? size : ( smallScreen ? 32 : 16 );
        QImage result( imageSize, imageSize, QImage::Format_ARGB32_Premultiplied );
        result.fill( qRgba( 0, 0, 0, 0 ) );

        // Paint a colored circle
        QPainter painter( &result );
        painter.setRenderHint( QPainter::Antialiasing, true );
        painter.setPen( QColor( Qt::black ) );
        bool const isVisited = visited( position );
        QColor const backgroundColor = isVisited ? Oxygen::aluminumGray4 : Oxygen::forestGreen4;
        painter.setBrush( QBrush( backgroundColor ) );
        painter.setPen( Qt::black );
        int const iconSize = imageSize - 2 * margin;
        painter.drawEllipse( margin, margin, iconSize, iconSize );

        char const text = char( 'A' + position );

        // Choose a suitable font size
        QFont font = painter.font();
        int fontSize = 20;
        while ( fontSize-- > 0 ) {
            font.setPointSize( fontSize );
            QFontMetrics const fontMetric( font );
            if ( fontMetric.width( text ) <= iconSize && fontMetric.height( ) <= iconSize ) {
                break;
            }
        }

        Q_ASSERT( fontSize );
        font.setPointSize( fontSize );
        painter.setFont( font );

        // Paint a character denoting the position (0=A, 1=B, 2=C, ...)
        painter.drawText( 0, 0, imageSize, imageSize, Qt::AlignCenter, QString( text ) );

        d->m_pixmapCache.insert( element, QPixmap::fromImage( result ) );
    }

    return d->m_pixmapCache[element];
}

void RouteRequest::clear()
{
    for ( int i=d->m_route.size()-1; i>=0; --i ) {
        remove( i );
    }
}

void RouteRequest::insert( int index, const GeoDataCoordinates &coordinates, const QString &name )
{
    GeoDataPlacemark placemark;
    placemark.setCoordinate( coordinates );
    d->m_route.insert( index, placemark );
    setName( index, name );
    emit positionAdded( index );
}

void RouteRequest::append( const GeoDataCoordinates &coordinates, const QString &name )
{
    GeoDataPlacemark placemark;
    placemark.setCoordinate( coordinates );
    placemark.setName( name );
    append( placemark );
}

void RouteRequest::append( const GeoDataPlacemark &placemark )
{
    d->m_route.append( placemark );
    emit positionAdded( d->m_route.size()-1 );
}

void RouteRequest::remove( int index )
{
    if ( index >= 0 && index < d->m_route.size() ) {
        d->m_route.remove( index );
        emit positionRemoved( index );
    }
}

void RouteRequest::addVia( const GeoDataCoordinates &position )
{
    int index = d->viaIndex( position );
    GeoDataPlacemark placemark;
    placemark.setCoordinate( position );
    d->m_route.insert( index, placemark );
    emit positionAdded( index );
}

void RouteRequest::setPosition( int index, const GeoDataCoordinates &position, const QString &name )
{
    if ( index >= 0 && index < d->m_route.size() ) {
        GeoDataPlacemark placemark;
        placemark.setCoordinate( position );
        d->m_route[index] = placemark;
        setName( index, name );
        setVisited( index, false );
        emit positionChanged( index, position );
    }
}

void RouteRequest::setName( int index, const QString &name )
{
    if ( index >= 0 && index < d->m_route.size() ) {
        d->m_route[index].setName( name );
    }
}

QString RouteRequest::name( int index ) const
{
    QString result;
    if ( index >= 0 && index < d->m_route.size() ) {
        result = d->m_route[index].name();
    }
    return result;
}

void RouteRequest::setVisited( int index, bool visited )
{
    if ( index >= 0 && index < d->m_route.size() ) {
        d->m_route[index].extendedData().addValue( GeoDataData( "routingVisited", visited ) );
        QMap<PixmapElement, QPixmap>::iterator iter = d->m_pixmapCache.begin();
        while ( iter != d->m_pixmapCache.end() ) {
             if ( iter.key().index == index ) {
                 iter = d->m_pixmapCache.erase( iter );
             } else {
                 ++iter;
             }
         }
        emit positionChanged( index, d->m_route[index].coordinate() );
    }
}

bool RouteRequest::visited( int index ) const
{
    bool visited = false;
    if ( index >= 0 && index < d->m_route.size() ) {
        if ( d->m_route[index].extendedData().contains( "routingVisited" ) ) {
            visited = d->m_route[index].extendedData().value( "routingVisited" ).value().toBool();
        }
    }
    return visited;
}

void RouteRequest::reverse()
{
    int const total = d->m_route.size();
    int const upper = total / 2;
    for( int i=0; i<upper; ++i ) {
        qSwap( d->m_route[i], d->m_route[total-i-1] );
        setVisited( i, false );
        setVisited( total-i-1, false );
    }
}

void RouteRequest::setRoutingProfile( const RoutingProfile &profile )
{
    d->m_routingProfile = profile;
    emit routingProfileChanged();
}

RoutingProfile RouteRequest::routingProfile() const
{
    return d->m_routingProfile;
}

GeoDataPlacemark &RouteRequest::operator []( int index )
{
    return d->m_route[index];
}

const GeoDataPlacemark &RouteRequest::operator [](int index) const
{
    return d->m_route[index];
}

} // namespace Marble

#include "RouteRequest.moc"
<|endoftext|>