<commit_before>#ifndef VSMC_RNG_GSL_RNG_HPP
#define VSMC_RNG_GSL_RNG_HPP

#include <vsmc/rng/generator_wrapper.hpp>
#include <gsl/gsl_rng.h>

#define VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(Name, pointer) \
    template <> struct GSLRngTypePointer< GSL_RNG_TYPE_##Name >              \
    {static const gsl_rng_type *get () {return gsl_rng_##pointer ;}};

#define VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(Name, Min, Max) \
    template <> struct GSLRngMinMaxTrait< GSL_RNG_TYPE_##Name >              \
    {                                                                        \
        static VSMC_CONSTEXPR const uint32_t _Min =                          \
        static_cast<uint32_t>(Min##UL);                                      \
        static VSMC_CONSTEXPR const uint32_t _Max =                          \
        static_cast<uint32_t>(Max##UL);                                      \
        static VSMC_CONSTEXPR uint32_t min VSMC_MNE () {return _Min;}        \
        static VSMC_CONSTEXPR uint32_t max VSMC_MNE () {return _Max;}        \
    };

namespace vsmc {

/// \brief GSL RNG algorithms
/// \ingroup GSLRNG
enum GSLRngType {
    GSL_RNG_TYPE_MT19937,    ///< gsl_rng_mt19937
    GSL_RNG_TYPE_RANLXS0,    ///< gsl_rng_ranlxs0
    GSL_RNG_TYPE_RANLXS1,    ///< gsl_rng_ranlxs1
    GSL_RNG_TYPE_RANLXS2,    ///< gsl_rng_ranlxs2
    GSL_RNG_TYPE_RANLXD1,    ///< gsl_rng_ranlxd1
    GSL_RNG_TYPE_RANLXD2,    ///< gsl_rng_ranlxd2
    GSL_RNG_TYPE_RANLUX,     ///< gsl_rng_ranlux
    GSL_RNG_TYPE_RANLUX389,  ///< gsl_rng_ranlux389
    GSL_RNG_TYPE_CMRG,       ///< gsl_rng_cmrg
    GSL_RNG_TYPE_MRG,        ///< gsl_rng_mrg
    GSL_RNG_TYPE_TAUS,       ///< gsl_rng_taus
    GSL_RNG_TYPE_TAUS2,      ///< gsl_rng_taus2
    GSL_RNG_TYPE_GFSR4       ///< gsl_rng_gfsr4
}; // enum GSLRngType

namespace internal {

template <GSLRngType> struct GSLRngTypePointer;

VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(MT19937,   mt19937)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLXS0,   ranlxs0)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLXS1,   ranlxs1)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLXS2,   ranlxs2)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLXD1,   ranlxd1)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLXD2,   ranlxd2)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLUX,    ranlux)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLUX389, ranlux389)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(CMRG,      cmrg)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(MRG,       mrg)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(TAUS,      taus)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(TAUS2,     taus2)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(GFSR4,     gfsr4)

template <GSLRngType RngType> struct GSLRngMinMaxTrait;

VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(MT19937,   0, 4294967295)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLXS0,   0, 16777215)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLXS1,   0, 16777215)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLXS2,   0, 16777215)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLXD1,   0, 4294967295)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLXD2,   0, 4294967295)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLUX,    0, 16777215)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLUX389, 0, 16777215)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(CMRG,      0, 2147483646)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(MRG,       0, 2147483646)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(TAUS,      0, 4294967295)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(TAUS2,     0, 4294967295)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(GFSR4,     0, 4294967295)

template <GSLRngType RngType>
class GSLRngGenerator
{
    public :

    GSLRngGenerator () :
        rng_(gsl_rng_alloc(internal::GSLRngTypePointer<RngType>::get())) {}

    GSLRngGenerator (const GSLRngGenerator<RngType> &other) :
        rng_(gsl_rng_clone(other.rng_)) {}

    GSLRngGenerator<RngType> &operator= (const GSLRngGenerator<RngType> &other)
    {
        if (this != &other) {
            if (rng_ != VSMC_NULLPTR)
                gsl_rng_free(rng_);
            rng_ = gsl_rng_memcpy(other.rng_);
        }

        return *this;
    }

#if VSMC_HAS_CXX11_RVALUE_REFERENCES
    GSLRngGenerator (GSLRngGenerator<RngType> &&other) :
        rng_(other.rng_) {other.rng_ = VSMC_NULLPTR;}

    GSLRngGenerator<RngType> &operator= (GSLRngGenerator<RngType> &&other)
    {
        if (this != &other) {
            if (rng_ != VSMC_NULLPTR)
                gsl_rng_free(rng_);
            rng_ = other.rng_;
            other.rng_ = VSMC_NULLPTR;
        }

        return *this;
    }
#endif

    ~GSLRngGenerator ()
    {
        if (rng_ != VSMC_NULLPTR)
            gsl_rng_free(rng_);
    }

    void seed (unsigned long s)
    {
        if (rng_ != VSMC_NULLPTR)
            gsl_rng_set(rng_, s);
    }

    unsigned long generate ()
    {
        if (rng_ != VSMC_NULLPTR)
            return gsl_rng_get(rng_);

        return 0;
    }

    unsigned long min VSMC_MNE () const {return gsl_rng_min(rng_);}

    unsigned long max VSMC_MNE () const {return gsl_rng_max(rng_);}

    private :

    gsl_rng *rng_;
}; // class GSLRngGenerator

} // namespace vsmc::internal

/// \brief GSL RNG Engine
/// \ingroup GSLRNG
template <GSLRngType RngType>
class GSLRngEngine :
    public GeneratorWrapper<uint32_t,
    internal::GSLRngGenerator<RngType>,
    internal::GSLRngMinMaxTrait<RngType> >
{
    typedef GeneratorWrapper<uint32_t,
    internal::GSLRngGenerator<RngType>,
    internal::GSLRngMinMaxTrait<RngType> > base;

    public :

    typedef uint32_t result_type;

    GSLRngEngine (result_type s = 0) : base(s) {seed(s);}

    template <typename SeedSeq>
    explicit GSLRngEngine (SeedSeq &seq, typename cxx11::enable_if<
            !internal::is_seed_sequence<SeedSeq, result_type>::value>::type * =
            VSMC_NULLPTR) : base(seq) {seed(seq);}

    void seed (result_type s)
    {this->generator().seed(static_cast<unsigned long>(s));}

    template <typename SeedSeq>
    void seed (SeedSeq &seq, typename cxx11::enable_if<
            !internal::is_seed_sequence<SeedSeq, result_type>::value>::type * =
            VSMC_NULLPTR)
    {
        unsigned long s;
        seq.generate(&s, &s + 1);
        this->generator().seed(s);
    }
}; // class GSLRngEngine

/// \brief A Mersenne-Twister pseudoranom number genertor
/// \ingroup GSLRNG
typedef GSLRngEngine<GSL_RNG_TYPE_MT19937> GSL_MT19937;

/// \brief A RANLUX generator with luxury level 0
/// \ingroup GSLRNG
typedef GSLRngEngine<GSL_RNG_TYPE_RANLXS0> GSL_RANLXS0;

/// \brief A RANLUX generator with luxury level 0
/// \ingroup GSLRNG
typedef GSLRngEngine<GSL_RNG_TYPE_RANLXS1> GSL_RANLXS1;

/// \brief A RANLUX generator with luxury level 0
/// \ingroup GSLRNG
typedef GSLRngEngine<GSL_RNG_TYPE_RANLXS2> GSL_RANLXS2;

/// \brief A RANLXS generator with luxury level 1
/// \ingroup GSLRNG
typedef GSLRngEngine<GSL_RNG_TYPE_RANLXD1> GSL_RANLXD1;

/// \brief A RANLXS generator with luxury level 2
/// \ingroup GSLRNG
typedef GSLRngEngine<GSL_RNG_TYPE_RANLXD2> GSL_RANLXD2;

/// \brief A RANLUX generator
/// \ingroup GSLRNG
typedef GSLRngEngine<GSL_RNG_TYPE_RANLUX> GSL_RANLUX;

/// \brief A RANLUX generator with the highest level of randomness
/// \ingroup GSLRNG
typedef GSLRngEngine<GSL_RNG_TYPE_RANLUX389> GSL_RANLUX389;

/// \brief A combined multiple recursive generator
/// \ingroup GSLRNG
typedef GSLRngEngine<GSL_RNG_TYPE_CMRG> GSL_CMRG;

/// \brief A fifth-order multiple recursive generator
/// \ingroup GSL
typedef GSLRngEngine<GSL_RNG_TYPE_MRG> GSL_MRG;

/// \brief A maximally equidistributed combined Tausworthe generator
/// \ingroup GSLRNG
typedef GSLRngEngine<GSL_RNG_TYPE_TAUS> GSL_TAUS;

/// \brief A maximally equidistributed combined Tausworthe generator with
/// improved seeding procedure
/// \ingroup GSLRNG
typedef GSLRngEngine<GSL_RNG_TYPE_TAUS2> GSL_TAUS2;

/// \brief A a lagged-fibonacci alike generator
/// \ingroup GSLRNG
typedef GSLRngEngine<GSL_RNG_TYPE_GFSR4> GSL_GFSR4;

} // namespace vsmc

#endif // VSMC_RNG_GSL_RNG_HPP
<commit_msg>move GSLGenerator to namespace vsmc<commit_after>#ifndef VSMC_RNG_GSL_RNG_HPP
#define VSMC_RNG_GSL_RNG_HPP

#include <vsmc/rng/generator_wrapper.hpp>
#include <gsl/gsl_rng.h>

