<commit_before>/*
 * ALURE  OpenAL utility library
 * Copyright (c) 2009-2010 by Chris Robinson.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "config.h"

#include "main.h"

#include <string.h>
#include <assert.h>

#include <istream>

#include <sndfile.h>

#ifdef _WIN32
#define SNDFILE_LIB "libsndfile-1.dll"
#elif defined(__APPLE__)
#define SNDFILE_LIB "libsndfile.1.dylib"
#else
#define SNDFILE_LIB "libsndfile.so.1"
#endif

static void *sndfile_handle;
#define MAKE_FUNC(x) static typeof(x)* p##x
MAKE_FUNC(sf_close);
MAKE_FUNC(sf_open_virtual);
MAKE_FUNC(sf_readf_short);
MAKE_FUNC(sf_seek);
#undef MAKE_FUNC

struct sndStream : public alureStream {
private:
    SNDFILE *sndFile;
    SF_INFO sndInfo;
    ALenum format;

public:
    static void Init()
    {
        sndfile_handle = OpenLib(SNDFILE_LIB);
        if(!sndfile_handle) return;

        LOAD_FUNC(sndfile_handle, sf_close);
        LOAD_FUNC(sndfile_handle, sf_open_virtual);
        LOAD_FUNC(sndfile_handle, sf_readf_short);
        LOAD_FUNC(sndfile_handle, sf_seek);
    }
    static void Deinit()
    {
        if(sndfile_handle)
            CloseLib(sndfile_handle);
        sndfile_handle = NULL;
    }

    virtual bool IsValid()
    { return sndFile != NULL; }

    virtual bool GetFormat(ALenum *fmt, ALuint *frequency, ALuint *blockalign)
    {
        if(format == AL_NONE)
            format = GetSampleFormat(sndInfo.channels, 16, false);
        *fmt = format;
        *frequency = sndInfo.samplerate;
        *blockalign = sndInfo.channels*2;
        return true;
    }

    virtual ALuint GetData(ALubyte *data, ALuint bytes)
    {
        const ALuint frameSize = 2*sndInfo.channels;
        return psf_readf_short(sndFile, (short*)data, bytes/frameSize) * frameSize;
    }

    virtual bool Rewind()
    {
        if(psf_seek(sndFile, 0, SEEK_SET) != -1)
            return true;

        SetError("Seek failed");
        return false;
    }

    sndStream(std::istream *_fstream)
      : alureStream(_fstream), sndFile(NULL), format(AL_NONE)
    {
        memset(&sndInfo, 0, sizeof(sndInfo));

        if(!sndfile_handle) return;

        static SF_VIRTUAL_IO streamIO = {
            get_filelen, seek,
            read, write, tell
        };
        sndFile = psf_open_virtual(&streamIO, SFM_READ, &sndInfo, this);
    }

    virtual ~sndStream()
    {
        if(sndFile)
            psf_close(sndFile);
        sndFile = NULL;
    }

private:
    // libSndFile iostream callbacks
    static sf_count_t get_filelen(void *user_data)
    {
        std::istream *stream = static_cast<sndStream*>(user_data)->fstream;
        stream->clear();

        std::streampos len = -1;
        std::streampos pos = stream->tellg();
        if(stream->seekg(0, std::ios_base::end))
        {
            len = stream->tellg();
            stream->seekg(pos);
        }

        return len;
    }

    static sf_count_t seek(sf_count_t offset, int whence, void *user_data)
    {
        std::istream *stream = static_cast<sndStream*>(user_data)->fstream;
        stream->clear();

        if(whence == SEEK_CUR)
            stream->seekg(offset, std::ios_base::cur);
        else if(whence == SEEK_SET)
            stream->seekg(offset, std::ios_base::beg);
        else if(whence == SEEK_END)
            stream->seekg(offset, std::ios_base::end);
        else
            return -1;

        return stream->tellg();
    }

    static sf_count_t read(void *ptr, sf_count_t count, void *user_data)
    {
        std::istream *stream = static_cast<sndStream*>(user_data)->fstream;
        stream->clear();
        stream->read(static_cast<char*>(ptr), count);
        return stream->gcount();
    }

    static sf_count_t write(const void*, sf_count_t, void*)
    { return -1; }

    static sf_count_t tell(void *user_data)
    {
        std::istream *stream = static_cast<sndStream*>(user_data)->fstream;
        stream->clear();
        return stream->tellg();
    }
};
static DecoderDecl<sndStream,0> sndStream_decoder;
<commit_msg>Return error if seeking fails in libsndfile's seek callback<commit_after>/*
 * ALURE  OpenAL utility library
 * Copyright (c) 2009-2010 by Chris Robinson.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "config.h"

#include "main.h"

#include <string.h>
#include <assert.h>

#include <istream>

#include <sndfile.h>

#ifdef _WIN32
#define SNDFILE_LIB "libsndfile-1.dll"
#elif defined(__APPLE__)
#define SNDFILE_LIB "libsndfile.1.dylib"
#else
#define SNDFILE_LIB "libsndfile.so.1"
#endif

static void *sndfile_handle;
#define MAKE_FUNC(x) static typeof(x)* p##x
MAKE_FUNC(sf_close);
MAKE_FUNC(sf_open_virtual);
MAKE_FUNC(sf_readf_short);
MAKE_FUNC(sf_seek);
#undef MAKE_FUNC

struct sndStream : public alureStream {
private:
    SNDFILE *sndFile;
    SF_INFO sndInfo;
    ALenum format;

public:
    static void Init()
    {
        sndfile_handle = OpenLib(SNDFILE_LIB);
        if(!sndfile_handle) return;

        LOAD_FUNC(sndfile_handle, sf_close);
        LOAD_FUNC(sndfile_handle, sf_open_virtual);
        LOAD_FUNC(sndfile_handle, sf_readf_short);
        LOAD_FUNC(sndfile_handle, sf_seek);
    }
    static void Deinit()
    {
        if(sndfile_handle)
            CloseLib(sndfile_handle);
        sndfile_handle = NULL;
    }

    virtual bool IsValid()
    { return sndFile != NULL; }

    virtual bool GetFormat(ALenum *fmt, ALuint *frequency, ALuint *blockalign)
    {
        if(format == AL_NONE)
            format = GetSampleFormat(sndInfo.channels, 16, false);
        *fmt = format;
        *frequency = sndInfo.samplerate;
        *blockalign = sndInfo.channels*2;
        return true;
    }

    virtual ALuint GetData(ALubyte *data, ALuint bytes)
    {
        const ALuint frameSize = 2*sndInfo.channels;
        return psf_readf_short(sndFile, (short*)data, bytes/frameSize) * frameSize;
    }

    virtual bool Rewind()
    {
        if(psf_seek(sndFile, 0, SEEK_SET) != -1)
            return true;

        SetError("Seek failed");
        return false;
    }

    sndStream(std::istream *_fstream)
      : alureStream(_fstream), sndFile(NULL), format(AL_NONE)
    {
        memset(&sndInfo, 0, sizeof(sndInfo));

        if(!sndfile_handle) return;

        static SF_VIRTUAL_IO streamIO = {
            get_filelen, seek,
            read, write, tell
        };
        sndFile = psf_open_virtual(&streamIO, SFM_READ, &sndInfo, this);
    }

    virtual ~sndStream()
    {
        if(sndFile)
            psf_close(sndFile);
        sndFile = NULL;
    }

private:
    // libSndFile iostream callbacks
    static sf_count_t get_filelen(void *user_data)
    {
        std::istream *stream = static_cast<sndStream*>(user_data)->fstream;
        stream->clear();

        std::streampos len = -1;
        std::streampos pos = stream->tellg();
        if(stream->seekg(0, std::ios_base::end))
        {
            len = stream->tellg();
            stream->seekg(pos);
        }

        return len;
    }

    static sf_count_t seek(sf_count_t offset, int whence, void *user_data)
    {
        std::istream *stream = static_cast<sndStream*>(user_data)->fstream;
        stream->clear();

        if((whence == SEEK_CUR && stream->seekg(offset, std::ios_base::cur)) ||
           (whence == SEEK_SET && stream->seekg(offset, std::ios_base::beg)) ||
           (whence == SEEK_END && stream->seekg(offset, std::ios_base::end)))
            return stream->tellg();

        return -1;
    }

    static sf_count_t read(void *ptr, sf_count_t count, void *user_data)
    {
        std::istream *stream = static_cast<sndStream*>(user_data)->fstream;
        stream->clear();
        stream->read(static_cast<char*>(ptr), count);
        return stream->gcount();
    }

    static sf_count_t write(const void*, sf_count_t, void*)
    { return -1; }

    static sf_count_t tell(void *user_data)
    {
        std::istream *stream = static_cast<sndStream*>(user_data)->fstream;
        stream->clear();
        return stream->tellg();
    }
};
static DecoderDecl<sndStream,0> sndStream_decoder;
<|endoftext|>


<commit_before>#include <qdebug.h>
#include <QThread>
#include <QMutex>
#include <MG.h>
#include <configuration.h>
#include<string>
#include "XbeeLink.h"

XbeeLink::XbeeLink(QString portName, int baudRate) :
	m_xbeeCon(NULL),
    m_portName(NULL),
    m_portNameLength(0),
    m_baudRate(baudRate),
    m_connected(false),
	m_addrHigh(0),
    m_addrLow(0)
{

	/* setup the xbee */
	this->setPortName(portName);
}

XbeeLink::~XbeeLink()
{
	if(m_portName)
	{
		delete m_portName;
		m_portName = NULL;
	}
	_disconnect();
}

QString XbeeLink::getPortName() const
{
	QString portName;
	for(unsigned int i = 0;i<this->m_portNameLength;i++)
	{
		portName.append(this->m_portName[i]);
	}
	return portName;
}

int XbeeLink::getBaudRate() const
{
	return this->m_baudRate;
}

bool XbeeLink::setPortName(QString portName)
{
	bool reconnect(false);
	if(this->m_connected)
	{
		_disconnect();
		reconnect = true;
	}
	if(m_portName)
	{
		delete m_portName;
		m_portName = NULL;
	}
	QStringList list = portName.split(QRegExp("\\s+"),QString::SkipEmptyParts);
	if(list.size()>0)
	{
		this->m_portNameLength = list[0].size()+1;
		m_portName = new char[this->m_portNameLength];
		for(int i=0;i<list[0].size();i++)
		{
			this->m_portName[i]=list[0][i].toLatin1();
		}
		this->m_portName[list[0].size()] = '\0';
	}
	else
	{
		this->m_portNameLength = 1;
		m_portName = new char[this->m_portNameLength];
		this->m_portName[0] = '\0';
	}

	bool retVal(true);
	if(reconnect)
	{
		retVal = _connect();
	}

	return retVal;
}

bool XbeeLink::setBaudRate(int rate)
{
	bool reconnect(false);
	if(this->m_connected)
	{
		_disconnect();
		reconnect = true;
	}
	bool retVal(true);
	this->m_baudRate = rate;
	if(reconnect)
	{
		retVal = _connect();
	}
	return retVal;
}

QString XbeeLink::getName() const
{
	return this->m_name;
}

bool XbeeLink::isConnected() const
{
	return this->m_connected;
}

qint64 XbeeLink::getConnectionSpeed() const
{
	return this->m_baudRate;
}

qint64 XbeeLink::getCurrentInDataRate() const
{
    return 0;
}

qint64 XbeeLink::getCurrentOutDataRate() const
{
    return 0;
}

bool XbeeLink::hardwareConnect()
{
	emit tryConnectBegin(true);
	if(this->isConnected())
	{
		_disconnect();
	}
	if (*this->m_portName == '\0')
	{
		return false;
	}
	if (xbee_setupAPI(this->m_portName,this->m_baudRate,0x2B,0x3E8) == -1)
		{
		  /* oh no... it failed */
			qDebug() <<"xbee_setup() failed...\n";
			emit tryConnectEnd(true);
			return false;
		}
	this->m_xbeeCon = xbee_newcon('A',xbee2_data,0x13A200,0x403D0935);
	emit tryConnectEnd(true);
	this->m_connected = true;
	emit connected();
	return true;
}