#define VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(Name, pointer) \
    template <> struct GSLRngTypePointer< GSL_RNG_TYPE_##Name >              \
    {static const gsl_rng_type *get () {return gsl_rng_##pointer ;}};

#define VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(Name, Min, Max) \
    template <> struct GSLRngMinMaxTrait< GSL_RNG_TYPE_##Name >              \
    {                                                                        \
        static VSMC_CONSTEXPR const uint32_t _Min =                          \
        static_cast<uint32_t>(Min##UL);                                      \
        static VSMC_CONSTEXPR const uint32_t _Max =                          \
        static_cast<uint32_t>(Max##UL);                                      \
        static VSMC_CONSTEXPR uint32_t min VSMC_MNE () {return _Min;}        \
        static VSMC_CONSTEXPR uint32_t max VSMC_MNE () {return _Max;}        \
    };

namespace vsmc {

/// \brief GSL RNG algorithms
/// \ingroup GSLRNG
enum GSLRngType {
    GSL_RNG_TYPE_MT19937,    ///< gsl_rng_mt19937
    GSL_RNG_TYPE_RANLXS0,    ///< gsl_rng_ranlxs0
    GSL_RNG_TYPE_RANLXS1,    ///< gsl_rng_ranlxs1
    GSL_RNG_TYPE_RANLXS2,    ///< gsl_rng_ranlxs2
    GSL_RNG_TYPE_RANLXD1,    ///< gsl_rng_ranlxd1
    GSL_RNG_TYPE_RANLXD2,    ///< gsl_rng_ranlxd2
    GSL_RNG_TYPE_RANLUX,     ///< gsl_rng_ranlux
    GSL_RNG_TYPE_RANLUX389,  ///< gsl_rng_ranlux389
    GSL_RNG_TYPE_CMRG,       ///< gsl_rng_cmrg
    GSL_RNG_TYPE_MRG,        ///< gsl_rng_mrg
    GSL_RNG_TYPE_TAUS,       ///< gsl_rng_taus
    GSL_RNG_TYPE_TAUS2,      ///< gsl_rng_taus2
    GSL_RNG_TYPE_GFSR4       ///< gsl_rng_gfsr4
}; // enum GSLRngType

namespace internal {

template <GSLRngType> struct GSLRngTypePointer;

VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(MT19937,   mt19937)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLXS0,   ranlxs0)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLXS1,   ranlxs1)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLXS2,   ranlxs2)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLXD1,   ranlxd1)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLXD2,   ranlxd2)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLUX,    ranlux)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(RANLUX389, ranlux389)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(CMRG,      cmrg)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(MRG,       mrg)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(TAUS,      taus)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(TAUS2,     taus2)
VSMC_DEFINE_RNG_GSL_RNG_TYPE_POINTER(GFSR4,     gfsr4)

template <GSLRngType RngType> struct GSLRngMinMaxTrait;

VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(MT19937,   0, 4294967295)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLXS0,   0, 16777215)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLXS1,   0, 16777215)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLXS2,   0, 16777215)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLXD1,   0, 4294967295)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLXD2,   0, 4294967295)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLUX,    0, 16777215)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(RANLUX389, 0, 16777215)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(CMRG,      0, 2147483646)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(MRG,       0, 2147483646)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(TAUS,      0, 4294967295)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(TAUS2,     0, 4294967295)
VSMC_DEFINE_RNG_GSL_RNG_MIN_MAX_TRAIT(GFSR4,     0, 4294967295)

} // namespace vsmc::internal

/// \brief GSL RNG generator for use with GeneratorWrapper
/// \ingroup GSLRNG
template <GSLRngType RngType>
class GSLGenerator
{
    public :

    GSLGenerator () :
        rng_(gsl_rng_alloc(internal::GSLRngTypePointer<RngType>::get())) {}

    GSLGenerator (const GSLGenerator<RngType> &other) :
        rng_(gsl_rng_clone(other.rng_)) {}

    GSLGenerator<RngType> &operator= (const GSLGenerator<RngType> &other)
    {
        if (this != &other) {
            if (rng_ != VSMC_NULLPTR)
                gsl_rng_free(rng_);
            rng_ = gsl_rng_memcpy(other.rng_);
        }

        return *this;
    }

#if VSMC_HAS_CXX11_RVALUE_REFERENCES
    GSLGenerator (GSLGenerator<RngType> &&other) :
        rng_(other.rng_) {other.rng_ = VSMC_NULLPTR;}

    GSLGenerator<RngType> &operator= (GSLGenerator<RngType> &&other)
    {
        if (this != &other) {
            if (rng_ != VSMC_NULLPTR)
                gsl_rng_free(rng_);
            rng_ = other.rng_;
            other.rng_ = VSMC_NULLPTR;
        }

        return *this;
    }
#endif

    ~GSLGenerator ()
    {
        if (rng_ != VSMC_NULLPTR)
            gsl_rng_free(rng_);
    }

    void seed (unsigned long s)
    {
        if (rng_ != VSMC_NULLPTR)
            gsl_rng_set(rng_, s);
    }

    unsigned long generate ()
    {
        if (rng_ != VSMC_NULLPTR)
            return gsl_rng_get(rng_);

        return 0;
    }

    unsigned long min VSMC_MNE () const {return gsl_rng_min(rng_);}

    unsigned long max VSMC_MNE () const {return gsl_rng_max(rng_);}

    private :

    gsl_rng *rng_;
}; // class GSLGenerator

/// \brief GSL RNG Engine
/// \ingroup GSLRNG
template <GSLRngType RngType>
class GSLEngine :
    public GeneratorWrapper<uint32_t, GSLGenerator<RngType>,
    internal::GSLRngMinMaxTrait<RngType> >
{
    typedef GeneratorWrapper<uint32_t, GSLGenerator<RngType>,
    internal::GSLRngMinMaxTrait<RngType> > base;

    public :

    typedef uint32_t result_type;

    GSLEngine (result_type s = 0) : base(s) {seed(s);}

    template <typename SeedSeq>
    explicit GSLEngine (SeedSeq &seq, typename cxx11::enable_if<
            !internal::is_seed_sequence<SeedSeq, result_type>::value>::type * =
            VSMC_NULLPTR) : base(seq) {seed(seq);}

    void seed (result_type s)
    {this->generator().seed(static_cast<unsigned long>(s));}

    template <typename SeedSeq>
    void seed (SeedSeq &seq, typename cxx11::enable_if<
            !internal::is_seed_sequence<SeedSeq, result_type>::value>::type * =
            VSMC_NULLPTR)
    {
        unsigned long s;
        seq.generate(&s, &s + 1);
        this->generator().seed(s);
    }
}; // class GSLEngine

/// \brief A Mersenne-Twister pseudoranom number genertor
/// \ingroup GSLRNG
typedef GSLEngine<GSL_RNG_TYPE_MT19937> GSL_MT19937;

/// \brief A RANLUX generator with luxury level 0
/// \ingroup GSLRNG
typedef GSLEngine<GSL_RNG_TYPE_RANLXS0> GSL_RANLXS0;

/// \brief A RANLUX generator with luxury level 0
/// \ingroup GSLRNG
typedef GSLEngine<GSL_RNG_TYPE_RANLXS1> GSL_RANLXS1;

/// \brief A RANLUX generator with luxury level 0
/// \ingroup GSLRNG
typedef GSLEngine<GSL_RNG_TYPE_RANLXS2> GSL_RANLXS2;

/// \brief A RANLXS generator with luxury level 1
/// \ingroup GSLRNG
typedef GSLEngine<GSL_RNG_TYPE_RANLXD1> GSL_RANLXD1;

/// \brief A RANLXS generator with luxury level 2
/// \ingroup GSLRNG
typedef GSLEngine<GSL_RNG_TYPE_RANLXD2> GSL_RANLXD2;

/// \brief A RANLUX generator
/// \ingroup GSLRNG
typedef GSLEngine<GSL_RNG_TYPE_RANLUX> GSL_RANLUX;

/// \brief A RANLUX generator with the highest level of randomness
/// \ingroup GSLRNG
typedef GSLEngine<GSL_RNG_TYPE_RANLUX389> GSL_RANLUX389;

/// \brief A combined multiple recursive generator
/// \ingroup GSLRNG
typedef GSLEngine<GSL_RNG_TYPE_CMRG> GSL_CMRG;

/// \brief A fifth-order multiple recursive generator
/// \ingroup GSL
typedef GSLEngine<GSL_RNG_TYPE_MRG> GSL_MRG;

/// \brief A maximally equidistributed combined Tausworthe generator
/// \ingroup GSLRNG
typedef GSLEngine<GSL_RNG_TYPE_TAUS> GSL_TAUS;

/// \brief A maximally equidistributed combined Tausworthe generator with
/// improved seeding procedure
/// \ingroup GSLRNG
typedef GSLEngine<GSL_RNG_TYPE_TAUS2> GSL_TAUS2;

/// \brief A a lagged-fibonacci alike generator
/// \ingroup GSLRNG
typedef GSLEngine<GSL_RNG_TYPE_GFSR4> GSL_GFSR4;

} // namespace vsmc

#endif // VSMC_RNG_GSL_RNG_HPP
<|endoftext|>