bool XbeeLink::_connect(void)
{
	if (this->isRunning()) _disconnect();
    this->start(LowPriority);
    return true;
}

bool XbeeLink::_disconnect(void)
{
	if(this->isRunning()) this->terminate(); //stop running the thread, restart it upon connect

	if(this->m_xbeeCon)
	{
		xbee_end();
		this->m_xbeeCon = NULL;
	}
	this->m_connected = false;

	emit disconnected();
	return true;
}

void XbeeLink::writeBytes(const char *bytes, qint64 length)  // TO DO: delete the data array
{
	char *data;
	data = new char[length];
	for(long i=0;i<length;i++)
	{
		data[i] = bytes[i];
	}
	if(!xbee_nsenddata(this->m_xbeeCon,data,length)) // return value of 0 is successful written
	{
        // Log the amount and time written out for future data rate calculations.
        QMutexLocker dataRateLocker(&dataRateMutex);
        logDataRateToBuffer(outDataWriteAmounts, outDataWriteTimes, &outDataIndex, length, QDateTime::currentMSecsSinceEpoch());
	}
	else
	{
		_disconnect();
		emit communicationError(tr("Link Error"), QString("Error on link: %1. Could not send data - link is disconnected!").arg(getName()));
	}
}

void XbeeLink::readBytes()
{
	xbee_pkt *xbeePkt;
	xbeePkt = xbee_getpacketwait(this->m_xbeeCon);
	if(!(NULL==xbeePkt))
	{
		QByteArray data;
		for(unsigned int i=0;i<=xbeePkt->datalen;i++)
		{
			data.push_back(xbeePkt->data[i]);
        }

        emit bytesReceived(this, data);

        // Log the amount and time received for future data rate calculations.
        QMutexLocker dataRateLocker(&dataRateMutex);
        logDataRateToBuffer(inDataWriteAmounts, inDataWriteTimes, &inDataIndex, data.length(), QDateTime::currentMSecsSinceEpoch());
	}
}

void XbeeLink::run()
{
    // Initialize the connection
	if(this->hardwareConnect())
	{
		// Qt way to make clear what a while(1) loop does
		forever
		{
			this->readBytes();
	    }
	}
}

bool XbeeLink::setRemoteAddressHigh(quint32 high)
{
	this->m_addrHigh = high;
	return true;
}

bool XbeeLink::setRemoteAddressLow(quint32 low)
{
	this->m_addrLow = low;
	return true;
}

/*
void CALLTYPE XbeeLink::portCallback(xbee_con *xbeeCon, xbee_pkt *XbeePkt)
{
	QByteArray buf;
	for(quint8 i=0;i<XbeePkt->datalen;i++)
	{
		buf.push_back(XbeePkt->data[i]);
	}
	emit bytesReceived(this, buf);
}*/
<commit_msg>Update to new input/output data rate apis<commit_after>#include <qdebug.h>
#include <QThread>
#include <QMutex>
#include <MG.h>
#include <configuration.h>
#include<string>
#include "XbeeLink.h"

XbeeLink::XbeeLink(QString portName, int baudRate) :
	m_xbeeCon(NULL),
    m_portName(NULL),
    m_portNameLength(0),
    m_baudRate(baudRate),
    m_connected(false),
	m_addrHigh(0),
    m_addrLow(0)
{

	/* setup the xbee */
	this->setPortName(portName);
}

XbeeLink::~XbeeLink()
{
	if(m_portName)
	{
		delete m_portName;
		m_portName = NULL;
	}
	_disconnect();
}

QString XbeeLink::getPortName() const
{
	QString portName;
	for(unsigned int i = 0;i<this->m_portNameLength;i++)
	{
		portName.append(this->m_portName[i]);
	}
	return portName;
}

int XbeeLink::getBaudRate() const
{
	return this->m_baudRate;
}

bool XbeeLink::setPortName(QString portName)
{
	bool reconnect(false);
	if(this->m_connected)
	{
		_disconnect();
		reconnect = true;
	}
	if(m_portName)
	{
		delete m_portName;
		m_portName = NULL;
	}
	QStringList list = portName.split(QRegExp("\\s+"),QString::SkipEmptyParts);
	if(list.size()>0)
	{
		this->m_portNameLength = list[0].size()+1;
		m_portName = new char[this->m_portNameLength];
		for(int i=0;i<list[0].size();i++)
		{
			this->m_portName[i]=list[0][i].toLatin1();
		}
		this->m_portName[list[0].size()] = '\0';
	}
	else
	{
		this->m_portNameLength = 1;
		m_portName = new char[this->m_portNameLength];
		this->m_portName[0] = '\0';
	}

	bool retVal(true);
	if(reconnect)
	{
		retVal = _connect();
	}

	return retVal;
}

bool XbeeLink::setBaudRate(int rate)
{
	bool reconnect(false);
	if(this->m_connected)
	{
		_disconnect();
		reconnect = true;
	}
	bool retVal(true);
	this->m_baudRate = rate;
	if(reconnect)
	{
		retVal = _connect();
	}
	return retVal;
}

QString XbeeLink::getName() const
{
	return this->m_name;
}

bool XbeeLink::isConnected() const
{
	return this->m_connected;
}

qint64 XbeeLink::getConnectionSpeed() const
{
	return this->m_baudRate;
}

qint64 XbeeLink::getCurrentInDataRate() const
{
    return 0;
}

qint64 XbeeLink::getCurrentOutDataRate() const
{
    return 0;
}

bool XbeeLink::hardwareConnect()
{
	emit tryConnectBegin(true);
	if(this->isConnected())
	{
		_disconnect();
	}
	if (*this->m_portName == '\0')
	{
		return false;
	}
	if (xbee_setupAPI(this->m_portName,this->m_baudRate,0x2B,0x3E8) == -1)
		{
		  /* oh no... it failed */
			qDebug() <<"xbee_setup() failed...\n";
			emit tryConnectEnd(true);
			return false;
		}
	this->m_xbeeCon = xbee_newcon('A',xbee2_data,0x13A200,0x403D0935);
	emit tryConnectEnd(true);
	this->m_connected = true;
	emit connected();
	return true;
}

bool XbeeLink::_connect(void)
{
	if (this->isRunning()) _disconnect();
    this->start(LowPriority);
    return true;
}

bool XbeeLink::_disconnect(void)
{
	if(this->isRunning()) this->terminate(); //stop running the thread, restart it upon connect

	if(this->m_xbeeCon)
	{
		xbee_end();
		this->m_xbeeCon = NULL;
	}
	this->m_connected = false;

	emit disconnected();
	return true;
}

void XbeeLink::writeBytes(const char *bytes, qint64 length)  // TO DO: delete the data array
{
	char *data;
	data = new char[length];
	for(long i=0;i<length;i++)
	{
		data[i] = bytes[i];
	}
	if(!xbee_nsenddata(this->m_xbeeCon,data,length)) // return value of 0 is successful written
	{
		_logOutputDataRate(length, QDateTime::currentMSecsSinceEpoch());
	}
	else
	{
		_disconnect();
		emit communicationError(tr("Link Error"), QString("Error on link: %1. Could not send data - link is disconnected!").arg(getName()));
	}
}

void XbeeLink::readBytes()
{
	xbee_pkt *xbeePkt;
	xbeePkt = xbee_getpacketwait(this->m_xbeeCon);
	if(!(NULL==xbeePkt))
	{
		QByteArray data;
		for(unsigned int i=0;i<=xbeePkt->datalen;i++)
		{
			data.push_back(xbeePkt->data[i]);
        }

		_logInputDataRate(data.length(), QDateTime::currentMSecsSinceEpoch());
		emit bytesReceived(this, data);
	}
}

void XbeeLink::run()
{
    // Initialize the connection
	if(this->hardwareConnect())
	{
		// Qt way to make clear what a while(1) loop does
		forever
		{
			this->readBytes();
	    }
	}
}

bool XbeeLink::setRemoteAddressHigh(quint32 high)
{
	this->m_addrHigh = high;
	return true;
}

bool XbeeLink::setRemoteAddressLow(quint32 low)
{
	this->m_addrLow = low;
	return true;
}

/*
void CALLTYPE XbeeLink::portCallback(xbee_con *xbeeCon, xbee_pkt *XbeePkt)
{
	QByteArray buf;
	for(quint8 i=0;i<XbeePkt->datalen;i++)
	{
		buf.push_back(XbeePkt->data[i]);
	}
	emit bytesReceived(this, buf);
}*/
<|endoftext|>


<commit_before>// Copyright (c) 2020 ASMlover. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list ofconditions and the following disclaimer.
//
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materialsprovided with the
//    distribution.
//
// 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <cstdarg>
#include <algorithm>
#include <iostream>
#include "chunk.hh"
#include "compiler.hh"
#include "native_object.hh"
#include "function_object.hh"
#include "closure_object.hh"
#include "vm.hh"

namespace tadpole {

class CallFrame final : public Copyable {
  ClosureObject* closure_{};
  const u8_t* ip_{};
  sz_t stack_begpos_{};
public:
  CallFrame() noexcept {}
  CallFrame(ClosureObject* c, const u8_t* i, sz_t begpos = 0) noexcept
    : closure_(c), ip_(i), stack_begpos_(begpos) {
  }

  inline ClosureObject* closure() const noexcept { return closure_; }
  inline FunctionObject* frame_fn() const noexcept { return closure_->fn(); }
  inline Chunk* frame_chunk() const noexcept { return frame_fn()->chunk(); }
  inline const u8_t* ip() const noexcept { return ip_; }
  inline u8_t get_ip(sz_t i) const noexcept { return ip_[i]; }
  inline u8_t inc_ip() noexcept { return *ip_++; }
  inline u8_t dec_ip() noexcept { return *ip_--; }
  inline sz_t stack_begpos() const noexcept { return stack_begpos_; }
};

VM::VM() noexcept {
  gcompiler_ = new GlobalCompiler();
  stack_.reserve(kDefaultCap);

  // TODO: register_builtins();
}

VM::~VM() {
  delete gcompiler_;

  globals_.clear();
  interned_strings_.clear();

  while (!all_objects_.empty()) {
    auto* o = all_objects_.back();
    all_objects_.pop_back();

    reclaim_object(o);
  }
}

void VM::define_native(const str_t& name, TadpoleCFun&& fn) {
  globals_[name] = NativeObject::create(*this, std::move(fn));
}

void VM::append_object(BaseObject* o) {
  if (all_objects_.size() >= kGCThreshold)
    collect();
  all_objects_.push_back(o);
}

void VM::mark_object(BaseObject* o) {
  if (o == nullptr || o->is_marked())
    return;

#if defined(_TADPOLE_DEBUG_GC)
  std::cout << "[" << o << "] mark object: `" << o->stringify() << "`" << std::endl;
#endif

  o->set_marked(true);
  worklist_.push_back(o);
}

void VM::mark_value(const Value& v) {
  if (v.is_object())
    mark_object(v.as_object());
}

InterpretRet VM::interpret(const str_t& source_bytes) {
  FunctionObject* fn = gcompiler_->compile(*this, source_bytes);
  if (fn == nullptr)
    return InterpretRet::ECOMPILE;

  push(fn);
  ClosureObject* closure = ClosureObject::create(*this, fn);
  pop();
  call(closure, 0);

  return run();
}

void VM::collect() {
  // mark root objects
  gcompiler_->mark_compiler();
  for (auto& v : stack_)
    mark_value(v);
  for (auto& f : frames_)
    mark_object(f.closure());
  for (auto& g : globals_)
    mark_value(g.second);
  for (auto* u = open_upvalues_; u != nullptr; u = u->next())
    mark_object(u);

  // mark reference objects
  while (!worklist_.empty()) {
    auto* o = worklist_.back();
    worklist_.pop_back();
    o->gc_blacken(*this);
  }

  // delete unmarked interned string objects
  for (auto it = interned_strings_.begin(); it != interned_strings_.end();) {
    if (!it->second->is_marked())
      interned_strings_.erase(it++);
    else
      ++it;
  }

  // sweep and reclaim unmarked objects
  for (auto it = all_objects_.begin(); it != all_objects_.end();) {
    if (!(*it)->is_marked()) {
      reclaim_object(*it);
      all_objects_.erase(it++);
    }
    else {
      (*it)->set_marked(false);
      ++it;
    }
  }

  gc_threshold_ = std::max(kGCThreshold, all_objects_.size() * kGCFactor);
}

void VM::reclaim_object(BaseObject* o) {
#if defined(_TADPOLE_DEBUG_GC)
  std::cout << "[" << o << "] reclaim object: `" << o->stringify() << "`" << std::endl;
#endif

  delete o;
}

void VM::reset() {
  stack_.clear();
  frames_.clear();
  open_upvalues_ = nullptr;
}

void VM::runtime_error(const char* format, ...) {
  std::cerr << "Traceback (most recent call last):" << std::endl;
  for (auto it = frames_.rbegin(); it != frames_.rend(); ++it) {
    auto& frame = *it;

    sz_t i = frame.frame_chunk()->offset_with(frame.ip()) - 1;
    std::cerr
      << "  [LINE: " << frame.frame_chunk()->get_line(i) << "] in "
      << "`" << frame.frame_fn()->name_asstr() << "()`"
      << std::endl;
  }

  va_list ap;
  va_start(ap, format);
  std::vfprintf(stderr, format, ap);
  va_end(ap);
  std::fprintf(stderr, "\n");

  reset();
}

void VM::push(Value value) noexcept {
  stack_.push_back(value);
}

Value VM::pop() noexcept {
  Value value = stack_.back();
  stack_.pop_back();
  return value;
}

const Value& VM::peek(sz_t distance) const noexcept {
  return stack_[stack_.size() - 1 - distance];
}

bool VM::call(ClosureObject* closure, sz_t argc) {
  FunctionObject* fn = closure->fn();
  if (fn->arity() != argc) {
    runtime_error("%s() takes exactly %ud arguments (%ud given)",
        fn->name_asstr(), fn->arity(), argc);
    return false;
  }

  frames_.push_back(CallFrame(closure, fn->chunk()->codes(), stack_.size() - argc - 1));
  return true;
}

bool VM::call(const Value& callee, sz_t argc) {
  if (callee.is_object()) {
    switch (callee.objtype()) {
    case ObjType::NATIVE:
      {
        Value* args = nullptr;
        if (argc > 0 && stack_.size() > argc)
          args = &stack_[stack_.size() - argc];

        Value result = callee.as_native()->fn()(argc, args);
        stack_.resize(stack_.size() - argc - 1);
        push(result);

        return true;
      }
    case ObjType::CLOSURE: return call(callee.as_closure(), argc);
    }
  }

  runtime_error("can only call function");
  return false;
}

UpvalueObject* VM::capture_upvalue(Value* local) {
  if (open_upvalues_ == nullptr) {
    open_upvalues_ = UpvalueObject::create(*this, local);
    return open_upvalues_;
  }

  UpvalueObject* upvalue = open_upvalues_;
  UpvalueObject* pre_upvalue = nullptr;
  while (upvalue != nullptr && upvalue->value() > local) {
    pre_upvalue = upvalue;
    upvalue = upvalue->next();
  }
  if (upvalue != nullptr && upvalue->value() == local)
    return upvalue;

  UpvalueObject* new_upvalue = UpvalueObject::create(*this, local, upvalue);
  if (pre_upvalue == nullptr)
    open_upvalues_ = new_upvalue;
  else
    pre_upvalue->set_next(new_upvalue);
  return new_upvalue;
}

void VM::close_upvalues(Value* last) {
  while (open_upvalues_ != nullptr && open_upvalues_->value() >= last) {
    UpvalueObject* upvalue = open_upvalues_;
    upvalue->set_closed(upvalue->value_asref());
    upvalue->set_value(upvalue->closed_asptr());

    open_upvalues_ = upvalue->next();
  }
}

InterpretRet VM::run() {
  CallFrame* frame = &frames_.back();

#define _RDBYTE()   frame->inc_ip()
#define _RDCONST()  frame->frame_chunk()->get_constant(_RDBYTE())
#define _RDSTR()    _RDCONST().as_string()
#define _RDCSTR()   _RDCONST().as_cstring()
#define _BINOP(op)  do {\
    if (!peek(0).is_numeric() || !peek(1).is_numeric()) {\
      runtime_error("operands must be two numerics");\
      return InterpretRet::ERUNTIME;\
    }\
    double b = pop().as_numeric();\
    double a = pop().as_numeric();\
    push(a op b);\
  } while (false)

  for (;;) {
#if defined(_TADPOLE_DEBUG_VM)
    auto* frame_chunk = frame->frame_chunk();
    std::cout << "          ";
    for (auto& v : stack_)
      std::cout << "[" << v << "]";
    std::cout << std::endl;
    frame_chunk->dis_code(frame_chunk->offset_with(frame->ip()));
#endif

    switch (Code c = as_type<Code>(_RDBYTE())) {
    case Code::CONSTANT: push(_RDCONST()); break;
    case Code::NIL: push(nullptr); break;
    case Code::FALSE: push(false); break;
    case Code::TRUE: push(true); break;
    case Code::POP: pop(); break;
    case Code::DEF_GLOBAL:
      {
        const char* name = _RDCSTR();
        if (auto it = globals_.find(name); it != globals_.end()) {
          runtime_error("name `%s` is redefined", name);
          return InterpretRet::ERUNTIME;
        }
        else {
          globals_[name] = pop();
        }
      } break;
    case Code::GET_GLOBAL:
      {
        const char* name = _RDCSTR();
        if (auto it = globals_.find(name); it != globals_.end()) {
          push(it->second);
        }
        else {
          runtime_error("name `%s` is not defined", name);
          return InterpretRet::ERUNTIME;
        }
      } break;
    case Code::SET_GLOBAL:
      {
        const char* name = _RDCSTR();
        if (auto it = globals_.find(name); it != globals_.end()) {
          it->second = peek();
        }
        else {
          runtime_error("name `%s` is not defined", name);
          return InterpretRet::ERUNTIME;
        }
      } break;
    case Code::GET_LOCAL: push(stack_[frame->stack_begpos() + _RDBYTE()]); break;
    case Code::SET_LOCAL: stack_[frame->stack_begpos() + _RDBYTE()] = peek(); break;
    case Code::GET_UPVALUE:
      {
        u8_t slot = _RDBYTE();
        push(frame->closure()->get_upvalue(slot)->value_asref());
      } break;
    case Code::SET_UPVALUE:
      {
        u8_t slot = _RDBYTE();
        frame->closure()->get_upvalue(slot)->set_value(peek());
      } break;
    case Code::ADD:
      {
        if (peek(0).is_string() && peek(1).is_string()) {
          StringObject* b = peek(0).as_string();
          StringObject* a = peek(1).as_string();
          StringObject* s = StringObject::concat(*this, a, b);
          pop();
          pop();
          push(s);
        }
        else if (peek(0).is_numeric() && peek(1).is_numeric()) {
          double b = pop().as_numeric();
          double a = pop().as_numeric();
          push(a + b);
        }
        else {
          runtime_error("operands must be two strings or two numerics");
          return InterpretRet::ERUNTIME;
        }
      } break;
    case Code::SUB: _BINOP(-); break;
    case Code::MUL: _BINOP(*); break;
    case Code::DIV: _BINOP(/); break;
    }
  }

#undef _BINOP
#undef _RDCSTR
#undef _RDSTR
#undef _RDCONST
#undef _RDBYTE

  return InterpretRet::OK;
}

}
<commit_msg>:construction: chore(vm): add CALLING execute code for vm<commit_after>// Copyright (c) 2020 ASMlover. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list ofconditions and the following disclaimer.
//
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materialsprovided with the
//    distribution.
//
// 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <cstdarg>
#include <algorithm>
#include <iostream>
#include "chunk.hh"
#include "compiler.hh"
#include "native_object.hh"
#include "function_object.hh"
#include "closure_object.hh"
#include "vm.hh"

namespace tadpole {

class CallFrame final : public Copyable {
  ClosureObject* closure_{};
  const u8_t* ip_{};
  sz_t stack_begpos_{};
public:
  CallFrame() noexcept {}
  CallFrame(ClosureObject* c, const u8_t* i, sz_t begpos = 0) noexcept
    : closure_(c), ip_(i), stack_begpos_(begpos) {
  }

  inline ClosureObject* closure() const noexcept { return closure_; }
  inline FunctionObject* frame_fn() const noexcept { return closure_->fn(); }
  inline Chunk* frame_chunk() const noexcept { return frame_fn()->chunk(); }
  inline const u8_t* ip() const noexcept { return ip_; }
  inline u8_t get_ip(sz_t i) const noexcept { return ip_[i]; }
  inline u8_t inc_ip() noexcept { return *ip_++; }
  inline u8_t dec_ip() noexcept { return *ip_--; }
  inline sz_t stack_begpos() const noexcept { return stack_begpos_; }
};

VM::VM() noexcept {
  gcompiler_ = new GlobalCompiler();
  stack_.reserve(kDefaultCap);

  // TODO: register_builtins();
}

VM::~VM() {
  delete gcompiler_;

  globals_.clear();
  interned_strings_.clear();

  while (!all_objects_.empty()) {
    auto* o = all_objects_.back();
    all_objects_.pop_back();

    reclaim_object(o);
  }
}

void VM::define_native(const str_t& name, TadpoleCFun&& fn) {
  globals_[name] = NativeObject::create(*this, std::move(fn));
}

void VM::append_object(BaseObject* o) {
  if (all_objects_.size() >= kGCThreshold)
    collect();
  all_objects_.push_back(o);
}

void VM::mark_object(BaseObject* o) {
  if (o == nullptr || o->is_marked())
    return;

#if defined(_TADPOLE_DEBUG_GC)
  std::cout << "[" << o << "] mark object: `" << o->stringify() << "`" << std::endl;
#endif

  o->set_marked(true);
  worklist_.push_back(o);
}

void VM::mark_value(const Value& v) {
  if (v.is_object())
    mark_object(v.as_object());
}

InterpretRet VM::interpret(const str_t& source_bytes) {
  FunctionObject* fn = gcompiler_->compile(*this, source_bytes);
  if (fn == nullptr)
    return InterpretRet::ECOMPILE;

  push(fn);
  ClosureObject* closure = ClosureObject::create(*this, fn);
  pop();
  call(closure, 0);

  return run();
}

void VM::collect() {
  // mark root objects
  gcompiler_->mark_compiler();
  for (auto& v : stack_)
    mark_value(v);
  for (auto& f : frames_)
    mark_object(f.closure());
  for (auto& g : globals_)
    mark_value(g.second);
  for (auto* u = open_upvalues_; u != nullptr; u = u->next())
    mark_object(u);

  // mark reference objects
  while (!worklist_.empty()) {
    auto* o = worklist_.back();
    worklist_.pop_back();
    o->gc_blacken(*this);
  }

  // delete unmarked interned string objects
  for (auto it = interned_strings_.begin(); it != interned_strings_.end();) {
    if (!it->second->is_marked())
      interned_strings_.erase(it++);
    else
      ++it;
  }

  // sweep and reclaim unmarked objects
  for (auto it = all_objects_.begin(); it != all_objects_.end();) {
    if (!(*it)->is_marked()) {
      reclaim_object(*it);
      all_objects_.erase(it++);
    }
    else {
      (*it)->set_marked(false);
      ++it;
    }
  }

  gc_threshold_ = std::max(kGCThreshold, all_objects_.size() * kGCFactor);
}

void VM::reclaim_object(BaseObject* o) {
#if defined(_TADPOLE_DEBUG_GC)
  std::cout << "[" << o << "] reclaim object: `" << o->stringify() << "`" << std::endl;
#endif

  delete o;
}

void VM::reset() {
  stack_.clear();
  frames_.clear();
  open_upvalues_ = nullptr;
}

void VM::runtime_error(const char* format, ...) {
  std::cerr << "Traceback (most recent call last):" << std::endl;
  for (auto it = frames_.rbegin(); it != frames_.rend(); ++it) {
    auto& frame = *it;

    sz_t i = frame.frame_chunk()->offset_with(frame.ip()) - 1;
    std::cerr
      << "  [LINE: " << frame.frame_chunk()->get_line(i) << "] in "
      << "`" << frame.frame_fn()->name_asstr() << "()`"
      << std::endl;
  }

  va_list ap;
  va_start(ap, format);
  std::vfprintf(stderr, format, ap);
  va_end(ap);
  std::fprintf(stderr, "\n");

  reset();
}

void VM::push(Value value) noexcept {
  stack_.push_back(value);
}

Value VM::pop() noexcept {
  Value value = stack_.back();
  stack_.pop_back();
  return value;
}

const Value& VM::peek(sz_t distance) const noexcept {
  return stack_[stack_.size() - 1 - distance];
}

bool VM::call(ClosureObject* closure, sz_t argc) {
  FunctionObject* fn = closure->fn();
  if (fn->arity() != argc) {
    runtime_error("%s() takes exactly %ud arguments (%ud given)",
        fn->name_asstr(), fn->arity(), argc);
    return false;
  }

  frames_.push_back(CallFrame(closure, fn->chunk()->codes(), stack_.size() - argc - 1));
  return true;
}

bool VM::call(const Value& callee, sz_t argc) {
  if (callee.is_object()) {
    switch (callee.objtype()) {
    case ObjType::NATIVE:
      {
        Value* args = nullptr;
        if (argc > 0 && stack_.size() > argc)
          args = &stack_[stack_.size() - argc];

        Value result = callee.as_native()->fn()(argc, args);
        stack_.resize(stack_.size() - argc - 1);
        push(result);

        return true;
      }
    case ObjType::CLOSURE: return call(callee.as_closure(), argc);
    }
  }

  runtime_error("can only call function");
  return false;
}

UpvalueObject* VM::capture_upvalue(Value* local) {
  if (open_upvalues_ == nullptr) {
    open_upvalues_ = UpvalueObject::create(*this, local);
    return open_upvalues_;
  }

  UpvalueObject* upvalue = open_upvalues_;
  UpvalueObject* pre_upvalue = nullptr;
  while (upvalue != nullptr && upvalue->value() > local) {
    pre_upvalue = upvalue;
    upvalue = upvalue->next();
  }
  if (upvalue != nullptr && upvalue->value() == local)
    return upvalue;

  UpvalueObject* new_upvalue = UpvalueObject::create(*this, local, upvalue);
  if (pre_upvalue == nullptr)
    open_upvalues_ = new_upvalue;
  else
    pre_upvalue->set_next(new_upvalue);
  return new_upvalue;
}

void VM::close_upvalues(Value* last) {
  while (open_upvalues_ != nullptr && open_upvalues_->value() >= last) {
    UpvalueObject* upvalue = open_upvalues_;
    upvalue->set_closed(upvalue->value_asref());
    upvalue->set_value(upvalue->closed_asptr());

    open_upvalues_ = upvalue->next();
  }
}

InterpretRet VM::run() {
  CallFrame* frame = &frames_.back();

#define _RDBYTE()   frame->inc_ip()
#define _RDCONST()  frame->frame_chunk()->get_constant(_RDBYTE())
#define _RDSTR()    _RDCONST().as_string()
#define _RDCSTR()   _RDCONST().as_cstring()
#define _BINOP(op)  do {\
    if (!peek(0).is_numeric() || !peek(1).is_numeric()) {\
      runtime_error("operands must be two numerics");\
      return InterpretRet::ERUNTIME;\
    }\
    double b = pop().as_numeric();\
    double a = pop().as_numeric();\
    push(a op b);\
  } while (false)