<commit_before>/*
 * @file llimagepng.cpp
 * @brief LLImageFormatted glue to encode / decode PNG files.
 *
 * $LicenseInfo:firstyear=2007&license=viewergpl$
 *
 * Copyright (c) 2007-2009, Linden Research, Inc.
 *
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 *
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 *
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "linden_common.h"
#include "stdtypes.h"
#include "llerror.h"

#include "llimage.h"
#include "llpngwrapper.h"
#include "llimagepng.h"

// ---------------------------------------------------------------------------
// LLImagePNG
// ---------------------------------------------------------------------------
LLImagePNG::LLImagePNG()
    : LLImageFormatted(IMG_CODEC_PNG),
	  mTmpWriteBuffer(NULL)
{
}

LLImagePNG::~LLImagePNG()
{
	if (mTmpWriteBuffer)
	{
		delete[] mTmpWriteBuffer;
	}
}

// Virtual
// Parse PNG image information and set the appropriate
// width, height and component (channel) information.
BOOL LLImagePNG::updateData()
{
    resetLastError();

    // Check to make sure that this instance has been initialized with data
    if (!getData() || (0 == getDataSize()))
    {
        setLastError("Uninitialized instance of LLImagePNG");
        return FALSE;
    }

	// Decode the PNG data and extract sizing information
	LLPngWrapper pngWrapper;
	LLPngWrapper::ImageInfo infop;
	if (! pngWrapper.readPng(getData(), NULL, &infop))
	{
		setLastError(pngWrapper.getErrorMessage());
		return FALSE;
	}

	setSize(infop.mWidth, infop.mHeight, infop.mComponents);

	return TRUE;
}

// Virtual
// Decode an in-memory PNG image into the raw RGB or RGBA format
// used within SecondLife.
BOOL LLImagePNG::decode(LLImageRaw* raw_image, F32 decode_time)
{
	llassert_always(raw_image);

    resetLastError();

    // Check to make sure that this instance has been initialized with data
    if (!getData() || (0 == getDataSize()))
    {
        setLastError("LLImagePNG trying to decode an image with no data!");
        return FALSE;
    }

	// Decode the PNG data into the raw image
	LLPngWrapper pngWrapper;
	if (! pngWrapper.readPng(getData(), raw_image))
	{
		setLastError(pngWrapper.getErrorMessage());
		return FALSE;
	}

	return TRUE;
}

// Virtual
// Encode the in memory RGB image into PNG format.
BOOL LLImagePNG::encode(const LLImageRaw* raw_image, F32 encode_time)
{
	llassert_always(raw_image);

    resetLastError();

	// Image logical size
	setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents());

	// Temporary buffer to hold the encoded image. Note: the final image
	// size should be much smaller due to compression.
	if (mTmpWriteBuffer)
	{
		delete[] mTmpWriteBuffer;
	}
	U32 bufferSize = getWidth() * getHeight() * getComponents() + 1024;
    U8* mTmpWriteBuffer = new U8[ bufferSize ];

	// Delegate actual encoding work to wrapper
	LLPngWrapper pngWrapper;
	if (! pngWrapper.writePng(raw_image, mTmpWriteBuffer))
	{
		setLastError(pngWrapper.getErrorMessage());
		return FALSE;
	}

	// Resize internal buffer and copy from temp
	U32 encodedSize = pngWrapper.getFinalSize();
	allocateData(encodedSize);
	memcpy(getData(), mTmpWriteBuffer, encodedSize);

	delete[] mTmpWriteBuffer;

	return TRUE;
}

<commit_msg>EXT-7851 FIXED Memory leak in LLImagePNG::encode()<commit_after>/*
 * @file llimagepng.cpp
 * @brief LLImageFormatted glue to encode / decode PNG files.
 *
 * $LicenseInfo:firstyear=2007&license=viewergpl$
 *
 * Copyright (c) 2007-2009, Linden Research, Inc.
 *
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 *
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 *
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "linden_common.h"
#include "stdtypes.h"
#include "llerror.h"

#include "llimage.h"
#include "llpngwrapper.h"
#include "llimagepng.h"

// ---------------------------------------------------------------------------
// LLImagePNG
// ---------------------------------------------------------------------------
LLImagePNG::LLImagePNG()
    : LLImageFormatted(IMG_CODEC_PNG),
	  mTmpWriteBuffer(NULL)
{
}

LLImagePNG::~LLImagePNG()
{
	if (mTmpWriteBuffer)
	{
		delete[] mTmpWriteBuffer;
	}
}

// Virtual
// Parse PNG image information and set the appropriate
// width, height and component (channel) information.
BOOL LLImagePNG::updateData()
{
    resetLastError();

    // Check to make sure that this instance has been initialized with data
    if (!getData() || (0 == getDataSize()))
    {
        setLastError("Uninitialized instance of LLImagePNG");
        return FALSE;
    }

	// Decode the PNG data and extract sizing information
	LLPngWrapper pngWrapper;
	LLPngWrapper::ImageInfo infop;
	if (! pngWrapper.readPng(getData(), NULL, &infop))
	{
		setLastError(pngWrapper.getErrorMessage());
		return FALSE;
	}

	setSize(infop.mWidth, infop.mHeight, infop.mComponents);

	return TRUE;
}

// Virtual
// Decode an in-memory PNG image into the raw RGB or RGBA format
// used within SecondLife.
BOOL LLImagePNG::decode(LLImageRaw* raw_image, F32 decode_time)
{
	llassert_always(raw_image);

    resetLastError();

    // Check to make sure that this instance has been initialized with data
    if (!getData() || (0 == getDataSize()))
    {
        setLastError("LLImagePNG trying to decode an image with no data!");
        return FALSE;
    }

	// Decode the PNG data into the raw image
	LLPngWrapper pngWrapper;
	if (! pngWrapper.readPng(getData(), raw_image))
	{
		setLastError(pngWrapper.getErrorMessage());
		return FALSE;
	}

	return TRUE;
}

// Virtual
// Encode the in memory RGB image into PNG format.
BOOL LLImagePNG::encode(const LLImageRaw* raw_image, F32 encode_time)
{
	llassert_always(raw_image);

    resetLastError();

	// Image logical size
	setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents());

	// Temporary buffer to hold the encoded image. Note: the final image
	// size should be much smaller due to compression.
	if (mTmpWriteBuffer)
	{
		delete[] mTmpWriteBuffer;
	}
	U32 bufferSize = getWidth() * getHeight() * getComponents() + 1024;
    U8* mTmpWriteBuffer = new U8[ bufferSize ];

	// Delegate actual encoding work to wrapper
	LLPngWrapper pngWrapper;
	if (! pngWrapper.writePng(raw_image, mTmpWriteBuffer))
	{
		setLastError(pngWrapper.getErrorMessage());
		delete[] mTmpWriteBuffer;
		return FALSE;
	}

	// Resize internal buffer and copy from temp
	U32 encodedSize = pngWrapper.getFinalSize();
	allocateData(encodedSize);
	memcpy(getData(), mTmpWriteBuffer, encodedSize);

	delete[] mTmpWriteBuffer;

	return TRUE;
}

<|endoftext|>


<commit_before>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "utils/linearAlg2D.h" //Distance from point to line.
#include "MergeInfillLines.h"

namespace cura
{

    MergeInfillLines::MergeInfillLines(ExtruderPlan& plan, const coord_t nozzle_size, const coord_t maximum_resolution) : extruder_plan(plan), nozzle_size(nozzle_size), maximum_resolution(maximum_resolution)
    {
        //Just copy the parameters to their fields.
    }

    coord_t MergeInfillLines::calcPathLength(const Point path_start, GCodePath& path) const
    {
        Point previous_point = path_start;
        coord_t result = 0;
        for (const Point point : path.points)
        {
            result += vSize(point - previous_point);
            previous_point = point;
        }
        return result;
    }

    /*
     * first_is_already_merged == false
     *
     *       o     o
     *      /     /
     *     /     /
     *    /  +  /      --->   -(t)-o-----o
     *   /     /
     *  /     /
     * o     o
     *
     * travel (t) to first location is done through first_path_start_changed and new_first_path_start.
     * this gets rid of the tiny "blips". Depending on the merged line distance a small gap may appear, but this is
     * accounted for in the volume.
     *
     * first_is_already_merged == true
     *
     *                   o
     *                  /
     *                 /
     *    o-----o  +  /     --->    o-----------o   or with slight   o-----o-----o
     *   /           /             /                   bend         /
     *  /           /             /                                /
     * o           o             o                                o
     *
     */
    bool MergeInfillLines::mergeLinesSideBySide(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const
    {
        Point average_first_path;

        coord_t first_path_length = calcPathLength(first_path_start, first_path);
        coord_t first_path_length_flow = first_path_length * first_path.flow; //To get the volume we don't need to include the line width since it's the same for both lines.
        const coord_t line_width = first_path.config->getLineWidth();

        if (first_is_already_merged)
        {
            // take second point of path, first_path.points[0]
            average_first_path = first_path.points[0];
        }
        else
        {
            average_first_path += first_path_start;
            for (const Point point : first_path.points)
            {
                average_first_path += point;
            }
            average_first_path /= (first_path.points.size() + 1);
        }

        coord_t second_path_length = calcPathLength(second_path_start, second_path);
        Point average_second_path = second_path_start;
        for (const Point point : second_path.points)
        {
            average_second_path += point;
        }
        coord_t second_path_length_flow = second_path_length *= second_path.flow;
        average_second_path /= (coord_t) (second_path.points.size() + 1);

        // predict new length and flow and if the new flow is to big, don't merge. conditions in this part must exactly match the actual merging
        coord_t new_path_length = first_path_length;
        coord_t dist2_from_line = 0;
        coord_t new_error_area = 0;
        coord_t merged_part_length = 0;
        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1) {
                dist2_from_line = LinearAlg2D::getDist2FromLine(average_second_path, first_path.points[first_path.points.size() - 2], first_path.points[first_path.points.size() - 1]);
                merged_part_length = vSize(first_path.points[first_path.points.size() - 2] - average_second_path);
                new_error_area = sqrt(dist2_from_line) * merged_part_length / 2;
            }
            // The max error margin uses the meshfix_maximum_resolution setting
            if (first_path.points.size() > 1 && error_area + new_error_area < merged_part_length * maximum_resolution)
            {
                new_path_length -= vSize(first_path.points[first_path.points.size() - 2] - first_path.points[first_path.points.size() - 1]);
                new_path_length += vSize(first_path.points[first_path.points.size() - 2] - average_second_path);
            }
            else
            {
                new_path_length += vSize(first_path.points[first_path.points.size() - 1] - average_second_path);
            }
        }
        else
        {
            new_path_length -= vSize(first_path.points.back() - first_path_start);
            new_path_length += vSize(average_second_path - average_first_path);
        }
        double new_flow = ((first_path_length_flow + second_path_length_flow) / static_cast<double>(new_path_length));
        if (new_flow > 2 * nozzle_size / line_width)  // line width becomes too wide.
        {
            return false;
        }

        // do the actual merging
        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1 && error_area + new_error_area < line_width * line_width)
            {
                first_path.points[first_path.points.size() - 1] = average_second_path;
                error_area += new_error_area;
            } else {
                first_path.points.push_back(average_second_path);
                error_area = 0;
            }
        }
        else
        {
            new_first_path_start = average_first_path;
            first_path.points.clear();
            first_path.points.push_back(average_second_path);
            error_area = 0;
        }

        first_path.flow = static_cast<double>(first_path_length_flow + second_path_length_flow) / new_path_length;

        return true;
    }

    bool MergeInfillLines::tryMerge(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const
    {
        const Point first_path_end = first_path.points.back();
        const Point second_path_end = second_path.points.back();
        const coord_t line_width = first_path.config->getLineWidth();

        // Reintroduction of this check prevents [CURA-5674] printing spurious infill-lines to origin:
        const auto line_width_sqrd = line_width * line_width;
        if (vSize2(first_path_end - second_path_start) < line_width_sqrd && vSize2(first_path_start - second_path_end) < line_width_sqrd)
        {
            return false;
        }

        //Lines may be adjacent side-by-side then.
        Point first_path_leave_point;
        coord_t merged_size2;
        if (first_is_already_merged)
        {
            first_path_leave_point = first_path.points.back();  // this is the point that's going to merge
        } else {
            first_path_leave_point = (first_path_start + first_path_end) / 2;
        }
        const Point second_path_destination_point = (second_path_start + second_path_end) / 2;
        const Point merged_direction = second_path_destination_point - first_path_leave_point;
        if (first_is_already_merged)
        {
            merged_size2 = vSize2(second_path_destination_point - first_path.points.back());  // check distance with last point in merged line that is to be replaced
        }
        else
        {
            merged_size2 = vSize2(merged_direction);
        }
        if (merged_size2 > 25 * line_width * line_width)
        {
            return false; //Lines are too far away from each other.
        }
        if (merged_direction.X == 0 && merged_direction.Y == 0)
        {
            return true;  // we can just disregard the second point as it's exactly at the leave point of the first path.
        }

        // Max 1 line width to the side of the merged_direction
        if (LinearAlg2D::getDist2FromLine(first_path_end, second_path_destination_point, second_path_destination_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_start, first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_end,   first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            //|| abs(dot(normal(merged_direction, 1000), normal(second_path_end - second_path_start, 1000))) > 866000    // 866000 angle of old second_path with new merged direction should not be too small (30 degrees), as it will introduce holes
            )
        {
            return false; //One of the lines is too far from the merged line. Lines would be too wide or too far off.
        }
        if (first_is_already_merged && first_path.points.size() > 1 && first_path.points[first_path.points.size() - 2] == second_path_destination_point)  // yes this can actually happen
        {
            return false;
        }

        return mergeLinesSideBySide(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area);
    }

    bool MergeInfillLines::mergeInfillLines(std::vector<GCodePath>& paths, const Point& starting_position) const
    {
        /* Algorithm overview:
            1. Loop over all lines to see if they can be merged.
            1a. Check if two adjacent lines can be merged (skipping travel moves in
                between).
            1b. If they can, merge both lines into the first line.
            1c. If they are merged, check next that the first line can be merged
                with the line after the second line.
            2. Do a second iteration over all paths to remove the tombstones. */
        std::vector<size_t> remove_path_indices;
        std::set<size_t> is_merged;
        std::set<size_t> removed;  // keep track of what we already removed, so don't remove it again

        //For each two adjacent lines, see if they can be merged.
        size_t first_path_index = 0;
        Point first_path_start = Point(starting_position.X, starting_position.Y);  // this one is not going to be overwritten
        size_t second_path_index = 1;
        coord_t error_area = 0;

        for (; second_path_index < paths.size(); second_path_index++)
        {
            GCodePath& first_path = paths[first_path_index];
            GCodePath& second_path = paths[second_path_index];
            Point second_path_start = paths[second_path_index - 1].points.back();

            if (second_path.config->isTravelPath())
            {
                continue; //Skip travel paths, we're looking for the first non-travel path.
            }

            bool allow_try_merge = true;
            // see if we meet criteria to merge. should be: travel - path1 not travel - (...) - travel - path2 not travel - travel
            // we're checking the travels here
            if (first_path_index <= 1 || !paths[first_path_index - 1].isTravelPath())  // "<= 1" because we don't want the first travel being changed. That may introduce a hole somewhere
            {
                allow_try_merge = false;
            }
            if (second_path_index + 1 >= paths.size() || !paths[second_path_index + 1].isTravelPath())
            {
                allow_try_merge = false;
            }
            if (first_path.config->isTravelPath()) //Don't merge travel moves.
            {
                allow_try_merge = false;
            }
            if (first_path.config != second_path.config) //Only merge lines that have the same type.
            {
                allow_try_merge = false;
            }
            if (first_path.config->type != PrintFeatureType::Infill && first_path.config->type != PrintFeatureType::Skin) //Only merge skin and infill lines.
            {
                allow_try_merge = false;
            }
            const bool first_is_already_merged = is_merged.find(first_path_index) != is_merged.end();
            if ((!first_is_already_merged && first_path.points.size() > 1) || second_path.points.size() > 1)
            {
                // For now we only merge simple lines, not polylines, to keep it simple.
                // If the first line is already a merged line, then allow it.
                allow_try_merge = false;
            }

            Point new_first_path_start;
            if (allow_try_merge && tryMerge(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area))
            {
                if (!first_is_already_merged)
                {
                    paths[first_path_index - 1].points.back().X = new_first_path_start.X;
                    paths[first_path_index - 1].points.back().Y = new_first_path_start.Y;
                }
                /* If we combine two lines, the next path may also be merged into the fist line, so we do NOT update
                first_path_index. */
                for (size_t to_delete_index = first_path_index + 1; to_delete_index <= second_path_index; to_delete_index++)
                {
                    if (removed.find(to_delete_index) == removed.end())  // if there are line(s) between first and second, then those lines are already marked as to be deleted, only add the new line(s)
                    {
                        remove_path_indices.push_back(to_delete_index);
                        removed.insert(to_delete_index);
                    }
                }
                is_merged.insert(first_path_index);
            }
            else
            {
                /* If we do not combine, the next iteration we must simply merge the
                second path with the line after it. */
                first_path_index = second_path_index;
                first_path_start = second_path_start;
            }
        }

        //Delete all removed lines in one pass so that we need to move lines less often.
        if (!remove_path_indices.empty())
        {
            size_t path_index = remove_path_indices[0];
            for (size_t removed_position = 1; removed_position < remove_path_indices.size(); removed_position++)
            {
                for (; path_index < remove_path_indices[removed_position] - removed_position; path_index++)
                {
                    paths[path_index] = paths[path_index + removed_position]; //Shift all paths.
                }
            }
            for (; path_index < paths.size() - remove_path_indices.size(); path_index++) //Remaining shifts at the end.
            {
                paths[path_index] = paths[path_index + remove_path_indices.size()];
            }
            paths.erase(paths.begin() + path_index, paths.end());
            return true;
        }
        else
        {
            return false;
        }
    }
}//namespace cura
<commit_msg>[CURA-5742] Small refactor of check in case it isn't removed after all.<commit_after>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "utils/linearAlg2D.h" //Distance from point to line.
#include "MergeInfillLines.h"

namespace cura
{

    MergeInfillLines::MergeInfillLines(ExtruderPlan& plan, const coord_t nozzle_size, const coord_t maximum_resolution) : extruder_plan(plan), nozzle_size(nozzle_size), maximum_resolution(maximum_resolution)
    {
        //Just copy the parameters to their fields.
    }

    coord_t MergeInfillLines::calcPathLength(const Point path_start, GCodePath& path) const
    {
        Point previous_point = path_start;
        coord_t result = 0;
        for (const Point point : path.points)
        {
            result += vSize(point - previous_point);
            previous_point = point;
        }
        return result;
    }

    /*
     * first_is_already_merged == false
     *
     *       o     o
     *      /     /
     *     /     /
     *    /  +  /      --->   -(t)-o-----o
     *   /     /
     *  /     /
     * o     o
     *
     * travel (t) to first location is done through first_path_start_changed and new_first_path_start.
     * this gets rid of the tiny "blips". Depending on the merged line distance a small gap may appear, but this is
     * accounted for in the volume.
     *
     * first_is_already_merged == true
     *
     *                   o
     *                  /
     *                 /
     *    o-----o  +  /     --->    o-----------o   or with slight   o-----o-----o
     *   /           /             /                   bend         /
     *  /           /             /                                /
     * o           o             o                                o
     *
     */
    bool MergeInfillLines::mergeLinesSideBySide(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const
    {
        Point average_first_path;

        coord_t first_path_length = calcPathLength(first_path_start, first_path);
        coord_t first_path_length_flow = first_path_length * first_path.flow; //To get the volume we don't need to include the line width since it's the same for both lines.
        const coord_t line_width = first_path.config->getLineWidth();

        if (first_is_already_merged)
        {
            // take second point of path, first_path.points[0]
            average_first_path = first_path.points[0];
        }
        else
        {
            average_first_path += first_path_start;
            for (const Point point : first_path.points)
            {
                average_first_path += point;
            }
            average_first_path /= (first_path.points.size() + 1);
        }

        coord_t second_path_length = calcPathLength(second_path_start, second_path);
        Point average_second_path = second_path_start;
        for (const Point point : second_path.points)
        {
            average_second_path += point;
        }
        coord_t second_path_length_flow = second_path_length *= second_path.flow;
        average_second_path /= (coord_t) (second_path.points.size() + 1);

        // predict new length and flow and if the new flow is to big, don't merge. conditions in this part must exactly match the actual merging
        coord_t new_path_length = first_path_length;
        coord_t dist2_from_line = 0;
        coord_t new_error_area = 0;
        coord_t merged_part_length = 0;
        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1) {
                dist2_from_line = LinearAlg2D::getDist2FromLine(average_second_path, first_path.points[first_path.points.size() - 2], first_path.points[first_path.points.size() - 1]);
                merged_part_length = vSize(first_path.points[first_path.points.size() - 2] - average_second_path);
                new_error_area = sqrt(dist2_from_line) * merged_part_length / 2;
            }
            // The max error margin uses the meshfix_maximum_resolution setting
            if (first_path.points.size() > 1 && error_area + new_error_area < merged_part_length * maximum_resolution)
            {
                new_path_length -= vSize(first_path.points[first_path.points.size() - 2] - first_path.points[first_path.points.size() - 1]);
                new_path_length += vSize(first_path.points[first_path.points.size() - 2] - average_second_path);
            }
            else
            {
                new_path_length += vSize(first_path.points[first_path.points.size() - 1] - average_second_path);
            }
        }
        else
        {
            new_path_length -= vSize(first_path.points.back() - first_path_start);
            new_path_length += vSize(average_second_path - average_first_path);
        }
        double new_flow = ((first_path_length_flow + second_path_length_flow) / static_cast<double>(new_path_length));
        if (new_flow > 2 * nozzle_size / line_width)  // line width becomes too wide.
        {
            return false;
        }

        // do the actual merging
        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1 && error_area + new_error_area < line_width * line_width)
            {
                first_path.points[first_path.points.size() - 1] = average_second_path;
                error_area += new_error_area;
            } else {
                first_path.points.push_back(average_second_path);
                error_area = 0;
            }
        }
        else
        {
            new_first_path_start = average_first_path;
            first_path.points.clear();
            first_path.points.push_back(average_second_path);
            error_area = 0;
        }

        first_path.flow = static_cast<double>(first_path_length_flow + second_path_length_flow) / new_path_length;

        return true;
    }

    bool MergeInfillLines::tryMerge(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const
    {
        const Point first_path_end = first_path.points.back();
        const Point second_path_end = second_path.points.back();
        const coord_t line_width = first_path.config->getLineWidth();

        // Reintroduction of this check prevents [CURA-5674] printing spurious infill-lines to origin:
        const coord_t line_width_squared = line_width * line_width;
        if (vSize2(first_path_end - second_path_start) < line_width_squared && vSize2(first_path_start - second_path_end) < line_width_squared)
        {
            return false;
        }

        //Lines may be adjacent side-by-side then.
        Point first_path_leave_point;
        coord_t merged_size2;
        if (first_is_already_merged)
        {
            first_path_leave_point = first_path.points.back();  // this is the point that's going to merge
        } else {
            first_path_leave_point = (first_path_start + first_path_end) / 2;
        }
        const Point second_path_destination_point = (second_path_start + second_path_end) / 2;
        const Point merged_direction = second_path_destination_point - first_path_leave_point;
        if (first_is_already_merged)
        {
            merged_size2 = vSize2(second_path_destination_point - first_path.points.back());  // check distance with last point in merged line that is to be replaced
        }
        else
        {
            merged_size2 = vSize2(merged_direction);
        }
        if (merged_size2 > 25 * line_width * line_width)
        {
            return false; //Lines are too far away from each other.
        }
        if (merged_direction.X == 0 && merged_direction.Y == 0)
        {
            return true;  // we can just disregard the second point as it's exactly at the leave point of the first path.
        }

        // Max 1 line width to the side of the merged_direction
        if (LinearAlg2D::getDist2FromLine(first_path_end, second_path_destination_point, second_path_destination_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_start, first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_end,   first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            //|| abs(dot(normal(merged_direction, 1000), normal(second_path_end - second_path_start, 1000))) > 866000    // 866000 angle of old second_path with new merged direction should not be too small (30 degrees), as it will introduce holes
            )
        {
            return false; //One of the lines is too far from the merged line. Lines would be too wide or too far off.
        }
        if (first_is_already_merged && first_path.points.size() > 1 && first_path.points[first_path.points.size() - 2] == second_path_destination_point)  // yes this can actually happen
        {
            return false;
        }

        return mergeLinesSideBySide(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area);
    }

    bool MergeInfillLines::mergeInfillLines(std::vector<GCodePath>& paths, const Point& starting_position) const
    {
        /* Algorithm overview:
            1. Loop over all lines to see if they can be merged.
            1a. Check if two adjacent lines can be merged (skipping travel moves in
                between).
            1b. If they can, merge both lines into the first line.
            1c. If they are merged, check next that the first line can be merged
                with the line after the second line.
            2. Do a second iteration over all paths to remove the tombstones. */
        std::vector<size_t> remove_path_indices;
        std::set<size_t> is_merged;
        std::set<size_t> removed;  // keep track of what we already removed, so don't remove it again

        //For each two adjacent lines, see if they can be merged.
        size_t first_path_index = 0;
        Point first_path_start = Point(starting_position.X, starting_position.Y);  // this one is not going to be overwritten
        size_t second_path_index = 1;
        coord_t error_area = 0;

        for (; second_path_index < paths.size(); second_path_index++)
        {
            GCodePath& first_path = paths[first_path_index];
            GCodePath& second_path = paths[second_path_index];
            Point second_path_start = paths[second_path_index - 1].points.back();

            if (second_path.config->isTravelPath())
            {
                continue; //Skip travel paths, we're looking for the first non-travel path.
            }

            bool allow_try_merge = true;
            // see if we meet criteria to merge. should be: travel - path1 not travel - (...) - travel - path2 not travel - travel
            // we're checking the travels here
            if (first_path_index <= 1 || !paths[first_path_index - 1].isTravelPath())  // "<= 1" because we don't want the first travel being changed. That may introduce a hole somewhere
            {
                allow_try_merge = false;
            }
            if (second_path_index + 1 >= paths.size() || !paths[second_path_index + 1].isTravelPath())
            {
                allow_try_merge = false;
            }
            if (first_path.config->isTravelPath()) //Don't merge travel moves.
            {
                allow_try_merge = false;
            }
            if (first_path.config != second_path.config) //Only merge lines that have the same type.
            {
                allow_try_merge = false;
            }
            if (first_path.config->type != PrintFeatureType::Infill && first_path.config->type != PrintFeatureType::Skin) //Only merge skin and infill lines.
            {
                allow_try_merge = false;
            }
            const bool first_is_already_merged = is_merged.find(first_path_index) != is_merged.end();
            if ((!first_is_already_merged && first_path.points.size() > 1) || second_path.points.size() > 1)
            {
                // For now we only merge simple lines, not polylines, to keep it simple.
                // If the first line is already a merged line, then allow it.
                allow_try_merge = false;
            }

            Point new_first_path_start;
            if (allow_try_merge && tryMerge(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area))
            {
                if (!first_is_already_merged)
                {
                    paths[first_path_index - 1].points.back().X = new_first_path_start.X;
                    paths[first_path_index - 1].points.back().Y = new_first_path_start.Y;
                }
                /* If we combine two lines, the next path may also be merged into the fist line, so we do NOT update
                first_path_index. */
                for (size_t to_delete_index = first_path_index + 1; to_delete_index <= second_path_index; to_delete_index++)
                {
                    if (removed.find(to_delete_index) == removed.end())  // if there are line(s) between first and second, then those lines are already marked as to be deleted, only add the new line(s)
                    {
                        remove_path_indices.push_back(to_delete_index);
                        removed.insert(to_delete_index);
                    }
                }
                is_merged.insert(first_path_index);
            }
            else
            {
                /* If we do not combine, the next iteration we must simply merge the
                second path with the line after it. */
                first_path_index = second_path_index;
                first_path_start = second_path_start;
            }
        }

        //Delete all removed lines in one pass so that we need to move lines less often.
        if (!remove_path_indices.empty())
        {
            size_t path_index = remove_path_indices[0];
            for (size_t removed_position = 1; removed_position < remove_path_indices.size(); removed_position++)
            {
                for (; path_index < remove_path_indices[removed_position] - removed_position; path_index++)
                {
                    paths[path_index] = paths[path_index + removed_position]; //Shift all paths.
                }
            }
            for (; path_index < paths.size() - remove_path_indices.size(); path_index++) //Remaining shifts at the end.
            {
                paths[path_index] = paths[path_index + remove_path_indices.size()];
            }
            paths.erase(paths.begin() + path_index, paths.end());
            return true;
        }
        else
        {
            return false;
        }
    }
}//namespace cura
<|endoftext|>


<commit_before>#include "Atm_pulse.h"

Atm_pulse & Atm_pulse::begin( int attached_pin, int minimum_duration )
{
  const static state_t state_table[] PROGMEM = {
  /*              ON_ENTER    ON_LOOP  ON_EXIT  EVT_TIMER   EVT_HIGH  EVT_LOW   ELSE */
  /* IDLE    */         -1,        -1,      -1,        -1,      WAIT,      -1,    -1,
  /* WAIT    */         -1,        -1,      -1,     PULSE,        -1,    IDLE,    -1,
  /* PULSE   */  ACT_PULSE,        -1,      -1,        -1,        -1,    IDLE,    -1,
  };
  Machine::begin( state_table, ELSE );
  pin = attached_pin;
  timer.set( minimum_duration );
  pinMode( pin, INPUT );
  return *this;
}

Atm_pulse & Atm_pulse::onPulse( Machine * machine, uint8_t event )
{
  client_machine = machine;
  client_event = event;
  return *this;
}

Atm_pulse & Atm_pulse::onPulse( pulsecb_t pulse_callback )
{
  callback = pulse_callback;
  return *this;
}

int Atm_pulse::event( int id )
{
  switch ( id ) {
  	case EVT_TIMER :
  	  return timer.expired( this );
    case EVT_HIGH :
      return digitalRead( pin );
    case EVT_LOW :
      return !digitalRead( pin );
   }
   return 0;
}

void Atm_pulse::action( int id )
{
  switch ( id ) {
  	case ACT_PULSE :
      if ( callback ) {
         (*callback)();
         return;
      }
      client_machine->trigger( client_event );
  	  return;
   }
}

Atm_pulse & Atm_pulse::trace( Stream & stream ) {

  setTrace( &stream, atm_serial_debug::trace,
    "EVT_TIMER\0EVT_HIGH\0EVT_LOW\0ELSE\0IDLE\0WAIT\0PULSE" );
  return *this;
}

// TinyMachine version

Att_pulse & Att_pulse::begin( int attached_pin, int minimum_duration )
{
  const static tiny_state_t state_table[] PROGMEM = {
  /*              ON_ENTER    ON_LOOP  ON_EXIT  EVT_TIMER   EVT_HIGH  EVT_LOW   ELSE */
  /* IDLE    */         -1,        -1,      -1,        -1,      WAIT,      -1,    -1,
  /* WAIT    */         -1,        -1,      -1,     PULSE,        -1,    IDLE,    -1,
  /* PULSE   */  ACT_PULSE,        -1,      -1,        -1,        -1,    IDLE,    -1,
  };
  TinyMachine::begin( state_table, ELSE );
  pin = attached_pin;
  timer.set( minimum_duration );
  pinMode( pin, INPUT );
  return *this;
}

Att_pulse & Att_pulse::onPulse( TinyMachine * machine, uint8_t event )
{
  client_machine = machine;
  client_event = event;
  return *this;
}

Att_pulse & Att_pulse::onPulse( pulsecb_t pulse_callback )
{
  callback = pulse_callback;
  return *this;
}

int Att_pulse::event( int id )
{
  switch ( id ) {
  	case EVT_TIMER :
  	  return timer.expired( this );
    case EVT_HIGH :
      return digitalRead( pin );
    case EVT_LOW :
      return !digitalRead( pin );
   }
   return 0;
}

void Att_pulse::action( int id )
{
  switch ( id ) {
  	case ACT_PULSE :
      if ( callback ) {
         (*callback)();
         return;
      }
      client_machine->trigger( client_event );
  	  return;
   }
}

<commit_msg>Fixed machine trigger<commit_after>#include "Atm_pulse.h"

Atm_pulse & Atm_pulse::begin( int attached_pin, int minimum_duration )
{
  const static state_t state_table[] PROGMEM = {
  /*              ON_ENTER    ON_LOOP  ON_EXIT  EVT_TIMER   EVT_HIGH  EVT_LOW   ELSE */
  /* IDLE    */         -1,        -1,      -1,        -1,      WAIT,      -1,    -1,
  /* WAIT    */         -1,        -1,      -1,     PULSE,        -1,    IDLE,    -1,
  /* PULSE   */  ACT_PULSE,        -1,      -1,        -1,        -1,    IDLE,    -1,
  };
  Machine::begin( state_table, ELSE );
  pin = attached_pin;
  timer.set( minimum_duration );
  pinMode( pin, INPUT );
  return *this;
}

Atm_pulse & Atm_pulse::onPulse( Machine * machine, uint8_t event )
{
  client_machine = machine;
  client_event = event;
  return *this;
}

Atm_pulse & Atm_pulse::onPulse( pulsecb_t pulse_callback )
{
  callback = pulse_callback;
  return *this;
}

int Atm_pulse::event( int id )
{
  switch ( id ) {
  	case EVT_TIMER :
  	  return timer.expired( this );
    case EVT_HIGH :
      return digitalRead( pin );
    case EVT_LOW :
      return !digitalRead( pin );
   }
   return 0;
}

void Atm_pulse::action( int id )
{
  switch ( id ) {
  	case ACT_PULSE :
      if ( callback ) {
         (*callback)();
         return;
      }
      if ( client_machine )
          client_machine->trigger( client_event );
  	  return;
   }
}

Atm_pulse & Atm_pulse::trace( Stream & stream ) {

  setTrace( &stream, atm_serial_debug::trace,
    "EVT_TIMER\0EVT_HIGH\0EVT_LOW\0ELSE\0IDLE\0WAIT\0PULSE" );
  return *this;
}

// TinyMachine version

Att_pulse & Att_pulse::begin( int attached_pin, int minimum_duration )
{
  const static tiny_state_t state_table[] PROGMEM = {
  /*              ON_ENTER    ON_LOOP  ON_EXIT  EVT_TIMER   EVT_HIGH  EVT_LOW   ELSE */
  /* IDLE    */         -1,        -1,      -1,        -1,      WAIT,      -1,    -1,
  /* WAIT    */         -1,        -1,      -1,     PULSE,        -1,    IDLE,    -1,
  /* PULSE   */  ACT_PULSE,        -1,      -1,        -1,        -1,    IDLE,    -1,
  };
  TinyMachine::begin( state_table, ELSE );
  pin = attached_pin;
  timer.set( minimum_duration );
  pinMode( pin, INPUT );
  return *this;
}

Att_pulse & Att_pulse::onPulse( TinyMachine * machine, uint8_t event )
{
  client_machine = machine;
  client_event = event;
  return *this;
}

Att_pulse & Att_pulse::onPulse( pulsecb_t pulse_callback )
{
  callback = pulse_callback;
  return *this;
}

int Att_pulse::event( int id )
{
  switch ( id ) {
  	case EVT_TIMER :
  	  return timer.expired( this );
    case EVT_HIGH :
      return digitalRead( pin );
    case EVT_LOW :
      return !digitalRead( pin );
   }
   return 0;
}

void Att_pulse::action( int id )
{
  switch ( id ) {
  	case ACT_PULSE :
      if ( callback ) {
         (*callback)();
         return;
      }
      if ( client_machine )
          client_machine->trigger( client_event );
  	  return;
   }
}

<|endoftext|>


<commit_before>/*
  Automaton.cpp - Library for creating and running Finite State Machines.
  Published under the MIT License (MIT), Copyright (c) 2015, J.P. van der Landen
*/

#include "Automaton.h"

void atm_timer::set( uint32_t v ) {
    value = v;
}

void atm_timer::begin( BaseMachine * machine, uint32_t v  ) {
    pmachine = machine;
    value = v;
}

int atm_timer_millis::expired( void ) {
    return value == ATM_TIMER_OFF ? 0 : millis() - pmachine->state_millis >= value;
}

int atm_timer_micros::expired( void ) {
    return value == ATM_TIMER_OFF ? 0 : micros() - pmachine->state_micros >= value;
}

void atm_counter::set( uint16_t v ) {
    value = v;
}

uint16_t atm_counter::decrement( void )
{
    return value > 0 && value != ATM_COUNTER_OFF ? --value : 0;
}

uint8_t atm_counter::expired()
{
    return value == ATM_COUNTER_OFF ? 0 : ( value > 0 ? 0 : 1 );
}

Machine & Machine::state(state_t state)
{
    next = state;
    last_trigger = -1;
    sleep = 0;
    if ( msg_autoclear ) msgClear();
    return *this;
}

state_t Machine::state()
{
    return current;
}

int Machine::trigger( int evt )
{
    if ( current > -1 ) {
        int new_state = read_state( state_table + ( current * state_width ) + evt + ATM_ON_EXIT + 1 );
        if ( new_state > -1 ) {
	      state( new_state );
          last_trigger = evt;
          return 1;
        }
    }
    return 0;
}

Machine &  Machine::toggle( state_t state1, state_t state2 )
{
    state( current == state1 ? state2 : state1 );
    return *this;
}

Machine & Machine::onSwitch( swcb_num_t callback )
{
    callback_num = callback;
    return *this;
}

Machine & Machine::onSwitch( swcb_sym_t callback, const char sym_s[], const char sym_e[] )
{
    callback_sym = callback;
    sym_states = sym_s;
    sym_events = sym_e;
    return *this;
}

Machine & Machine::label( const char label[] )
{
    inst_label = label;
    return *this;
}

int8_t Machine::priority()
{
    return prio;
}

Machine & Machine::priority( int8_t priority )
{
    prio = priority;
    return *this;
}

// .asleep() Returns true if the machine is in a sleeping state

uint8_t BaseMachine::asleep()
{
    return sleep;
}

Machine & Machine::begin( const state_t* tbl, int width )
{
    state_table = tbl;
    state_width = ATM_ON_EXIT + width + 2;
    prio = ATM_DEFAULT_PRIO;
    if ( !inst_label ) inst_label = class_label;
    return *this;
}

Machine & Machine::msgQueue( atm_msg_t msg[], int width )
{
    msg_table = msg;
    msg_width = width;
    return *this;
}

Machine & Machine::msgQueue( atm_msg_t msg[], int width, uint8_t autoclear )
{
    msg_table = msg;
    msg_width = width;
    msg_autoclear = autoclear;
    return *this;
}

unsigned char Machine::pinChange( uint8_t pin ) {

  unsigned char v = digitalRead( pin ) ? 1 : 0;
  if ( (( pinstate >> pin ) & 1 ) != ( v == 1 ) ) {
    pinstate ^= ( (uint32_t)1 << pin );
    return 1;
  }
  return 0;
}

int Machine::msgRead( uint8_t id_msg ) // Checks msg queue and removes 1 msg
{
  if ( msg_table[id_msg] > 0 ) {
      msg_table[id_msg]--;
      return 1;
  }
  return 0;
}

int Machine::msgRead( uint8_t id_msg, int cnt )
{
  if ( msg_table[id_msg] > 0 ) {
    if ( cnt >= msg_table[id_msg] ) {
        msg_table[id_msg] = 0;
    } else {
        msg_table[id_msg] -= cnt;
    }
    return 1;
  }
  return 0;
}

int Machine::msgRead( uint8_t id_msg, int cnt, int clear )
{
  if ( msg_table[id_msg] > 0 ) {
    if ( cnt >= msg_table[id_msg] ) {
        msg_table[id_msg] = 0;
    } else {
        msg_table[id_msg] -= cnt;
    }
	if ( clear ) {
		for ( int i = 0; i < msg_width; i++ ) {
			msg_table[i] = 0;
		}
    }
    return 1;
  }
  return 0;
}

int Machine::msgPeek( uint8_t id_msg )
{
  return msg_table[id_msg];
}

int Machine::msgClear( uint8_t id_msg ) // Destructive read (clears queue for this type)
{
  sleep = 0;
  if ( msg_table[id_msg] > 0 ) {
    msg_table[id_msg] = 0;
    return 1;
  }
  return 0;
}

Machine & Machine::msgClear()
{
  sleep = 0;
  for ( int i = 0; i < msg_width; i++ ) {
    msg_table[i] = 0;
  }
  return *this;
}

Machine & Machine::msgWrite( uint8_t id_msg )
{
  sleep = 0;
  msg_table[id_msg]++;
  return *this;
}

Machine & Machine::msgWrite( uint8_t id_msg, int cnt )
{
  sleep = 0;
  msg_table[id_msg] += cnt;
  return *this;
}

const char * Machine::map_symbol( int id, const char map[] )
{
  int cnt = 0;
  int i = 0;
  if ( id == -1 ) return "*NONE*";
  if ( id ==  0 ) return map;
  while ( 1 ) {
    if ( map[i] == '\0' && ++cnt == id ) {
      i++;
      break;
    }
    i++;
  }
  return &map[i];
}

// .cycle() Executes one cycle of a state machine
Machine & Machine::cycle()
{
    if ( !sleep ) {
        cycles++;
        if ( next != -1 ) {
            action( ATM_ON_SWITCH );
            if ( callback_sym || callback_num ) {
                if ( callback_sym ) {
                    callback_sym( inst_label,
                        map_symbol(      current, sym_states ),
                        map_symbol(         next, sym_states ),
                        map_symbol( last_trigger, sym_events ), millis() - state_millis, cycles );
                } else {
                    callback_num( inst_label, current, next, last_trigger, millis() - state_millis, cycles );
                }
            }
            if ( current > -1 )
		action( read_state( state_table + ( current * state_width ) + ATM_ON_EXIT ) );
            previous = current;
            current = next;
            next = -1;
            state_millis = millis();
            state_micros = micros();
            action( read_state( state_table + ( current * state_width ) + ATM_ON_ENTER ) );
            sleep = read_state( state_table + ( current * state_width ) + ATM_ON_LOOP ) == ATM_SLEEP;
            cycles = 0;
        }
        state_t i = read_state( state_table + ( current * state_width ) + ATM_ON_LOOP );
        if ( i != -1 ) { action( i ); }
        for ( i = ATM_ON_EXIT + 1; i < state_width; i++ ) {
            if ( ( read_state( state_table + ( current * state_width ) + i ) != -1 ) && ( i == state_width - 1 || event( i - ATM_ON_EXIT - 1 ) ) ) {
                state( read_state( state_table + ( current * state_width ) + i ) );
                last_trigger = i - ATM_ON_EXIT - 1;
                return *this;
            }
        }
    }
    return *this;
}

// TINY MACHINE

TinyMachine & TinyMachine::state(tiny_state_t state)
{
    next = state;
    sleep = 0;
    return *this;
}

tiny_state_t TinyMachine::state()
{
    return current;
}

int TinyMachine::trigger( int evt )
{
    if ( current > -1 ) {
        int new_state = tiny_read_state( state_table + ( current * state_width ) + evt + ATM_ON_EXIT + 1 );
        if ( new_state > -1 ) {
          state( new_state );
          return 1;
        }
    }
    return 0;
}

TinyMachine & TinyMachine::begin( const tiny_state_t* tbl, int width )
{
    state_table = tbl;
    state_width = ATM_ON_EXIT + width + 2;
    return *this;
}

// .cycle() Executes one cycle of a state machine
TinyMachine & TinyMachine::cycle()
{
    if ( !sleep ) {
        if ( next != -1 ) {
            action( ATM_ON_SWITCH );
            if ( current > -1 )
                action( tiny_read_state( state_table + ( current * state_width ) + ATM_ON_EXIT ) );
            previous = current;
            current = next;
            next = -1;
            state_millis = millis();
            state_micros = micros();
            action( tiny_read_state( state_table + ( current * state_width ) + ATM_ON_ENTER ) );
            sleep = tiny_read_state( state_table + ( current * state_width ) + ATM_ON_LOOP ) == ATM_SLEEP;
        }
        tiny_state_t i = tiny_read_state( state_table + ( current * state_width ) + ATM_ON_LOOP );
        if ( i != -1 ) { action( i ); }
        for ( i = ATM_ON_EXIT + 1; i < state_width; i++ ) {
            if ( ( tiny_read_state( state_table + ( current * state_width ) + i ) != -1 ) && ( i == state_width - 1 || event( i - ATM_ON_EXIT - 1 ) ) ) {
                state( tiny_read_state( state_table + ( current * state_width ) + i ) );
                return *this;
            }
        }
    }
    return *this;
}

// FACTORY

// .calibrate() Distributes the machines in the inventory to the appropriate priority queues
void Factory::calibrate( void )
{
    // Reset all priority queues to empty lists
    for ( int8_t i = 0; i < ATM_NO_OF_QUEUES; i++ )
        priority_root[i] = 0;
    // Walk the inventory list that contains all state machines in this factory
    Machine * m = inventory_root;
    while ( m ) {
        // Prepend every machine to the appropriate priority queue
        if ( m->prio < ATM_NO_OF_QUEUES ) {
            m->priority_next = priority_root[m->prio];
            priority_root[m->prio] = m;
        }
        m = m->inventory_next;
    }
    recalibrate = 0;
}

// .run( q ) Traverses an individual priority queue and cycles the machines in it once (except for queue 0)
void Factory::run( int q )
{
    Machine * m = priority_root[ q ];
    while ( m ) {
        if ( q > 0 && !m->sleep ) m->cycle();
        // Request a recalibrate if the prio field doesn't match the current queue
        if ( m->prio != q ) recalibrate = 1;
        // Move to the next machine
        m = m->priority_next;
    }
}

// .add( machine ) Adds a state machine to the factory by prepending it to the inventory list
Factory & Factory::add( Machine & machine )
{
    machine.inventory_next = inventory_root;
    inventory_root = &machine;
    machine.factory = this;
    recalibrate = 1;
    machine.cycle();
    return *this;
}

// .find() Search the factory inventory for a machine by instance label
Machine * Factory::find( const char label[] )
{
    Machine * m = inventory_root;
    while ( m ) {
        if ( strcmp( label, m->inst_label ) == 0 ) {
            return m;
        }
        m = m->inventory_next;
    }
    return 0;
}

int Factory::msgSend( const char label[], int msg )
{
    Machine * m = find( label );
    if ( m ) {
        m->msgWrite( msg );
        return 1;
    }
    return 0;
}

int Factory::msgSendClass( const char label[], int msg )
{
    int r = 0;
    Machine * m = inventory_root;
    while ( m ) {
        if ( strcmp( label, m->class_label ) == 0 ) {
            m->msgWrite( msg );
            r = 1;
        }
        m = m->inventory_next;
    }
    return 0;
}

// .cycle() executes one factory cycle (runs all priority queues a certain number of times)
Factory & Factory::cycle( void )
{
    if ( recalibrate ) calibrate();
    run( 1 ); run( 2 );	run( 1 ); run( 2 );
    run( 1 ); run( 3 );	run( 1 ); run( 4 );
    run( 1 ); run( 2 );	run( 1 ); run( 3 );
    run( 1 ); run( 2 );	run( 1 ); run( 0 );
    return  *this;
}

// TINYFACTORY - A factory for TinyMachines

// .add( machine ) Adds a state machine to the factory by prepending it to the inventory list
TinyFactory & TinyFactory::add( TinyMachine & machine )
{
    machine.inventory_next = inventory_root;
    inventory_root = &machine;
    machine.cycle();
    return *this;
}

// .cycle() executes the factory cycle
TinyFactory &  TinyFactory::cycle( void )
{
    TinyMachine * m = inventory_root;
    while ( m ) {
        if ( !m->sleep ) m->cycle();
        // Move to the next machine
        m = m->inventory_next;
    }
    return *this;
}

<commit_msg>Adapted factory::msgSend()<commit_after>/*
  Automaton.cpp - Library for creating and running Finite State Machines.
  Published under the MIT License (MIT), Copyright (c) 2015, J.P. van der Landen
*/

#include "Automaton.h"

void atm_timer::set( uint32_t v ) {
    value = v;
}

void atm_timer::begin( BaseMachine * machine, uint32_t v  ) {
    pmachine = machine;
    value = v;
}

int atm_timer_millis::expired( void ) {
    return value == ATM_TIMER_OFF ? 0 : millis() - pmachine->state_millis >= value;
}

int atm_timer_micros::expired( void ) {
    return value == ATM_TIMER_OFF ? 0 : micros() - pmachine->state_micros >= value;
}

void atm_counter::set( uint16_t v ) {
    value = v;
}

uint16_t atm_counter::decrement( void )
{
    return value > 0 && value != ATM_COUNTER_OFF ? --value : 0;
}

uint8_t atm_counter::expired()
{
    return value == ATM_COUNTER_OFF ? 0 : ( value > 0 ? 0 : 1 );
}

Machine & Machine::state(state_t state)
{
    next = state;
    last_trigger = -1;
    sleep = 0;
    if ( msg_autoclear ) msgClear();
    return *this;
}

state_t Machine::state()
{
    return current;
}

int Machine::trigger( int evt )
{
    if ( current > -1 ) {
        int new_state = read_state( state_table + ( current * state_width ) + evt + ATM_ON_EXIT + 1 );
        if ( new_state > -1 ) {
	      state( new_state );
          last_trigger = evt;
          return 1;
        }
    }
    return 0;
}

Machine &  Machine::toggle( state_t state1, state_t state2 )
{
    state( current == state1 ? state2 : state1 );
    return *this;
}

Machine & Machine::onSwitch( swcb_num_t callback )
{
    callback_num = callback;
    return *this;
}

Machine & Machine::onSwitch( swcb_sym_t callback, const char sym_s[], const char sym_e[] )
{
    callback_sym = callback;
    sym_states = sym_s;
    sym_events = sym_e;
    return *this;
}

Machine & Machine::label( const char label[] )
{
    inst_label = label;
    return *this;
}

int8_t Machine::priority()
{
    return prio;
}

Machine & Machine::priority( int8_t priority )
{
    prio = priority;
    return *this;
}

// .asleep() Returns true if the machine is in a sleeping state

uint8_t BaseMachine::asleep()
{
    return sleep;
}

Machine & Machine::begin( const state_t* tbl, int width )
{
    state_table = tbl;
    state_width = ATM_ON_EXIT + width + 2;
    prio = ATM_DEFAULT_PRIO;
    if ( !inst_label ) inst_label = class_label;
    return *this;
}

Machine & Machine::msgQueue( atm_msg_t msg[], int width )
{
    msg_table = msg;
    msg_width = width;
    return *this;
}

Machine & Machine::msgQueue( atm_msg_t msg[], int width, uint8_t autoclear )
{
    msg_table = msg;
    msg_width = width;
    msg_autoclear = autoclear;
    return *this;
}

unsigned char Machine::pinChange( uint8_t pin ) {

  unsigned char v = digitalRead( pin ) ? 1 : 0;
  if ( (( pinstate >> pin ) & 1 ) != ( v == 1 ) ) {
    pinstate ^= ( (uint32_t)1 << pin );
    return 1;
  }
  return 0;
}

int Machine::msgRead( uint8_t id_msg ) // Checks msg queue and removes 1 msg
{
  if ( msg_table[id_msg] > 0 ) {
      msg_table[id_msg]--;
      return 1;
  }
  return 0;
}

int Machine::msgRead( uint8_t id_msg, int cnt )
{
  if ( msg_table[id_msg] > 0 ) {
    if ( cnt >= msg_table[id_msg] ) {
        msg_table[id_msg] = 0;
    } else {
        msg_table[id_msg] -= cnt;
    }
    return 1;
  }
  return 0;
}

int Machine::msgRead( uint8_t id_msg, int cnt, int clear )
{
  if ( msg_table[id_msg] > 0 ) {
    if ( cnt >= msg_table[id_msg] ) {
        msg_table[id_msg] = 0;
    } else {
        msg_table[id_msg] -= cnt;
    }
	if ( clear ) {
		for ( int i = 0; i < msg_width; i++ ) {
			msg_table[i] = 0;
		}
    }
    return 1;
  }
  return 0;
}

int Machine::msgPeek( uint8_t id_msg )
{
  return msg_table[id_msg];
}

int Machine::msgClear( uint8_t id_msg ) // Destructive read (clears queue for this type)
{
  sleep = 0;
  if ( msg_table[id_msg] > 0 ) {
    msg_table[id_msg] = 0;
    return 1;
  }
  return 0;
}

Machine & Machine::msgClear()
{
  sleep = 0;
  for ( int i = 0; i < msg_width; i++ ) {
    msg_table[i] = 0;
  }
  return *this;
}

Machine & Machine::msgWrite( uint8_t id_msg )
{
  sleep = 0;
  msg_table[id_msg]++;
  return *this;
}

Machine & Machine::msgWrite( uint8_t id_msg, int cnt )
{
  sleep = 0;
  msg_table[id_msg] += cnt;
  return *this;
}

const char * Machine::map_symbol( int id, const char map[] )
{
  int cnt = 0;
  int i = 0;
  if ( id == -1 ) return "*NONE*";
  if ( id ==  0 ) return map;
  while ( 1 ) {
    if ( map[i] == '\0' && ++cnt == id ) {
      i++;
      break;
    }
    i++;
  }
  return &map[i];
}

// .cycle() Executes one cycle of a state machine
Machine & Machine::cycle()
{
    if ( !sleep ) {
        cycles++;
        if ( next != -1 ) {
            action( ATM_ON_SWITCH );
            if ( callback_sym || callback_num ) {
                if ( callback_sym ) {
                    callback_sym( inst_label,
                        map_symbol(      current, sym_states ),
                        map_symbol(         next, sym_states ),
                        map_symbol( last_trigger, sym_events ), millis() - state_millis, cycles );
                } else {
                    callback_num( inst_label, current, next, last_trigger, millis() - state_millis, cycles );
                }
            }
            if ( current > -1 )
		action( read_state( state_table + ( current * state_width ) + ATM_ON_EXIT ) );
            previous = current;
            current = next;
            next = -1;
            state_millis = millis();
            state_micros = micros();
            action( read_state( state_table + ( current * state_width ) + ATM_ON_ENTER ) );
            sleep = read_state( state_table + ( current * state_width ) + ATM_ON_LOOP ) == ATM_SLEEP;
            cycles = 0;
        }
        state_t i = read_state( state_table + ( current * state_width ) + ATM_ON_LOOP );
        if ( i != -1 ) { action( i ); }
        for ( i = ATM_ON_EXIT + 1; i < state_width; i++ ) {
            if ( ( read_state( state_table + ( current * state_width ) + i ) != -1 ) && ( i == state_width - 1 || event( i - ATM_ON_EXIT - 1 ) ) ) {
                state( read_state( state_table + ( current * state_width ) + i ) );
                last_trigger = i - ATM_ON_EXIT - 1;
                return *this;
            }
        }
    }
    return *this;
}

// TINY MACHINE

TinyMachine & TinyMachine::state(tiny_state_t state)
{
    next = state;
    sleep = 0;
    return *this;
}

tiny_state_t TinyMachine::state()
{
    return current;
}

int TinyMachine::trigger( int evt )
{
    if ( current > -1 ) {
        int new_state = tiny_read_state( state_table + ( current * state_width ) + evt + ATM_ON_EXIT + 1 );
        if ( new_state > -1 ) {
          state( new_state );
          return 1;
        }
    }
    return 0;
}

TinyMachine & TinyMachine::begin( const tiny_state_t* tbl, int width )
{
    state_table = tbl;
    state_width = ATM_ON_EXIT + width + 2;
    return *this;
}

// .cycle() Executes one cycle of a state machine
TinyMachine & TinyMachine::cycle()
{
    if ( !sleep ) {
        if ( next != -1 ) {
            action( ATM_ON_SWITCH );
            if ( current > -1 )
                action( tiny_read_state( state_table + ( current * state_width ) + ATM_ON_EXIT ) );
            previous = current;
            current = next;
            next = -1;
            state_millis = millis();
            state_micros = micros();
            action( tiny_read_state( state_table + ( current * state_width ) + ATM_ON_ENTER ) );
            sleep = tiny_read_state( state_table + ( current * state_width ) + ATM_ON_LOOP ) == ATM_SLEEP;
        }
        tiny_state_t i = tiny_read_state( state_table + ( current * state_width ) + ATM_ON_LOOP );
        if ( i != -1 ) { action( i ); }
        for ( i = ATM_ON_EXIT + 1; i < state_width; i++ ) {
            if ( ( tiny_read_state( state_table + ( current * state_width ) + i ) != -1 ) && ( i == state_width - 1 || event( i - ATM_ON_EXIT - 1 ) ) ) {
                state( tiny_read_state( state_table + ( current * state_width ) + i ) );
                return *this;
            }
        }
    }
    return *this;
}

// FACTORY

// .calibrate() Distributes the machines in the inventory to the appropriate priority queues
void Factory::calibrate( void )
{
    // Reset all priority queues to empty lists
    for ( int8_t i = 0; i < ATM_NO_OF_QUEUES; i++ )
        priority_root[i] = 0;
    // Walk the inventory list that contains all state machines in this factory
    Machine * m = inventory_root;
    while ( m ) {
        // Prepend every machine to the appropriate priority queue
        if ( m->prio < ATM_NO_OF_QUEUES ) {
            m->priority_next = priority_root[m->prio];
            priority_root[m->prio] = m;
        }
        m = m->inventory_next;
    }
    recalibrate = 0;
}

// .run( q ) Traverses an individual priority queue and cycles the machines in it once (except for queue 0)
void Factory::run( int q )
{
    Machine * m = priority_root[ q ];
    while ( m ) {
        if ( q > 0 && !m->sleep ) m->cycle();
        // Request a recalibrate if the prio field doesn't match the current queue
        if ( m->prio != q ) recalibrate = 1;
        // Move to the next machine
        m = m->priority_next;
    }
}

// .add( machine ) Adds a state machine to the factory by prepending it to the inventory list
Factory & Factory::add( Machine & machine )
{
    machine.inventory_next = inventory_root;
    inventory_root = &machine;
    machine.factory = this;
    recalibrate = 1;
    machine.cycle();
    return *this;
}

// .find() Search the factory inventory for a machine by instance label
Machine * Factory::find( const char label[] )
{
    Machine * m = inventory_root;
    while ( m ) {
        if ( strcmp( label, m->inst_label ) == 0 ) {
            return m;
        }
        m = m->inventory_next;
    }
    return 0;
}

int Factory::msgSend( const char label[], int msg )
{
    int r = 0;
    Machine * m = inventory_root;
    while ( m ) {
        if ( strcmp( label, m->inst_label ) == 0 ) {
            m->msgWrite( msg );
            r = 1;
        }
        m = m->inventory_next;
    }
    return 0;
}

int Factory::msgSendClass( const char label[], int msg )
{
    int r = 0;
    Machine * m = inventory_root;
    while ( m ) {
        if ( strcmp( label, m->class_label ) == 0 ) {
            m->msgWrite( msg );
            r = 1;
        }
        m = m->inventory_next;
    }
    return 0;
}

// .cycle() executes one factory cycle (runs all priority queues a certain number of times)
Factory & Factory::cycle( void )
{
    if ( recalibrate ) calibrate();
    run( 1 ); run( 2 );	run( 1 ); run( 2 );
    run( 1 ); run( 3 );	run( 1 ); run( 4 );
    run( 1 ); run( 2 );	run( 1 ); run( 3 );
    run( 1 ); run( 2 );	run( 1 ); run( 0 );
    return  *this;
}

// TINYFACTORY - A factory for TinyMachines

// .add( machine ) Adds a state machine to the factory by prepending it to the inventory list
TinyFactory & TinyFactory::add( TinyMachine & machine )
{
    machine.inventory_next = inventory_root;
    inventory_root = &machine;
    machine.cycle();
    return *this;
}

// .cycle() executes the factory cycle
TinyFactory &  TinyFactory::cycle( void )
{
    TinyMachine * m = inventory_root;
    while ( m ) {
        if ( !m->sleep ) m->cycle();
        // Move to the next machine
        m = m->inventory_next;
    }
    return *this;
}

<|endoftext|>