  for (;;) {
#if defined(_TADPOLE_DEBUG_VM)
    auto* frame_chunk = frame->frame_chunk();
    std::cout << "          ";
    for (auto& v : stack_)
      std::cout << "[" << v << "]";
    std::cout << std::endl;
    frame_chunk->dis_code(frame_chunk->offset_with(frame->ip()));
#endif

    switch (Code c = as_type<Code>(_RDBYTE())) {
    case Code::CONSTANT: push(_RDCONST()); break;
    case Code::NIL: push(nullptr); break;
    case Code::FALSE: push(false); break;
    case Code::TRUE: push(true); break;
    case Code::POP: pop(); break;
    case Code::DEF_GLOBAL:
      {
        const char* name = _RDCSTR();
        if (auto it = globals_.find(name); it != globals_.end()) {
          runtime_error("name `%s` is redefined", name);
          return InterpretRet::ERUNTIME;
        }
        else {
          globals_[name] = pop();
        }
      } break;
    case Code::GET_GLOBAL:
      {
        const char* name = _RDCSTR();
        if (auto it = globals_.find(name); it != globals_.end()) {
          push(it->second);
        }
        else {
          runtime_error("name `%s` is not defined", name);
          return InterpretRet::ERUNTIME;
        }
      } break;
    case Code::SET_GLOBAL:
      {
        const char* name = _RDCSTR();
        if (auto it = globals_.find(name); it != globals_.end()) {
          it->second = peek();
        }
        else {
          runtime_error("name `%s` is not defined", name);
          return InterpretRet::ERUNTIME;
        }
      } break;
    case Code::GET_LOCAL: push(stack_[frame->stack_begpos() + _RDBYTE()]); break;
    case Code::SET_LOCAL: stack_[frame->stack_begpos() + _RDBYTE()] = peek(); break;
    case Code::GET_UPVALUE:
      {
        u8_t slot = _RDBYTE();
        push(frame->closure()->get_upvalue(slot)->value_asref());
      } break;
    case Code::SET_UPVALUE:
      {
        u8_t slot = _RDBYTE();
        frame->closure()->get_upvalue(slot)->set_value(peek());
      } break;
    case Code::ADD:
      {
        if (peek(0).is_string() && peek(1).is_string()) {
          StringObject* b = peek(0).as_string();
          StringObject* a = peek(1).as_string();
          StringObject* s = StringObject::concat(*this, a, b);
          pop();
          pop();
          push(s);
        }
        else if (peek(0).is_numeric() && peek(1).is_numeric()) {
          double b = pop().as_numeric();
          double a = pop().as_numeric();
          push(a + b);
        }
        else {
          runtime_error("operands must be two strings or two numerics");
          return InterpretRet::ERUNTIME;
        }
      } break;
    case Code::SUB: _BINOP(-); break;
    case Code::MUL: _BINOP(*); break;
    case Code::DIV: _BINOP(/); break;
    case Code::CALL_0:
    case Code::CALL_1:
    case Code::CALL_2:
    case Code::CALL_3:
    case Code::CALL_4:
    case Code::CALL_5:
    case Code::CALL_6:
    case Code::CALL_7:
    case Code::CALL_8:
      {
        sz_t argc = c - Code::CALL_0;
        if (!call(peek(argc), argc))
          return InterpretRet::ERUNTIME;
        frame = &frames_.back();
      } break;
    case Code::CLOSURE:
      {
      } break;
    }
  }

#undef _BINOP
#undef _RDCSTR
#undef _RDSTR
#undef _RDCONST
#undef _RDBYTE

  return InterpretRet::OK;
}

}
<|endoftext|>


<commit_before>#include "Deinterleave.h"
#include "IRMutator.h"
#include "Simplify.h"
#include "IROperator.h"
#include "IREquality.h"
#include "IRPrinter.h"
#include "ModulusRemainder.h"
#include "Log.h"
#include "Scope.h"

namespace Halide {
namespace Internal {

using std::pair;
using std::make_pair;

class Deinterleaver : public IRMutator {
public:
    bool even_lanes;
    int new_width;
    bool failed;
private:
    Scope<int> internal;

    using IRMutator::visit;

    void visit(const Broadcast *op) {
        expr = new Broadcast(op->value, new_width);
    }

    void visit(const Load *op) {
        Type t = op->type;
        t.width = new_width;
        expr = new Load(t, op->name, mutate(op->index), op->image, op->param);
    }

    void visit(const Ramp *op) {
        if (even_lanes) {
            expr = new Ramp(op->base, op->stride * 2, new_width);
        } else {
            expr = new Ramp(op->base + op->stride, op->stride * 2, new_width);
        }
    }

    void visit(const Variable *op) {
        Type t = op->type;
        t.width = new_width;
        if (internal.contains(op->name)) {
            expr = new Variable(t, op->name, op->param, op->reduction_domain);
        } else {
            // Uh-oh, we don't know how to deinterleave this vector expression
            // Make llvm do it
            expr = new Call(t, even_lanes ? "extract even lanes" : "extract odd lanes", vec<Expr>(op));
        }
    }

    void visit(const Let *op) {
        Expr value = mutate(op->value);
        internal.push(op->name, 0);
        Expr body = mutate(op->body);
        internal.pop(op->name);
        expr = new Let(op->name, value, body);
    }
};

Expr extract_odd_lanes(Expr e) {
    Deinterleaver d;
    d.even_lanes = false;
    d.new_width = e.type().width/2;
    d.failed = false;
    e = d.mutate(e);
    if (d.failed) return Expr();
    else return simplify(e);
}

Expr extract_even_lanes(Expr e) {
    Deinterleaver d;
    d.even_lanes = true;
    d.new_width = (e.type().width+1)/2;
    d.failed = false;
    e = d.mutate(e);
    if (d.failed) return Expr();
    else return simplify(e);
}

class Interleaver : public IRMutator {
    Scope<ModulusRemainder> alignment_info;

    using IRMutator::visit;

    void visit(const Let *op) {
        Expr value = mutate(op->value);
        if (value.type() == Int(32)) alignment_info.push(op->name, modulus_remainder(value));
        Expr body = mutate(op->body);
        if (value.type() == Int(32)) alignment_info.pop(op->name);
        if (value.same_as(op->value) && body.same_as(op->body)) {
            expr = op;
        } else {
            expr = new Let(op->name, value, body);
        }
    }

    void visit(const LetStmt *op) {
        Expr value = mutate(op->value);
        if (value.type() == Int(32)) alignment_info.push(op->name, modulus_remainder(value));
        Stmt body = mutate(op->body);
        if (value.type() == Int(32)) alignment_info.pop(op->name);
        if (value.same_as(op->value) && body.same_as(op->body)) {
            stmt = op;
        } else {
            stmt = new LetStmt(op->name, value, body);
        }
    }

    void visit(const Select *op) {
        Expr condition = mutate(op->condition);
        Expr true_value = mutate(op->true_value);
        Expr false_value = mutate(op->false_value);
        const EQ *eq = condition.as<EQ>();
        const Mod *mod = eq ? eq->a.as<Mod>() : NULL;
        const Ramp *ramp = mod ? mod->a.as<Ramp>() : NULL;
        if (ramp && ramp->width > 2 && is_one(ramp->stride) && is_const(eq->b) && is_two(mod->b)) {
            log(3) << "Detected interleave vector pattern. Deinterleaving.\n";
            ModulusRemainder mod_rem = modulus_remainder(ramp->base, alignment_info);
            log(3) << "Base is congruent to " << mod_rem.remainder << " modulo " << mod_rem.modulus << "\n";
            Expr a, b;
            bool base_is_even = ((mod_rem.modulus & 1) == 0) && ((mod_rem.remainder & 1) == 0);
            bool base_is_odd  = ((mod_rem.modulus & 1) == 0) && ((mod_rem.remainder & 1) == 1);
            if ((is_zero(eq->b) && base_is_even) ||
                (is_one(eq->b) && base_is_odd)) {
                a = extract_even_lanes(true_value);
                b = extract_odd_lanes(false_value);
            } else if ((is_one(eq->b) && base_is_even) ||
                       (is_zero(eq->b) && base_is_odd)) {
                a = extract_even_lanes(false_value);
                b = extract_odd_lanes(true_value);
            }

            if (a.defined() && b.defined()) {
                expr = new Call(op->type, "interleave vectors", vec(a, b));
                return;
            }
        }

        if (condition.same_as(op->condition) &&
            true_value.same_as(op->true_value) &&
            false_value.same_as(op->false_value)) {
            expr = op;
        } else {
            expr = new Select(condition, true_value, false_value);
        }
    }
};

Stmt rewrite_interleavings(Stmt s) {
    return Interleaver().mutate(s);
}

namespace {
void check(Expr a, Expr even, Expr odd) {
    a = simplify(a);
    Expr correct_even = extract_even_lanes(a);
    Expr correct_odd = extract_odd_lanes(a);
    if (!equal(correct_even, even)) {
        assert(false);
    }
    if (!equal(correct_odd, odd)) {
        assert(false);
    }
}
}

void deinterleave_vector_test() {
    std::pair<Expr, Expr> result;
    Expr x = new Variable(Int(32), "x");
    Expr ramp = new Ramp(x + 4, 3, 7);
    Expr ramp_a = new Ramp(x + 4, 6, 4);
    Expr ramp_b = new Ramp(x + 7, 6, 3);
    Expr broadcast = new Broadcast(x + 4, 16);
    Expr broadcast_a = new Broadcast(x + 4, 8);
    Expr broadcast_b = broadcast_a;

    check(ramp, ramp_a, ramp_b);
    check(broadcast, broadcast_a, broadcast_b);

    check(new Load(ramp.type(), "buf", ramp, Buffer(), Parameter()),
          new Load(ramp_a.type(), "buf", ramp_a, Buffer(), Parameter()),
          new Load(ramp_b.type(), "buf", ramp_b, Buffer(), Parameter()));

    std::cout << "deinterleave_vector test passed" << std::endl;
}

}
}
<commit_msg>Deinterleave wasn't tracking alignment info correctly.<commit_after>#include "Deinterleave.h"
#include "IRMutator.h"
#include "Simplify.h"
#include "IROperator.h"
#include "IREquality.h"
#include "IRPrinter.h"
#include "ModulusRemainder.h"
#include "Log.h"
#include "Scope.h"

namespace Halide {
namespace Internal {

using std::pair;
using std::make_pair;

class Deinterleaver : public IRMutator {
public:
    bool even_lanes;
    int new_width;
    bool failed;
private:
    Scope<int> internal;

    using IRMutator::visit;

    void visit(const Broadcast *op) {
        expr = new Broadcast(op->value, new_width);
    }

    void visit(const Load *op) {
        Type t = op->type;
        t.width = new_width;
        expr = new Load(t, op->name, mutate(op->index), op->image, op->param);
    }

    void visit(const Ramp *op) {
        if (even_lanes) {
            expr = new Ramp(op->base, op->stride * 2, new_width);
        } else {
            expr = new Ramp(op->base + op->stride, op->stride * 2, new_width);
        }
    }

    void visit(const Variable *op) {
        Type t = op->type;
        t.width = new_width;
        if (internal.contains(op->name)) {
            expr = new Variable(t, op->name, op->param, op->reduction_domain);
        } else {
            // Uh-oh, we don't know how to deinterleave this vector expression
            // Make llvm do it
            expr = new Call(t, even_lanes ? "extract even lanes" : "extract odd lanes", vec<Expr>(op));
        }
    }

    void visit(const Let *op) {
        Expr value = mutate(op->value);
        internal.push(op->name, 0);
        Expr body = mutate(op->body);
        internal.pop(op->name);
        expr = new Let(op->name, value, body);
    }
};

Expr extract_odd_lanes(Expr e) {
    Deinterleaver d;
    d.even_lanes = false;
    d.new_width = e.type().width/2;
    d.failed = false;
    e = d.mutate(e);
    if (d.failed) return Expr();
    else return simplify(e);
}

Expr extract_even_lanes(Expr e) {
    Deinterleaver d;
    d.even_lanes = true;
    d.new_width = (e.type().width+1)/2;
    d.failed = false;
    e = d.mutate(e);
    if (d.failed) return Expr();
    else return simplify(e);
}

class Interleaver : public IRMutator {
    Scope<ModulusRemainder> alignment_info;

    using IRMutator::visit;

    void visit(const Let *op) {
        Expr value = mutate(op->value);
        if (value.type() == Int(32)) alignment_info.push(op->name, modulus_remainder(value));
        Expr body = mutate(op->body);
        if (value.type() == Int(32)) alignment_info.pop(op->name);
        if (value.same_as(op->value) && body.same_as(op->body)) {
            expr = op;
        } else {
            expr = new Let(op->name, value, body);
        }
    }

    void visit(const LetStmt *op) {
        Expr value = mutate(op->value);
        if (value.type() == Int(32)) alignment_info.push(op->name,
                                                         modulus_remainder(value, alignment_info));
        Stmt body = mutate(op->body);
        if (value.type() == Int(32)) alignment_info.pop(op->name);
        if (value.same_as(op->value) && body.same_as(op->body)) {
            stmt = op;
        } else {
            stmt = new LetStmt(op->name, value, body);
        }
    }

    void visit(const Select *op) {
        Expr condition = mutate(op->condition);
        Expr true_value = mutate(op->true_value);
        Expr false_value = mutate(op->false_value);
        const EQ *eq = condition.as<EQ>();
        const Mod *mod = eq ? eq->a.as<Mod>() : NULL;
        const Ramp *ramp = mod ? mod->a.as<Ramp>() : NULL;
        if (ramp && ramp->width > 2 && is_one(ramp->stride) && is_const(eq->b) && is_two(mod->b)) {
            log(3) << "Detected interleave vector pattern. Deinterleaving.\n";
            ModulusRemainder mod_rem = modulus_remainder(ramp->base, alignment_info);
            log(3) << "Base is congruent to " << mod_rem.remainder << " modulo " << mod_rem.modulus << "\n";
            Expr a, b;
            bool base_is_even = ((mod_rem.modulus & 1) == 0) && ((mod_rem.remainder & 1) == 0);
            bool base_is_odd  = ((mod_rem.modulus & 1) == 0) && ((mod_rem.remainder & 1) == 1);
            if ((is_zero(eq->b) && base_is_even) ||
                (is_one(eq->b) && base_is_odd)) {
                a = extract_even_lanes(true_value);
                b = extract_odd_lanes(false_value);
            } else if ((is_one(eq->b) && base_is_even) ||
                       (is_zero(eq->b) && base_is_odd)) {
                a = extract_even_lanes(false_value);
                b = extract_odd_lanes(true_value);
            }

            if (a.defined() && b.defined()) {
                expr = new Call(op->type, "interleave vectors", vec(a, b));
                return;
            }
        }

        if (condition.same_as(op->condition) &&
            true_value.same_as(op->true_value) &&
            false_value.same_as(op->false_value)) {
            expr = op;
        } else {
            expr = new Select(condition, true_value, false_value);
        }
    }
};

Stmt rewrite_interleavings(Stmt s) {
    return Interleaver().mutate(s);
}

namespace {
void check(Expr a, Expr even, Expr odd) {
    a = simplify(a);
    Expr correct_even = extract_even_lanes(a);
    Expr correct_odd = extract_odd_lanes(a);
    if (!equal(correct_even, even)) {
        assert(false);
    }
    if (!equal(correct_odd, odd)) {
        assert(false);
    }
}
}

void deinterleave_vector_test() {
    std::pair<Expr, Expr> result;
    Expr x = new Variable(Int(32), "x");
    Expr ramp = new Ramp(x + 4, 3, 7);
    Expr ramp_a = new Ramp(x + 4, 6, 4);
    Expr ramp_b = new Ramp(x + 7, 6, 3);
    Expr broadcast = new Broadcast(x + 4, 16);
    Expr broadcast_a = new Broadcast(x + 4, 8);
    Expr broadcast_b = broadcast_a;

    check(ramp, ramp_a, ramp_b);
    check(broadcast, broadcast_a, broadcast_b);

    check(new Load(ramp.type(), "buf", ramp, Buffer(), Parameter()),
          new Load(ramp_a.type(), "buf", ramp_a, Buffer(), Parameter()),
          new Load(ramp_b.type(), "buf", ramp_b, Buffer(), Parameter()));

    std::cout << "deinterleave_vector test passed" << std::endl;
}

}
}
<|endoftext|>


<commit_before>#ifndef ALEPH_TOPOLOGY_MESH_HH__
#define ALEPH_TOPOLOGY_MESH_HH__

#include <cassert>

#include <algorithm>
#include <iterator>
#include <limits>
#include <memory>
#include <stdexcept>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace aleph
{

namespace topology
{

/**
  @class Mesh
  @brief Half-edge mesh data structure

  This data structure is capable of representing two-dimensional piecewise
  linear manifolds. In order to speed up standard queries, this class uses
  a standard half-edge data structure.
*/

template <class Position = float, class Data = float> class Mesh
{
public:
  struct Face;
  struct HalfEdge;
  struct Vertex;

  using Index           = std::size_t;

  using FacePointer     = std::shared_ptr<Face>;
  using HalfEdgePointer = std::shared_ptr<HalfEdge>;
  using VertexPointer   = std::shared_ptr<Vertex>;

  struct HalfEdge
  {
    FacePointer   face;
    VertexPointer vertex;

    HalfEdgePointer next; // Next half-edge (counter-clockwise)
    HalfEdgePointer prev; // Previous half-edge
    HalfEdgePointer pair; // Opposite half-edge

    VertexPointer source() const noexcept
    {
      return pair->vertex;
    }

    VertexPointer target() const noexcept
    {
      return vertex;
    }
  };

  struct Face
  {
    HalfEdgePointer edge;

    /**
      Collects all vertices of the given face. Vertex IDs will be
      returned in the order in which they are traversed along the
      face.
    */

    std::vector<Index> vertices() const
    {
      std::vector<Index> v;

      auto e = edge;

      do
      {
        v.push_back( e->source()->id );
        e = e->next;
      }
      while( e != edge );

      return v;
    }
  };

  struct Vertex
  {
    Index    id   = Index();
    Position x    = Position();
    Position y    = Position();
    Position z    = Position();
    Data     data = Data();

    HalfEdgePointer edge;
  };

  // Mesh attributes ---------------------------------------------------

  std::vector<Index> vertices() const noexcept
  {
    std::vector<Index> result;
    result.reserve( _vertices.size() );

    for( auto&& pair : _vertices )
      result.push_back( pair.first );

    return result;
  }

  std::size_t numVertices() const noexcept
  {
    return _vertices.size();
  }

  // TODO: Better return type?
  std::vector< std::vector<Index> > faces() const noexcept
  {
    std::unordered_set<FacePointer> faces;

    for( auto&& pair : _vertices )
    {
      auto&& vertex = pair.second;
      auto&& edge   = vertex->edge;
      auto&& face   = edge->face;

      if( face )
        faces.insert( face );
    }

    std::vector< std::vector<Index> > results;
    results.reserve( faces.size() );

    for( auto&& face : faces )
      results.push_back( face->vertices() );

    return results;
  }

  std::size_t numFaces() const noexcept
  {
    std::unordered_set<FacePointer> faces;

    for( auto&& pair : _vertices )
    {
      auto&& vertex = pair.second;
      auto&& edge   = vertex->edge;
      auto&& face   = edge->face;

      if( face )
        faces.insert( face );
    }

    return faces.size();
  }

  // Mesh modification -------------------------------------------------

  /** Adds a new vertex to the mesh */
  void addVertex( Position x, Position y, Position z, Data data = Data(), Index id = std::numeric_limits<Index>::max() )
  {
    Vertex v;

    v.id   = id == std::numeric_limits<Index>::max() ? std::max( _vertices.size(), _largestVertexID ) : id;
    v.x    = x;
    v.y    = y;
    v.z    = z;
    v.data = data;

    auto pair = _vertices.insert( std::make_pair( v.id,
                                  std::make_shared<Vertex>( v ) ) );

    if( !pair.second )
      throw std::runtime_error( "Vertex ID must be unique" );

    _largestVertexID = std::max( _largestVertexID, v.id );
  }

  /**
    Adds a new face to the mesh. This function expects a range of vertex IDs
    that make up the face. The vertices of the face need to sorted correctly
    in order for the orientation to be consistent.
  */

  template <class InputIterator> void addFace( InputIterator begin, InputIterator end )
  {
    FacePointer face = std::make_shared<Face>();

    // Stores all half-edges created (or found) by this function in the
    // order in which they belong to the face.
    std::vector<HalfEdgePointer> edges;
    edges.reserve( static_cast<std::size_t>( std::distance( begin, end ) ) );

    for( InputIterator it = begin; it != end; ++it )
    {
      auto curr = it;
      auto next = std::next( it );

      if( next == end )
        next = begin;

      auto source = _vertices.at( *curr );   // Edge source vertex
      auto target = _vertices.at( *next );   // Edge target vertex
      auto edge   = getEdge( *curr, *next ); // Edge

      // Case 1: A new edge. Create a new edge and a new pair. Set edges
      // of source and target vertex correctly. Moreover, initialize the
      // paired edge with sensible default values.
      if( !edge )
      {
        edge      = std::make_shared<HalfEdge>();
        auto pair = std::make_shared<HalfEdge>();

        pair->vertex = source; // This is flipped by design: We point to the
                               // target vertex of the flipped edge. This is
                               // just the source vertex again.

        pair->pair   = edge;
        edge->pair   = pair;

        if( !source->edge )
          source->edge = edge;

        if( !target->edge )
          target->edge = pair;
      }

      // Ensures that the first edge that is specified for the new face
      // will be set as the outgoing edge of the face. This is not just
      // a 'cosmetic' choice but also ensures that vertex IDs for every
      // face are reported in the original order.
      if( !face->edge )
        face->edge = edge;

      assert( !edge->face );
      assert(  edge->pair );

      edge->face   = face;
      edge->vertex = target;

      edges.push_back( edge );
    }

    // Set 'next' and 'prev' pointers correctly ------------------------
    //
    // We first traverse all edges that bound the current face. Here, it
    // should be possible to traverse the face directly, so we require a
    // proper pointer in both directions.

    for( auto itEdge = edges.begin(); itEdge != edges.end(); ++itEdge )
    {
      auto curr = itEdge;
      auto prev = std::prev( curr );
      auto next = std::next( curr );

      if( curr == edges.begin() )
        prev = std::prev( edges.end() );

      if( next == edges.end() )
        next = edges.begin();

      auto&& edge = *itEdge;

      edge->next = *next;
      edge->prev = *prev;
    }

    // Extend boundary -------------------------------------------------
    //
    // Traverse all vertices whose paired edges have an empty face. Any
    // of these edges is part of the boundary face.

    for( auto&& pair : _vertices )
    {
      auto&& vertex = pair.second;

      if( !vertex->edge || vertex->edge->pair->face )
        continue;

      auto curr = vertex->edge->target();
      auto edge = vertex->edge->pair;

      do
      {
        assert( !edge->face );
        auto edges = this->getEdges( *curr );

        for( auto&& e : edges )
        {
          if( !e->pair->face )
          {
            e->pair->next = edge;

            edge          = e->pair;
            curr          = e->pair->pair->target();

            break;
          }
        }
      }
      while( curr != vertex );

      // Close the loop around the boundary face by adding a pointer to
      // the identified edge.
      vertex->edge->pair->next = edge;
    }
  }

  // Mesh queries ------------------------------------------------------

  /**
    The star of a vertex is defined as the mesh that contains all the
    triangles and edges of which the vertex is a face.
  */

  Mesh star( Index id ) const noexcept
  {
    Mesh M;

    auto vertex = this->getVertex( id );
    auto faces  = this->getFaces( *vertex );

    {
      std::unordered_set<Index> vertices;

      for( auto&& f : faces )
      {
        auto&& v = f->vertices();
        vertices.insert( v.begin(), v.end() );
      }

      for( auto&& v : vertices )
      {
        auto oldVertex = this->getVertex( v );

        M.addVertex( oldVertex->x, oldVertex->y, oldVertex->z,
                     oldVertex->data,
                     oldVertex->id );
      }
    }

    for( auto&& f : faces )
    {
      auto&& vertices = f->vertices();
      M.addFace( vertices.begin(), vertices.end() );
    }

    return M;
  }

  /**
    The link of a vertex is defined as all simplices in the closed star
    that are disjoint from the vertex. For 2-manifolds, this will yield
    a cycle of edges and vertices.

    This function will represent the cycle by returning all vertex IDs,
    in an order that is consistent with the orientation of the mesh.
  */

  std::vector<Index> link( const Vertex& v ) const noexcept
  {
    auto neighbours = this->getNeighbours( v );

    std::vector<Index> result;
    result.reserve( neighbours.size() );

    for( auto&& neighbour : neighbours )
      result.push_back( neighbour->id );

    return result;
  }

  std::vector<Index> getLowerNeighbours( Index id ) const noexcept
  {
    auto&& vertex     = this->getVertex( id );
    auto&& neighbours = this->getNeighbours( *vertex );
    auto&& data       = vertex->data;

    neighbours.erase( std::remove_if( neighbours.begin(), neighbours.end(),
                                      [&data] ( const VertexPointer& neighbour )
                                      {
                                        return neighbour->data >= data;
                                      } ),
                      neighbours.end() );

    std::vector<Index> result;
    result.reserve( neighbours.size() );

    std::transform( neighbours.begin(), neighbours.end(), std::back_inserter( result ),
                    [] ( const VertexPointer& neighbour )
                    {
                      return neighbour->id;
                    } );

    return result;
  }

  std::vector<Index> getHigherNeighbours( Index id ) const noexcept
  {
    auto&& vertex     = this->getVertex( id );
    auto&& neighbours = this->getNeighbours( *vertex );
    auto&& data       = vertex->data;

    neighbours.erase( std::remove_if( neighbours.begin(), neighbours.end(),
                                      [&data] ( const VertexPointer& neighbour )
                                      {
                                        return neighbour->data <= data;
                                      } ),
                      neighbours.end() );

    std::vector<Index> result;
    result.reserve( neighbours.size() );

    std::transform( neighbours.begin(), neighbours.end(), std::back_inserter( result ),
                    [] ( const VertexPointer& neighbour )
                    {
                      return neighbour->id;
                    } );

    return result;
  }

  /**
    Checks whether an edge between two vertices that are identified by
    their index exists.
  */

  bool hasEdge( Index u, Index v ) const
  {
    auto&& source = this->getVertex(u);
    auto&& target = this->getVertex(v);

    auto neighbours = this->getNeighbours( *source );

    return std::find( neighbours.begin(), neighbours.end(), target ) != neighbours.end();
  }

private:

  /** Gets all vertices that are adjacent to a given vertex */
  std::vector<VertexPointer> getNeighbours( const Vertex& v ) const noexcept
  {
    std::vector<VertexPointer> neighbours;

    auto edge = v.edge;
    do
    {
      if( edge )
        neighbours.push_back( edge->target() );
      else
        break;

      edge = edge->pair->next;
    }
    while( edge != v.edge );

    return neighbours;
  }

  /** Gets all edges that are incident on a given vertex. */
  std::vector<HalfEdgePointer> getEdges( const Vertex& v ) const noexcept
  {
    std::vector<HalfEdgePointer> edges;

    auto edge = v.edge;
    do
    {
      if( edge )
        edges.push_back( edge );
      else
        break;

      edge = edge->pair->next;
    }
    while( edge != v.edge );

    return edges;
  }

  /** Gets all faces that are incident on a given vertex */
  std::vector<FacePointer> getFaces( const Vertex& v ) const noexcept
  {
    std::vector<FacePointer> faces;

    auto edge = v.edge;
    do
    {
      if( edge && edge->face )
        faces.push_back( edge->face );
      else
        break;

      edge = edge->pair->next;
    }
    while( edge != v.edge );

    return faces;
  }

  /**
    Check whether a given (directed) edge already exists. If so,
    a pointer to the edge is being returned.
  */

  HalfEdgePointer getEdge( Index u, Index v ) const noexcept
  {
    auto source = _vertices.at(u);           // Edge source vertex
    auto target = _vertices.at(v);           // Edge target vertex
    auto edges  = this->getEdges( *source ); // Incident edges

    auto itEdge = std::find_if( edges.begin(), edges.end(),
                                [&source, &target] ( const HalfEdgePointer& edge )
                                {
                                  return edge->source() == source && edge->target() == target;
                                } );

    if( itEdge != edges.end() )
      return *itEdge;
    else
      return nullptr;
  }

  /**
    Returns vertex with the given ID. This is an internal function
    that is used to obtain all information about a vertex.
  */

  VertexPointer getVertex( Index id ) const
  {
    return _vertices.at( id );
  }

  /**
   Stores largest vertex ID. This is required in order to ensure
   that vertex IDs are not assigned multiple times when the user
   adds vertices one after the other.
  */

  Index _largestVertexID = Index();

  /**
    Stores all vertex pointers. This is sufficient to store the
    complete mesh.
  */

  std::unordered_map<Index, VertexPointer> _vertices;
};

} // namespace topology

} // namespace aleph

#endif
<commit_msg>Implemented calculation of connected components for meshes<commit_after>#ifndef ALEPH_TOPOLOGY_MESH_HH__
#define ALEPH_TOPOLOGY_MESH_HH__

#include <cassert>

#include <algorithm>
#include <iterator>
#include <limits>
#include <memory>
#include <stdexcept>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "topology/UnionFind.hh"

namespace aleph
{

namespace topology
{

/**
  @class Mesh
  @brief Half-edge mesh data structure

  This data structure is capable of representing two-dimensional piecewise
  linear manifolds. In order to speed up standard queries, this class uses
  a standard half-edge data structure.
*/

template <class Position = float, class Data = float> class Mesh
{
public:
  struct Face;
  struct HalfEdge;
  struct Vertex;

  using Index           = std::size_t;

  using FacePointer     = std::shared_ptr<Face>;
  using HalfEdgePointer = std::shared_ptr<HalfEdge>;
  using VertexPointer   = std::shared_ptr<Vertex>;

  struct HalfEdge
  {
    FacePointer   face;
    VertexPointer vertex;

    HalfEdgePointer next; // Next half-edge (counter-clockwise)
    HalfEdgePointer prev; // Previous half-edge
    HalfEdgePointer pair; // Opposite half-edge

    VertexPointer source() const noexcept
    {
      return pair->vertex;
    }

    VertexPointer target() const noexcept
    {
      return vertex;
    }
  };

  struct Face
  {
    HalfEdgePointer edge;

    /**
      Collects all vertices of the given face. Vertex IDs will be
      returned in the order in which they are traversed along the
      face.
    */

    std::vector<Index> vertices() const
    {
      std::vector<Index> v;

      auto e = edge;

      do
      {
        v.push_back( e->source()->id );
        e = e->next;
      }
      while( e != edge );

      return v;
    }
  };

  struct Vertex
  {
    Index    id   = Index();
    Position x    = Position();
    Position y    = Position();
    Position z    = Position();
    Data     data = Data();

    HalfEdgePointer edge;
  };

  // Mesh attributes ---------------------------------------------------

  std::vector<Index> vertices() const noexcept
  {
    std::vector<Index> result;
    result.reserve( _vertices.size() );

    for( auto&& pair : _vertices )
      result.push_back( pair.first );

    return result;
  }

  std::size_t numVertices() const noexcept
  {
    return _vertices.size();
  }

  // TODO: Better return type?
  std::vector< std::vector<Index> > faces() const noexcept
  {
    std::unordered_set<FacePointer> faces;

    for( auto&& pair : _vertices )
    {
      auto&& vertex = pair.second;
      auto&& edge   = vertex->edge;
      auto&& face   = edge->face;

      if( face )
        faces.insert( face );
    }

    std::vector< std::vector<Index> > results;
    results.reserve( faces.size() );

    for( auto&& face : faces )
      results.push_back( face->vertices() );

    return results;
  }

  std::size_t numFaces() const noexcept
  {
    std::unordered_set<FacePointer> faces;

    for( auto&& pair : _vertices )
    {
      auto&& vertex = pair.second;
      auto&& edge   = vertex->edge;
      auto&& face   = edge->face;

      if( face )
        faces.insert( face );
    }

    return faces.size();
  }

  // Mesh modification -------------------------------------------------

  /** Adds a new vertex to the mesh */
  void addVertex( Position x, Position y, Position z, Data data = Data(), Index id = std::numeric_limits<Index>::max() )
  {
    Vertex v;

    v.id   = id == std::numeric_limits<Index>::max() ? std::max( _vertices.size(), _largestVertexID ) : id;
    v.x    = x;
    v.y    = y;
    v.z    = z;
    v.data = data;

    auto pair = _vertices.insert( std::make_pair( v.id,
                                  std::make_shared<Vertex>( v ) ) );

    if( !pair.second )
      throw std::runtime_error( "Vertex ID must be unique" );

    _largestVertexID = std::max( _largestVertexID, v.id );
  }

  /**
    Adds a new face to the mesh. This function expects a range of vertex IDs
    that make up the face. The vertices of the face need to sorted correctly
    in order for the orientation to be consistent.
  */

  template <class InputIterator> void addFace( InputIterator begin, InputIterator end )
  {
    FacePointer face = std::make_shared<Face>();

    // Stores all half-edges created (or found) by this function in the
    // order in which they belong to the face.
    std::vector<HalfEdgePointer> edges;
    edges.reserve( static_cast<std::size_t>( std::distance( begin, end ) ) );

    for( InputIterator it = begin; it != end; ++it )
    {
      auto curr = it;
      auto next = std::next( it );

      if( next == end )
        next = begin;

      auto source = _vertices.at( *curr );   // Edge source vertex
      auto target = _vertices.at( *next );   // Edge target vertex
      auto edge   = getEdge( *curr, *next ); // Edge

      // Case 1: A new edge. Create a new edge and a new pair. Set edges
      // of source and target vertex correctly. Moreover, initialize the
      // paired edge with sensible default values.
      if( !edge )
      {
        edge      = std::make_shared<HalfEdge>();
        auto pair = std::make_shared<HalfEdge>();

        pair->vertex = source; // This is flipped by design: We point to the
                               // target vertex of the flipped edge. This is
                               // just the source vertex again.

        pair->pair   = edge;
        edge->pair   = pair;

        if( !source->edge )
          source->edge = edge;

        if( !target->edge )
          target->edge = pair;
      }

      // Ensures that the first edge that is specified for the new face
      // will be set as the outgoing edge of the face. This is not just
      // a 'cosmetic' choice but also ensures that vertex IDs for every
      // face are reported in the original order.
      if( !face->edge )
        face->edge = edge;

      assert( !edge->face );
      assert(  edge->pair );

      edge->face   = face;
      edge->vertex = target;

      edges.push_back( edge );
    }

    // Set 'next' and 'prev' pointers correctly ------------------------
    //
    // We first traverse all edges that bound the current face. Here, it
    // should be possible to traverse the face directly, so we require a
    // proper pointer in both directions.

    for( auto itEdge = edges.begin(); itEdge != edges.end(); ++itEdge )
    {
      auto curr = itEdge;
      auto prev = std::prev( curr );
      auto next = std::next( curr );

      if( curr == edges.begin() )
        prev = std::prev( edges.end() );

      if( next == edges.end() )
        next = edges.begin();

      auto&& edge = *itEdge;

      edge->next = *next;
      edge->prev = *prev;
    }

    // Extend boundary -------------------------------------------------
    //
    // Traverse all vertices whose paired edges have an empty face. Any
    // of these edges is part of the boundary face.

    for( auto&& pair : _vertices )
    {
      auto&& vertex = pair.second;

      if( !vertex->edge || vertex->edge->pair->face )
        continue;

      auto curr = vertex->edge->target();
      auto edge = vertex->edge->pair;

      do
      {
        assert( !edge->face );
        auto edges = this->getEdges( *curr );

        for( auto&& e : edges )
        {
          if( !e->pair->face )
          {
            e->pair->next = edge;

            edge          = e->pair;
            curr          = e->pair->pair->target();

            break;
          }
        }
      }
      while( curr != vertex );

      // Close the loop around the boundary face by adding a pointer to
      // the identified edge.
      vertex->edge->pair->next = edge;
    }
  }

  // Mesh queries ------------------------------------------------------

  /**
    The star of a vertex is defined as the mesh that contains all the
    triangles and edges of which the vertex is a face.
  */

  Mesh star( Index id ) const noexcept
  {
    Mesh M;

    auto vertex = this->getVertex( id );
    auto faces  = this->getFaces( *vertex );

    {
      std::unordered_set<Index> vertices;

      for( auto&& f : faces )
      {
        auto&& v = f->vertices();
        vertices.insert( v.begin(), v.end() );
      }

      for( auto&& v : vertices )
      {
        auto oldVertex = this->getVertex( v );

        M.addVertex( oldVertex->x, oldVertex->y, oldVertex->z,
                     oldVertex->data,
                     oldVertex->id );
      }
    }

    for( auto&& f : faces )
    {
      auto&& vertices = f->vertices();
      M.addFace( vertices.begin(), vertices.end() );
    }

    return M;
  }

  /**
    The link of a vertex is defined as all simplices in the closed star
    that are disjoint from the vertex. For 2-manifolds, this will yield
    a cycle of edges and vertices.

    This function will represent the cycle by returning all vertex IDs,
    in an order that is consistent with the orientation of the mesh.
  */

  std::vector<Index> link( const Vertex& v ) const noexcept
  {
    auto neighbours = this->getNeighbours( v );

    std::vector<Index> result;
    result.reserve( neighbours.size() );

    for( auto&& neighbour : neighbours )
      result.push_back( neighbour->id );

    return result;
  }

  std::vector<Index> getLowerNeighbours( Index id ) const noexcept
  {
    auto&& vertex     = this->getVertex( id );
    auto&& neighbours = this->getNeighbours( *vertex );
    auto&& data       = vertex->data;

    neighbours.erase( std::remove_if( neighbours.begin(), neighbours.end(),
                                      [&data] ( const VertexPointer& neighbour )
                                      {
                                        return neighbour->data >= data;
                                      } ),
                      neighbours.end() );

    std::vector<Index> result;
    result.reserve( neighbours.size() );

    std::transform( neighbours.begin(), neighbours.end(), std::back_inserter( result ),
                    [] ( const VertexPointer& neighbour )
                    {
                      return neighbour->id;
                    } );

    return result;
  }

  std::vector<Index> getHigherNeighbours( Index id ) const noexcept
  {
    auto&& vertex     = this->getVertex( id );
    auto&& neighbours = this->getNeighbours( *vertex );
    auto&& data       = vertex->data;

    neighbours.erase( std::remove_if( neighbours.begin(), neighbours.end(),
                                      [&data] ( const VertexPointer& neighbour )
                                      {
                                        return neighbour->data <= data;
                                      } ),
                      neighbours.end() );

    std::vector<Index> result;
    result.reserve( neighbours.size() );

    std::transform( neighbours.begin(), neighbours.end(), std::back_inserter( result ),
                    [] ( const VertexPointer& neighbour )
                    {
                      return neighbour->id;
                    } );

    return result;
  }

  /**
    Checks whether an edge between two vertices that are identified by
    their index exists.
  */

  bool hasEdge( Index u, Index v ) const
  {
    auto&& source = this->getVertex(u);
    auto&& target = this->getVertex(v);

    auto neighbours = this->getNeighbours( *source );

    return std::find( neighbours.begin(), neighbours.end(), target ) != neighbours.end();
  }

  /** Counts the number of connected components */
  std::size_t numConnectedComponents() const noexcept
  {
    auto&& vertices = this->vertices();

    UnionFind<Index> uf( vertices.begin(), vertices.end() );

    for( auto&& vertex : vertices )
    {
      auto&& v      = _vertices.at( vertex );
      auto&& e      = v->edge;
      auto&& source = e->source()->id;
      auto&& target = e->target()->id;

      // Actually, the order does not matter one bit here...
      uf.merge( target, source );
    }

    std::vector<Index> roots;
    uf.roots( std::back_inserter( roots ) );

    return roots.size();
  }

private:

  /** Gets all vertices that are adjacent to a given vertex */
  std::vector<VertexPointer> getNeighbours( const Vertex& v ) const noexcept
  {
    std::vector<VertexPointer> neighbours;

    auto edge = v.edge;
    do
    {
      if( edge )
        neighbours.push_back( edge->target() );
      else
        break;

      edge = edge->pair->next;
    }
    while( edge != v.edge );

    return neighbours;
  }

  /** Gets all edges that are incident on a given vertex. */
  std::vector<HalfEdgePointer> getEdges( const Vertex& v ) const noexcept
  {
    std::vector<HalfEdgePointer> edges;

    auto edge = v.edge;
    do
    {
      if( edge )
        edges.push_back( edge );
      else
        break;

      edge = edge->pair->next;
    }
    while( edge != v.edge );

    return edges;
  }

  /** Gets all faces that are incident on a given vertex */
  std::vector<FacePointer> getFaces( const Vertex& v ) const noexcept
  {
    std::vector<FacePointer> faces;

    auto edge = v.edge;
    do
    {
      if( edge && edge->face )
        faces.push_back( edge->face );
      else
        break;

      edge = edge->pair->next;
    }
    while( edge != v.edge );

    return faces;
  }

  /**
    Check whether a given (directed) edge already exists. If so,
    a pointer to the edge is being returned.
  */

  HalfEdgePointer getEdge( Index u, Index v ) const noexcept
  {
    auto source = _vertices.at(u);           // Edge source vertex
    auto target = _vertices.at(v);           // Edge target vertex
    auto edges  = this->getEdges( *source ); // Incident edges

    auto itEdge = std::find_if( edges.begin(), edges.end(),
                                [&source, &target] ( const HalfEdgePointer& edge )
                                {
                                  return edge->source() == source && edge->target() == target;
                                } );

    if( itEdge != edges.end() )
      return *itEdge;
    else
      return nullptr;
  }

  /**
    Returns vertex with the given ID. This is an internal function
    that is used to obtain all information about a vertex.
  */

  VertexPointer getVertex( Index id ) const
  {
    return _vertices.at( id );
  }

  /**
   Stores largest vertex ID. This is required in order to ensure
   that vertex IDs are not assigned multiple times when the user
   adds vertices one after the other.
  */

  Index _largestVertexID = Index();

  /**
    Stores all vertex pointers. This is sufficient to store the
    complete mesh.
  */

  std::unordered_map<Index, VertexPointer> _vertices;
};

} // namespace topology

} // namespace aleph

#endif
<|endoftext|>


<commit_before>#include <wayfire/singleton-plugin.hpp>
#include <wayfire/view.hpp>
#include <wayfire/output.hpp>
#include <wayfire/workspace-manager.hpp>
#include <getopt.h>

#include "ipc.hpp"

namespace wf
{
static nlohmann::json geometry_to_json(wf::geometry_t g)
{
    nlohmann::json j;
    j["x"]     = g.x;
    j["y"]     = g.y;
    j["width"] = g.width;
    j["height"] = g.height;
    return j;
}

static std::string layer_to_string(uint32_t layer)
{
    switch (layer)
    {
      case LAYER_BACKGROUND:
        return "background";

      case LAYER_BOTTOM:
        return "bottom";

      case LAYER_WORKSPACE:
        return "workspace";

      case LAYER_TOP:
        return "top";

      case LAYER_UNMANAGED:
        return "unmanaged";

      case LAYER_LOCK:
        return "lock";

      case LAYER_DESKTOP_WIDGET:
        return "dew";

      case LAYER_MINIMIZED:
        return "minimized";

      default:
        break;
    }

    return "none";
}

class ipc_plugin_t
{
  public:
    ipc_plugin_t()
    {
        char *pre_socket   = getenv("_WAYFIRE_SOCKET");
        const auto& dname  = wf::get_core().wayland_display;
        std::string socket = pre_socket ?: "/tmp/wayfire-" + dname + ".socket";
        setenv("WAYFIRE_SOCKET", socket.c_str(), 1);

        server = std::make_unique<ipc::server_t>(socket);
        server->register_method("core/list_views", list_views);
    }

    using method_t = ipc::server_t::method_cb;

    method_t list_views = [] (nlohmann::json)
    {
        auto response = nlohmann::json::array();

        for (auto& view : wf::get_core().get_all_views())
        {
            nlohmann::json v;
            v["title"]    = view->get_title();
            v["app-id"]   = view->get_app_id();
            v["geometry"] = geometry_to_json(view->get_wm_geometry());
            v["base-geometry"] = geometry_to_json(view->get_output_geometry());
            v["state"] = {
                {"tiled", view->tiled_edges},
                {"fullscreen", view->fullscreen},
                {"minimized", view->minimized},
            };

            auto layer = view->get_output()->workspace->get_view_layer(view);
            v["layer"] = layer_to_string(layer);

            response.push_back(v);
        }

        return response;
    };

    std::unique_ptr<ipc::server_t> server;
};
}

DECLARE_WAYFIRE_PLUGIN((wf::singleton_plugin_t<wf::ipc_plugin_t, false>));
<commit_msg>stipc: add command to create nested wl outputs<commit_after>#include <wayfire/singleton-plugin.hpp>
#include <wayfire/view.hpp>
#include <wayfire/output.hpp>
#include <wayfire/workspace-manager.hpp>
#include <getopt.h>

#include "ipc.hpp"

extern "C" {
#include <wlr/backend/wayland.h>
#include <wlr/backend/multi.h>
}

static void locate_wayland_backend(wlr_backend *backend, void *data)
{
    if (wlr_backend_is_wl(backend))
    {
        wlr_backend **result = (wlr_backend**)data;
        *result = backend;
    }
}

namespace wf
{
static nlohmann::json geometry_to_json(wf::geometry_t g)
{
    nlohmann::json j;
    j["x"]     = g.x;
    j["y"]     = g.y;
    j["width"] = g.width;
    j["height"] = g.height;
    return j;
}

static std::string layer_to_string(uint32_t layer)
{
    switch (layer)
    {
      case LAYER_BACKGROUND:
        return "background";

      case LAYER_BOTTOM:
        return "bottom";

      case LAYER_WORKSPACE:
        return "workspace";

      case LAYER_TOP:
        return "top";

      case LAYER_UNMANAGED:
        return "unmanaged";

      case LAYER_LOCK:
        return "lock";

      case LAYER_DESKTOP_WIDGET:
        return "dew";

      case LAYER_MINIMIZED:
        return "minimized";

      default:
        break;
    }

    return "none";
}

class ipc_plugin_t
{
  public:
    ipc_plugin_t()
    {
        char *pre_socket   = getenv("_WAYFIRE_SOCKET");
        const auto& dname  = wf::get_core().wayland_display;
        std::string socket = pre_socket ?: "/tmp/wayfire-" + dname + ".socket";
        setenv("WAYFIRE_SOCKET", socket.c_str(), 1);

        server = std::make_unique<ipc::server_t>(socket);
        server->register_method("core/list_views", list_views);
        server->register_method("core/create_wayland_output", create_wayland_output);
    }

    using method_t = ipc::server_t::method_cb;

    method_t list_views = [] (nlohmann::json)
    {
        auto response = nlohmann::json::array();

        for (auto& view : wf::get_core().get_all_views())
        {
            nlohmann::json v;
            v["title"]    = view->get_title();
            v["app-id"]   = view->get_app_id();
            v["geometry"] = geometry_to_json(view->get_wm_geometry());
            v["base-geometry"] = geometry_to_json(view->get_output_geometry());
            v["state"] = {
                {"tiled", view->tiled_edges},
                {"fullscreen", view->fullscreen},
                {"minimized", view->minimized},
            };

            auto layer = view->get_output()->workspace->get_view_layer(view);
            v["layer"] = layer_to_string(layer);

            response.push_back(v);
        }

        return response;
    };

    method_t create_wayland_output = [] (nlohmann::json)
    {
        auto backend = wf::get_core().backend;

        wlr_backend *wayland_backend = NULL;
        wlr_multi_for_each_backend(backend, locate_wayland_backend,
            &wayland_backend);

        if (!wayland_backend)
        {
            return nlohmann::json{
                {"error", "Wayfire is not running in nested wayland mode!"},
            };
        }

        wlr_wl_output_create(wayland_backend);
        return nlohmann::json{
            {"result", "ok"}
        };
    };

    std::unique_ptr<ipc::server_t> server;
};
}

DECLARE_WAYFIRE_PLUGIN((wf::singleton_plugin_t<wf::ipc_plugin_t, false>));
<|endoftext|>


<commit_before>/*********************************************************************
* Software License Agreement (BSD License)
*
*  Copyright (c) 2012, Willow Garage, Inc.
*  Copyright (c) 2018 Intel Corporation.
*  All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the Willow Garage nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
*  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
*  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
*  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
*  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
*  POSSIBILITY OF SUCH DAMAGE.
*********************************************************************/

#include "module.hpp"
#include <string>

PyObject * mod_opencv;

bp::object
cvtColor2Wrap(bp::object obj_in, const std::string & encoding_in, const std::string & encoding_out)
{
  // Convert the Python input to an image
  cv::Mat mat_in;
  convert_to_CvMat2(obj_in.ptr(), mat_in);

  // Call cv_bridge for color conversion
  cv_bridge::CvImagePtr cv_image(new cv_bridge::CvImage(
      std_msgs::msg::Header(), encoding_in, mat_in));

  cv::Mat mat = cv_bridge::cvtColor(cv_image, encoding_out)->image;

  return bp::object(boost::python::handle<>(pyopencv_from(mat)));
}

bp::object
cvtColorForDisplayWrap(
  bp::object obj_in,
  const std::string & encoding_in,
  const std::string & encoding_out,
  bool do_dynamic_scaling = false,
  double min_image_value = 0.0,
  double max_image_value = 0.0)
{
  // Convert the Python input to an image
  cv::Mat mat_in;
  convert_to_CvMat2(obj_in.ptr(), mat_in);

  cv_bridge::CvImagePtr cv_image(new cv_bridge::CvImage(
      std_msgs::msg::Header(), encoding_in, mat_in));

  cv_bridge::CvtColorForDisplayOptions options;
  options.do_dynamic_scaling = do_dynamic_scaling;
  options.min_image_value = min_image_value;
  options.max_image_value = max_image_value;
  cv::Mat mat = cv_bridge::cvtColorForDisplay(/*source=*/ cv_image,
      /*encoding_out=*/ encoding_out,
      /*options=*/ options)->image;

  return bp::object(boost::python::handle<>(pyopencv_from(mat)));
}

BOOST_PYTHON_FUNCTION_OVERLOADS(cvtColorForDisplayWrap_overloads, cvtColorForDisplayWrap, 3, 6)

int CV_MAT_CNWrap(int i)
{
  return CV_MAT_CN(i);
}

int CV_MAT_DEPTHWrap(int i)
{
  return CV_MAT_DEPTH(i);
}

BOOST_PYTHON_MODULE(cv_bridge_boost)
{
  do_numpy_import();
  mod_opencv = PyImport_ImportModule("cv2");

  // Wrap the function to get encodings as OpenCV types
  boost::python::def("getCvType", cv_bridge::getCvType);
  boost::python::def("cvtColor2", cvtColor2Wrap);
  boost::python::def("CV_MAT_CNWrap", CV_MAT_CNWrap);
  boost::python::def("CV_MAT_DEPTHWrap", CV_MAT_DEPTHWrap);
  boost::python::def("cvtColorForDisplay", cvtColorForDisplayWrap,
    cvtColorForDisplayWrap_overloads(
      boost::python::args("source", "encoding_in", "encoding_out", "do_dynamic_scaling",
      "min_image_value", "max_image_value"),
      "Convert image to display with specified encodings.\n\n"
      "Args:\n"
      "  - source (numpy.ndarray): input image\n"
      "  - encoding_in (str): input image encoding\n"
      "  - encoding_out (str): encoding to which the image conveted\n"
      "  - do_dynamic_scaling (bool): flag to do dynamic scaling with min/max value\n"
      "  - min_image_value (float): minimum pixel value for dynamic scaling\n"
      "  - max_image_value (float): maximum pixel value for dynamic scaling\n"
  ));
}
<commit_msg>[cv_bridge] Add colormap argument to python wrapper<commit_after>/*********************************************************************
* Software License Agreement (BSD License)
*
*  Copyright (c) 2012, Willow Garage, Inc.
*  Copyright (c) 2018 Intel Corporation.
*  All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the Willow Garage nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
*  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
*  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
*  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
*  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
*  POSSIBILITY OF SUCH DAMAGE.
*********************************************************************/

#include "module.hpp"
#include <string>

PyObject * mod_opencv;

bp::object
cvtColor2Wrap(bp::object obj_in, const std::string & encoding_in, const std::string & encoding_out)
{
  // Convert the Python input to an image
  cv::Mat mat_in;
  convert_to_CvMat2(obj_in.ptr(), mat_in);

  // Call cv_bridge for color conversion
  cv_bridge::CvImagePtr cv_image(new cv_bridge::CvImage(
      std_msgs::msg::Header(), encoding_in, mat_in));

  cv::Mat mat = cv_bridge::cvtColor(cv_image, encoding_out)->image;

  return bp::object(boost::python::handle<>(pyopencv_from(mat)));
}

bp::object
cvtColorForDisplayWrap(
  bp::object obj_in,
  const std::string & encoding_in,
  const std::string & encoding_out,
  bool do_dynamic_scaling = false,
  double min_image_value = 0.0,
  double max_image_value = 0.0,
  int colormap = -1)
{
  // Convert the Python input to an image
  cv::Mat mat_in;
  convert_to_CvMat2(obj_in.ptr(), mat_in);

  cv_bridge::CvImagePtr cv_image(new cv_bridge::CvImage(
      std_msgs::msg::Header(), encoding_in, mat_in));

  cv_bridge::CvtColorForDisplayOptions options;
  options.do_dynamic_scaling = do_dynamic_scaling;
  options.min_image_value = min_image_value;
  options.max_image_value = max_image_value;
  options.colormap = colormap;
  cv::Mat mat = cv_bridge::cvtColorForDisplay(/*source=*/ cv_image,
      /*encoding_out=*/ encoding_out,
      /*options=*/ options)->image;

  return bp::object(boost::python::handle<>(pyopencv_from(mat)));
}

BOOST_PYTHON_FUNCTION_OVERLOADS(cvtColorForDisplayWrap_overloads, cvtColorForDisplayWrap, 3, 7)

int CV_MAT_CNWrap(int i)
{
  return CV_MAT_CN(i);
}

int CV_MAT_DEPTHWrap(int i)
{
  return CV_MAT_DEPTH(i);
}

BOOST_PYTHON_MODULE(cv_bridge_boost)
{
  do_numpy_import();
  mod_opencv = PyImport_ImportModule("cv2");

  // Wrap the function to get encodings as OpenCV types
  boost::python::def("getCvType", cv_bridge::getCvType);
  boost::python::def("cvtColor2", cvtColor2Wrap);
  boost::python::def("CV_MAT_CNWrap", CV_MAT_CNWrap);
  boost::python::def("CV_MAT_DEPTHWrap", CV_MAT_DEPTHWrap);
  boost::python::def("cvtColorForDisplay", cvtColorForDisplayWrap,
    cvtColorForDisplayWrap_overloads(
      boost::python::args("source", "encoding_in", "encoding_out", "do_dynamic_scaling",
      "min_image_value", "max_image_value", "colormap"),
      "Convert image to display with specified encodings.\n\n"
      "Args:\n"
      "  - source (numpy.ndarray): input image\n"
      "  - encoding_in (str): input image encoding\n"
      "  - encoding_out (str): encoding to which the image conveted\n"
      "  - do_dynamic_scaling (bool): flag to do dynamic scaling with min/max value\n"
      "  - min_image_value (float): minimum pixel value for dynamic scaling\n"
      "  - max_image_value (float): maximum pixel value for dynamic scaling\n"
      "  - colormap (int): colormap to use when converting to color image\n"
  ));
}
<|endoftext|>


<commit_before><commit_msg>Update documentation of TFormula adding the full list  of predefined functions (#8889)<commit_after><|endoftext|>