<commit_before>/*
    This file is part of Android File Transfer For Linux.
    Copyright (C) 2015-2020  Vladimir Menshakov

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

    This library is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library; if not, write to the Free Software Foundation,
    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include <usb/BufferAllocator.h>
#include <usb/Device.h>
#include <Exception.h>
#include <mtp/usb/TimeoutException.h>
#include <mtp/usb/DeviceBusyException.h>
#include <mtp/usb/DeviceNotFoundException.h>
#include <mtp/ByteArray.h>
#include <mtp/log.h>

#include <sys/ioctl.h>
#include <sys/time.h>
#include <poll.h>
#include <signal.h>

#include "linux/usbdevice_fs.h"

#define IOCTL(FD, ...) do \
{ \
	int r = ioctl(FD, __VA_ARGS__); \
	if (r < 0) \
	{ \
		if (errno == EBUSY) \
			throw DeviceBusyException(FD); \
		else if (errno == ENODEV) \
			throw DeviceNotFoundException(); \
		else \
			throw posix::Exception("ioctl(" #__VA_ARGS__ ")"); \
	} \
} while(false)

namespace mtp { namespace usb
{

	InterfaceToken::InterfaceToken(int fd, unsigned interfaceNumber): _fd(fd), _interfaceNumber(interfaceNumber)
	{
		IOCTL(_fd, USBDEVFS_CLAIMINTERFACE, &interfaceNumber);
	}

	InterfaceToken::~InterfaceToken()
	{
		ioctl(_fd, USBDEVFS_RELEASEINTERFACE, &_interfaceNumber);
	}

#define PRINT_CAP(CAP, NAME) \
	if (capabilities & (CAP)) \
	{ \
		debug(NAME " "); \
		capabilities &= ~(CAP); \
	}

	Device::Device(int fd, const EndpointPtr &controlEp): _fd(fd), _capabilities(0), _controlEp(controlEp)
	{
		try { IOCTL(_fd.Get(), USBDEVFS_GET_CAPABILITIES, &_capabilities); }
		catch(const std::exception &ex)
		{ error("get usbfs capabilities failed: ", ex.what()); }

		debug("capabilities = 0x", hex(_capabilities, 8));
		bool mmap = _capabilities & USBDEVFS_CAP_MMAP;
		//disable mmap allocation for now, see https://github.com/whoozle/android-file-transfer-linux/issues/194
#if 1
		mmap = false;
#endif
		_bufferAllocator = std::make_shared<BufferAllocator>(mmap? fd: -1);

		if (_capabilities)
		{
			u32 capabilities = _capabilities;
			PRINT_CAP(USBDEVFS_CAP_ZERO_PACKET, "<zero-packet>");
			PRINT_CAP(USBDEVFS_CAP_BULK_CONTINUATION, "<bulk-continuation>");
			PRINT_CAP(USBDEVFS_CAP_NO_PACKET_SIZE_LIM, "<no-packet-size-limit>");
			PRINT_CAP(USBDEVFS_CAP_BULK_SCATTER_GATHER, "<bulk-scatter-gather>");
			PRINT_CAP(USBDEVFS_CAP_REAP_AFTER_DISCONNECT, "<reap-after-disconnect>");
			PRINT_CAP(USBDEVFS_CAP_MMAP, "<mmap>");
			PRINT_CAP(USBDEVFS_CAP_DROP_PRIVILEGES, "<drop-privileges>");
			PRINT_CAP(USBDEVFS_CAP_CONNINFO_EX, "<conninfo-ex>");
			PRINT_CAP(USBDEVFS_CAP_SUSPEND, "<suspend>");
			if (capabilities)
				debug("<unknown capability 0x", hex(capabilities, 2), ">");
		}
		else
			debug("[none]\n");
	}

	Device::~Device()
	{ }

	void Device::Reset()
	{
		try
		{ IOCTL(_fd.Get(), USBDEVFS_RESET); }
		catch(const std::exception &ex)
		{ error("resetting device failed: ", ex.what()); }
	}

	int Device::GetConfiguration() const
	{
		return 0;
	}

	void Device::SetConfiguration(int idx)
	{
		error("SetConfiguration(", idx, "): not implemented");
	}

	struct Device::Urb : usbdevfs_urb, Noncopyable
	{
		static const int 		MaxBufferSize = 4096;
		BufferAllocator &		Allocator;
		int						Fd;
		int						PacketSize;
		Buffer					DataBuffer;

		Urb(BufferAllocator & allocator, int fd, u8 urbType, const EndpointPtr & ep):
			usbdevfs_urb(),
			Allocator(allocator),
			Fd(fd),
			PacketSize(ep->GetMaxPacketSize()),
			DataBuffer(Allocator.Allocate(std::max(PacketSize, MaxBufferSize / PacketSize * PacketSize)))
		{
			type			= urbType;
			endpoint		= ep->GetAddress();
			buffer			= DataBuffer.GetData();
			buffer_length	= DataBuffer.GetSize();
		}

		usbdevfs_urb *GetKernelUrb()
		{ return static_cast<usbdevfs_urb *>(this); }

		~Urb()
		{ Allocator.Free(DataBuffer); }

		size_t GetTransferSize() const
		{ return DataBuffer.GetSize(); }

		void Submit()
		{
			IOCTL(Fd, USBDEVFS_SUBMITURB, GetKernelUrb());
		}

		void Discard()
		{
			int r = ioctl(Fd, USBDEVFS_DISCARDURB, GetKernelUrb());
			if (r != 0)
			{
				perror("ioctl(USBDEVFS_DISCARDURB)");
			}
		}

		size_t Send(const IObjectInputStreamPtr &inputStream, size_t size)
		{
			if (size > DataBuffer.GetSize())
				throw std::logic_error("invalid size passed to Send");
			auto data = DataBuffer.GetData();
			size_t r = inputStream->Read(data, size);
			//HexDump("write", ByteArray(data, data + r), true);
			buffer_length = r;
			return r;
		}

		size_t Send(const ByteArray &inputData)
		{
			size_t r = std::min(DataBuffer.GetSize(), inputData.size());
			std::copy(inputData.data(), inputData.data() + r, DataBuffer.GetData());
			buffer_length = r;
			return r;
		}

		size_t Recv(const IObjectOutputStreamPtr &outputStream)
		{
			auto data = DataBuffer.GetData();
			//HexDump("read", ByteArray(data, data + actual_length), true);
			return outputStream->Write(data, actual_length);
		}

		template<unsigned Flag>
		void SetFlag(bool value)
		{
			if (value)
				flags |= Flag;
			else
				flags &= ~Flag;
		}

		void SetContinuationFlag(bool continuation)
		{ SetFlag<USBDEVFS_URB_BULK_CONTINUATION>(continuation); }

		void SetZeroPacketFlag(bool zero)
		{ SetFlag<USBDEVFS_URB_ZERO_PACKET>(zero); }
	};

	void * Device::Reap(int timeout)
	{
		auto urb = AsyncReap(); //attempt to pick up old urbs
		if (urb)
			return urb;

		timeval started = {};
		if (gettimeofday(&started, NULL) == -1)
			throw posix::Exception("gettimeofday");

		pollfd fd = {};
		fd.fd		= _fd.Get();
		fd.events	= POLLOUT | POLLWRNORM;
		int r = poll(&fd, 1, timeout);

		if (r < 0)
			throw posix::Exception("poll");

		timeval now = {};
		if (gettimeofday(&now, NULL) == -1)
			throw posix::Exception("gettimeofday");

		if (r == 0 && timeout > 0)
		{
			int ms = (now.tv_sec - started.tv_sec) * 1000 + (now.tv_usec - started.tv_usec) / 1000;
			error(ms, " ms since the last poll call");
		}
		urb = AsyncReap();
		if (urb)
			return urb;
		else
			throw TimeoutException("timeout reaping usb urb");
	}

	void * Device::AsyncReap()
	{
		usbdevfs_urb *urb;
		int r = ioctl(_fd.Get(), USBDEVFS_REAPURBNDELAY, &urb);
		if (r == 0)
			return urb;
		else if (errno == EAGAIN)
			return nullptr;
		else
			throw posix::Exception("ioctl(USBDEVFS_REAPURBNDELAY)");
	}

	void Device::ClearHalt(const EndpointPtr & ep)
	{
		try
		{ unsigned index = ep->GetAddress(); IOCTL(_fd.Get(), USBDEVFS_CLEAR_HALT, &index); }
		catch(const std::exception &ex)
		{ error("clearing halt status for ep ", hex(ep->GetAddress(), 2), ": ", ex.what()); }
	}

	void Device::Submit(Urb *urb, int timeout)
	{
		urb->Submit();
		try
		{
			while(true)
			{
				usbdevfs_urb * completedKernelUrb = static_cast<usbdevfs_urb *>(Reap(timeout));
				if (urb->GetKernelUrb() != completedKernelUrb)
				{
					error("got unknown urb: ", completedKernelUrb);
					continue;
				}
				else
					break;
			}
		}
		catch(const TimeoutException &ex)
		{
			urb->Discard();
			throw;
		}
		catch(const std::exception &ex)
		{
			error("error while submitting urb: ", ex.what());
			urb->Discard();
			throw;
		}
	}

	void Device::WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout)
	{
		Urb urb(*_bufferAllocator, _fd.Get(), USBDEVFS_URB_TYPE_BULK, ep);
		size_t transferSize = urb.GetTransferSize();

		size_t r;
		bool continuation = false;
		do
		{
			r = urb.Send(inputStream, transferSize);

			if (_capabilities & USBDEVFS_CAP_ZERO_PACKET)
				urb.SetZeroPacketFlag(r != transferSize);

			if (_capabilities & USBDEVFS_CAP_BULK_CONTINUATION)
			{
				urb.SetContinuationFlag(continuation);
				continuation = true;
			}
			Submit(&urb, timeout);
		}
		while(r == transferSize);
	}

	void Device::ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout)
	{
		Urb urb(*_bufferAllocator, _fd.Get(), USBDEVFS_URB_TYPE_BULK, ep);
		size_t transferSize = urb.GetTransferSize();

		size_t r;
		bool continuation = false;
		do
		{
			if (_capabilities & USBDEVFS_CAP_BULK_CONTINUATION)
			{
				urb.SetContinuationFlag(continuation);
				continuation = true;
			}
			Submit(&urb, timeout);

			r = urb.Recv(outputStream);
		}
		while(r == transferSize);
	}

	u8 Device::TransactionType(const EndpointPtr &ep)
	{
		EndpointType type = ep->GetType();
		switch(type)
		{
		case EndpointType::Control:
			return USBDEVFS_URB_TYPE_CONTROL;
		case EndpointType::Isochronous:
			return USBDEVFS_URB_TYPE_ISO;
		case EndpointType::Bulk:
			return USBDEVFS_URB_TYPE_BULK;
		case EndpointType::Interrupt:
			return USBDEVFS_URB_TYPE_INTERRUPT;
		default:
			throw std::runtime_error("invalid endpoint type");
		}
	}

	void Device::ReadControl(u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout)
	{
		debug("read control ", hex(type, 2), " ", hex(req, 2), " ", hex(value, 4), " ", hex(index, 4));
		usbdevfs_ctrltransfer ctrl = { };
		ctrl.bRequestType = type;
		ctrl.bRequest = req;
		ctrl.wValue = value;
		ctrl.wIndex = index;
		ctrl.wLength = data.size();
		ctrl.data = const_cast<u8 *>(data.data());
		ctrl.timeout = timeout;

		int fd = _fd.Get();

		int r = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
		if (r >= 0)
			data.resize(r);
		else if (errno == EAGAIN)
			throw TimeoutException("timeout sending control transfer");
		else
			throw posix::Exception("ioctl");
	}

	void Device::WriteControl(u8 type, u8 req, u16 value, u16 index, const ByteArray &data, int timeout)
	{
		debug("write control ", hex(type, 2), " ", hex(req, 2), " ", hex(value, 4), " ", hex(index, 4));
		usbdevfs_ctrltransfer ctrl = { };
		ctrl.bRequestType = type;
		ctrl.bRequest = req;
		ctrl.wValue = value;
		ctrl.wIndex = index;
		ctrl.wLength = data.size();
		ctrl.data = const_cast<u8 *>(data.data());
		ctrl.timeout = timeout;

		int fd = _fd.Get();

		int r = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
		if (r >= 0)
			return;
		else if (errno == EAGAIN)
			throw TimeoutException("timeout sending control transfer");
		else
			throw posix::Exception("ioctl");
	}

}}
<commit_msg>add USBDEVFS_SETCONFIGURATION stub for linux usb bus backend<commit_after>/*
    This file is part of Android File Transfer For Linux.
    Copyright (C) 2015-2020  Vladimir Menshakov

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

    This library is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library; if not, write to the Free Software Foundation,
    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include <usb/BufferAllocator.h>
#include <usb/Device.h>
#include <Exception.h>
#include <mtp/usb/TimeoutException.h>
#include <mtp/usb/DeviceBusyException.h>
#include <mtp/usb/DeviceNotFoundException.h>
#include <mtp/ByteArray.h>
#include <mtp/log.h>

#include <sys/ioctl.h>
#include <sys/time.h>
#include <poll.h>
#include <signal.h>

#include "linux/usbdevice_fs.h"

#define IOCTL(FD, ...) do \
{ \
	int r = ioctl(FD, __VA_ARGS__); \
	if (r < 0) \
	{ \
		if (errno == EBUSY) \
			throw DeviceBusyException(FD); \
		else if (errno == ENODEV) \
			throw DeviceNotFoundException(); \
		else \
			throw posix::Exception("ioctl(" #__VA_ARGS__ ")"); \
	} \
} while(false)

namespace mtp { namespace usb
{

	InterfaceToken::InterfaceToken(int fd, unsigned interfaceNumber): _fd(fd), _interfaceNumber(interfaceNumber)
	{
		IOCTL(_fd, USBDEVFS_CLAIMINTERFACE, &interfaceNumber);
	}

	InterfaceToken::~InterfaceToken()
	{
		ioctl(_fd, USBDEVFS_RELEASEINTERFACE, &_interfaceNumber);
	}

#define PRINT_CAP(CAP, NAME) \
	if (capabilities & (CAP)) \
	{ \
		debug(NAME " "); \
		capabilities &= ~(CAP); \
	}

	Device::Device(int fd, const EndpointPtr &controlEp): _fd(fd), _capabilities(0), _controlEp(controlEp)
	{
		try { IOCTL(_fd.Get(), USBDEVFS_GET_CAPABILITIES, &_capabilities); }
		catch(const std::exception &ex)
		{ error("get usbfs capabilities failed: ", ex.what()); }

		debug("capabilities = 0x", hex(_capabilities, 8));
		bool mmap = _capabilities & USBDEVFS_CAP_MMAP;
		//disable mmap allocation for now, see https://github.com/whoozle/android-file-transfer-linux/issues/194
#if 1
		mmap = false;
#endif
		_bufferAllocator = std::make_shared<BufferAllocator>(mmap? fd: -1);

		if (_capabilities)
		{
			u32 capabilities = _capabilities;
			PRINT_CAP(USBDEVFS_CAP_ZERO_PACKET, "<zero-packet>");
			PRINT_CAP(USBDEVFS_CAP_BULK_CONTINUATION, "<bulk-continuation>");
			PRINT_CAP(USBDEVFS_CAP_NO_PACKET_SIZE_LIM, "<no-packet-size-limit>");
			PRINT_CAP(USBDEVFS_CAP_BULK_SCATTER_GATHER, "<bulk-scatter-gather>");
			PRINT_CAP(USBDEVFS_CAP_REAP_AFTER_DISCONNECT, "<reap-after-disconnect>");
			PRINT_CAP(USBDEVFS_CAP_MMAP, "<mmap>");
			PRINT_CAP(USBDEVFS_CAP_DROP_PRIVILEGES, "<drop-privileges>");
			PRINT_CAP(USBDEVFS_CAP_CONNINFO_EX, "<conninfo-ex>");
			PRINT_CAP(USBDEVFS_CAP_SUSPEND, "<suspend>");
			if (capabilities)
				debug("<unknown capability 0x", hex(capabilities, 2), ">");
		}
		else
			debug("[none]\n");
	}

	Device::~Device()
	{ }

	void Device::Reset()
	{
		try
		{ IOCTL(_fd.Get(), USBDEVFS_RESET); }
		catch(const std::exception &ex)
		{ error("resetting device failed: ", ex.what()); }
	}

	int Device::GetConfiguration() const
	{
		return 0;
	}

	void Device::SetConfiguration(int idx)
	{
		error("SetConfiguration(", idx, "): not implemented");
		//IOCTL(_fd.Get(), USBDEVFS_SETCONFIGURATION, &idx); //this will crash your device forever
	}

	struct Device::Urb : usbdevfs_urb, Noncopyable
	{
		static const int 		MaxBufferSize = 4096;
		BufferAllocator &		Allocator;
		int						Fd;
		int						PacketSize;
		Buffer					DataBuffer;

		Urb(BufferAllocator & allocator, int fd, u8 urbType, const EndpointPtr & ep):
			usbdevfs_urb(),
			Allocator(allocator),
			Fd(fd),
			PacketSize(ep->GetMaxPacketSize()),
			DataBuffer(Allocator.Allocate(std::max(PacketSize, MaxBufferSize / PacketSize * PacketSize)))
		{
			type			= urbType;
			endpoint		= ep->GetAddress();
			buffer			= DataBuffer.GetData();
			buffer_length	= DataBuffer.GetSize();
		}

		usbdevfs_urb *GetKernelUrb()
		{ return static_cast<usbdevfs_urb *>(this); }

		~Urb()
		{ Allocator.Free(DataBuffer); }

		size_t GetTransferSize() const
		{ return DataBuffer.GetSize(); }

		void Submit()
		{
			IOCTL(Fd, USBDEVFS_SUBMITURB, GetKernelUrb());
		}

		void Discard()
		{
			int r = ioctl(Fd, USBDEVFS_DISCARDURB, GetKernelUrb());
			if (r != 0)
			{
				perror("ioctl(USBDEVFS_DISCARDURB)");
			}
		}

		size_t Send(const IObjectInputStreamPtr &inputStream, size_t size)
		{
			if (size > DataBuffer.GetSize())
				throw std::logic_error("invalid size passed to Send");
			auto data = DataBuffer.GetData();
			size_t r = inputStream->Read(data, size);
			//HexDump("write", ByteArray(data, data + r), true);
			buffer_length = r;
			return r;
		}

		size_t Send(const ByteArray &inputData)
		{
			size_t r = std::min(DataBuffer.GetSize(), inputData.size());
			std::copy(inputData.data(), inputData.data() + r, DataBuffer.GetData());
			buffer_length = r;
			return r;
		}

		size_t Recv(const IObjectOutputStreamPtr &outputStream)
		{
			auto data = DataBuffer.GetData();
			//HexDump("read", ByteArray(data, data + actual_length), true);
			return outputStream->Write(data, actual_length);
		}

		template<unsigned Flag>
		void SetFlag(bool value)
		{
			if (value)
				flags |= Flag;
			else
				flags &= ~Flag;
		}

		void SetContinuationFlag(bool continuation)
		{ SetFlag<USBDEVFS_URB_BULK_CONTINUATION>(continuation); }

		void SetZeroPacketFlag(bool zero)
		{ SetFlag<USBDEVFS_URB_ZERO_PACKET>(zero); }
	};

	void * Device::Reap(int timeout)
	{
		auto urb = AsyncReap(); //attempt to pick up old urbs
		if (urb)
			return urb;

		timeval started = {};
		if (gettimeofday(&started, NULL) == -1)
			throw posix::Exception("gettimeofday");

		pollfd fd = {};
		fd.fd		= _fd.Get();
		fd.events	= POLLOUT | POLLWRNORM;
		int r = poll(&fd, 1, timeout);

		if (r < 0)
			throw posix::Exception("poll");

		timeval now = {};
		if (gettimeofday(&now, NULL) == -1)
			throw posix::Exception("gettimeofday");

		if (r == 0 && timeout > 0)
		{
			int ms = (now.tv_sec - started.tv_sec) * 1000 + (now.tv_usec - started.tv_usec) / 1000;
			error(ms, " ms since the last poll call");
		}
		urb = AsyncReap();
		if (urb)
			return urb;
		else
			throw TimeoutException("timeout reaping usb urb");
	}

	void * Device::AsyncReap()
	{
		usbdevfs_urb *urb;
		int r = ioctl(_fd.Get(), USBDEVFS_REAPURBNDELAY, &urb);
		if (r == 0)
			return urb;
		else if (errno == EAGAIN)
			return nullptr;
		else
			throw posix::Exception("ioctl(USBDEVFS_REAPURBNDELAY)");
	}

	void Device::ClearHalt(const EndpointPtr & ep)
	{
		try
		{ unsigned index = ep->GetAddress(); IOCTL(_fd.Get(), USBDEVFS_CLEAR_HALT, &index); }
		catch(const std::exception &ex)
		{ error("clearing halt status for ep ", hex(ep->GetAddress(), 2), ": ", ex.what()); }
	}

	void Device::Submit(Urb *urb, int timeout)
	{
		urb->Submit();
		try
		{
			while(true)
			{
				usbdevfs_urb * completedKernelUrb = static_cast<usbdevfs_urb *>(Reap(timeout));
				if (urb->GetKernelUrb() != completedKernelUrb)
				{
					error("got unknown urb: ", completedKernelUrb);
					continue;
				}
				else
					break;
			}
		}
		catch(const TimeoutException &ex)
		{
			urb->Discard();
			throw;
		}
		catch(const std::exception &ex)
		{
			error("error while submitting urb: ", ex.what());
			urb->Discard();
			throw;
		}
	}

	void Device::WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout)
	{
		Urb urb(*_bufferAllocator, _fd.Get(), USBDEVFS_URB_TYPE_BULK, ep);
		size_t transferSize = urb.GetTransferSize();

		size_t r;
		bool continuation = false;
		do
		{
			r = urb.Send(inputStream, transferSize);

			if (_capabilities & USBDEVFS_CAP_ZERO_PACKET)
				urb.SetZeroPacketFlag(r != transferSize);

			if (_capabilities & USBDEVFS_CAP_BULK_CONTINUATION)
			{
				urb.SetContinuationFlag(continuation);
				continuation = true;
			}
			Submit(&urb, timeout);
		}
		while(r == transferSize);
	}

	void Device::ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout)
	{
		Urb urb(*_bufferAllocator, _fd.Get(), USBDEVFS_URB_TYPE_BULK, ep);
		size_t transferSize = urb.GetTransferSize();

		size_t r;
		bool continuation = false;
		do
		{
			if (_capabilities & USBDEVFS_CAP_BULK_CONTINUATION)
			{
				urb.SetContinuationFlag(continuation);
				continuation = true;
			}
			Submit(&urb, timeout);

			r = urb.Recv(outputStream);
		}
		while(r == transferSize);
	}

	u8 Device::TransactionType(const EndpointPtr &ep)
	{
		EndpointType type = ep->GetType();
		switch(type)
		{
		case EndpointType::Control:
			return USBDEVFS_URB_TYPE_CONTROL;
		case EndpointType::Isochronous:
			return USBDEVFS_URB_TYPE_ISO;
		case EndpointType::Bulk:
			return USBDEVFS_URB_TYPE_BULK;
		case EndpointType::Interrupt:
			return USBDEVFS_URB_TYPE_INTERRUPT;
		default:
			throw std::runtime_error("invalid endpoint type");
		}
	}

	void Device::ReadControl(u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout)
	{
		debug("read control ", hex(type, 2), " ", hex(req, 2), " ", hex(value, 4), " ", hex(index, 4));
		usbdevfs_ctrltransfer ctrl = { };
		ctrl.bRequestType = type;
		ctrl.bRequest = req;
		ctrl.wValue = value;
		ctrl.wIndex = index;
		ctrl.wLength = data.size();
		ctrl.data = const_cast<u8 *>(data.data());
		ctrl.timeout = timeout;

		int fd = _fd.Get();

		int r = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
		if (r >= 0)
			data.resize(r);
		else if (errno == EAGAIN)
			throw TimeoutException("timeout sending control transfer");
		else
			throw posix::Exception("ioctl");
	}

	void Device::WriteControl(u8 type, u8 req, u16 value, u16 index, const ByteArray &data, int timeout)
	{
		debug("write control ", hex(type, 2), " ", hex(req, 2), " ", hex(value, 4), " ", hex(index, 4));
		usbdevfs_ctrltransfer ctrl = { };
		ctrl.bRequestType = type;
		ctrl.bRequest = req;
		ctrl.wValue = value;
		ctrl.wIndex = index;
		ctrl.wLength = data.size();
		ctrl.data = const_cast<u8 *>(data.data());
		ctrl.timeout = timeout;

		int fd = _fd.Get();

		int r = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
		if (r >= 0)
			return;
		else if (errno == EAGAIN)
			throw TimeoutException("timeout sending control transfer");
		else
			throw posix::Exception("ioctl");
	}

}}
<|endoftext|>


<commit_before>// ========================================================================== //
// This file is part of DO++, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2013 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

#include <gtest/gtest.h>
#include <DO/Core/Timer.hpp>
#include <DO/Core/DebugUtilities.hpp>
#include <chrono>
#include <thread>

using namespace DO;
using namespace std;

inline void sleep(unsigned milliseconds)
{
  chrono::milliseconds duration(milliseconds);
  this_thread::sleep_for(duration);
}

TEST(DO_Core_Test, testTimer)
{
  Timer timer;
  HighResTimer hrTimer;
  double elapsedTimeMs;
  double elapsedTimeS;
  unsigned sleepTimeMs = 580;

  hrTimer.restart();
  sleep(sleepTimeMs);
  elapsedTimeMs =  hrTimer.elapsedMs();
  EXPECT_NEAR(elapsedTimeMs, sleepTimeMs, 100);

  hrTimer.restart();
  sleep(sleepTimeMs);
  elapsedTimeS = hrTimer.elapsed();
  EXPECT_NEAR(elapsedTimeS, sleepTimeMs/1e3, 2e-3);

  timer.restart();
  sleep(sleepTimeMs);
  elapsedTimeS = timer.elapsed();
  EXPECT_NEAR(elapsedTimeS, sleepTimeMs/1e3, 1e-2);
}

int main(int argc, char** argv)
{
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}<commit_msg>Renamed sleep to milliSleep in test_Timer.cpp<commit_after>// ========================================================================== //
// This file is part of DO++, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2013 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

#include <gtest/gtest.h>
#include <DO/Core/Timer.hpp>
#include <DO/Core/DebugUtilities.hpp>
#include <chrono>
#include <thread>

using namespace DO;
using namespace std;

inline void milliSleep(unsigned milliseconds)
{
  chrono::milliseconds duration(milliseconds);
  this_thread::sleep_for(duration);
}

TEST(DO_Core_Test, testTimer)
{
  Timer timer;
  HighResTimer hrTimer;
  double elapsedTimeMs;
  double elapsedTimeS;
  unsigned sleepTimeMs = 580;

  hrTimer.restart();
  milliSleep(sleepTimeMs);
  elapsedTimeMs =  hrTimer.elapsedMs();
  EXPECT_NEAR(elapsedTimeMs, sleepTimeMs, 100);

  hrTimer.restart();
  milliSleep(sleepTimeMs);
  elapsedTimeS = hrTimer.elapsed();
  EXPECT_NEAR(elapsedTimeS, sleepTimeMs/1e3, 2e-3);

  timer.restart();
  milliSleep(sleepTimeMs);
  elapsedTimeS = timer.elapsed();
  EXPECT_NEAR(elapsedTimeS, sleepTimeMs/1e3, 1e-2);
}

int main(int argc, char** argv)
{
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}<|endoftext|>


<commit_before>/*=========================================================================

Program:   Insight Segmentation & Registration Toolkit
Module:    $RCSfile: CuberilleTest01.cxx,v $
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) Insight Software Consortium. All rights reserved.
See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

   This software is distributed WITHOUT ANY WARRANTY; without even
   the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
   PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#define USE_BSPLINE_INTERPOLATOR 0
#define USE_MARCHING_CUBES 0
#define USE_QUAD_EDGE_MESH 0
#define USE_DECIMATION 0

#include <iostream>
#include <sstream>

#include "itkTimeProbe.h"
#include "itkImage.h"
#include "itkMesh.h"
#include "itkQuadEdgeMesh.h"
#include "itkCuberilleImageToMeshFilter.h"
#include "itkBinaryThresholdImageFilter.h"
#include "itkBinaryMask3DMeshSource.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkVTKPolyDataWriter.h"
#include "itkLinearInterpolateImageFunction.h"
#include "itkBSplineInterpolateImageFunction.h"
#include "itkQuadEdgeMeshQuadricDecimation.h"
#include "itkQuadEdgeMeshDecimationCriteria.h"

int CuberilleTest01(int argc, char * argv [])
{
try
  {
  // Typedefs
  const unsigned int Dimension = 3;
  typedef unsigned char PixelType;
  //typedef signed short PixelType;
  typedef itk::Image< PixelType, Dimension > ImageType;
#if USE_QUAD_EDGE_MESH | USE_DECIMATION
  typedef itk::QuadEdgeMesh< PixelType, Dimension > MeshType;
#else
  typedef itk::Mesh< PixelType, Dimension > MeshType;
#endif
  typedef itk::ImageFileReader< ImageType > ImageFileReaderType;
  typedef itk::VTKPolyDataWriter< MeshType > MeshFileWriterType;
#if USE_BSPLINE_INTERPOLATOR
  typedef itk::BSplineInterpolateImageFunction< ImageType, float, float > InterpolatorType;
#else
  typedef itk::LinearInterpolateImageFunction< ImageType > InterpolatorType;
#endif
  typedef itk::CuberilleImageToMeshFilter< ImageType, MeshType, InterpolatorType > CuberilleType;
  typedef itk::BinaryMask3DMeshSource< ImageType, MeshType > MarchingCubesType;
  typedef itk::BinaryThresholdImageFilter< ImageType, ImageType > BinaryThresholdFilterType;

  // Read command-line parameters
  if ( argc < 6 )
    {
      std::cout << "USAGE: " << argv[0];
      std::cout << "InputImage OutputMesh IsoSurfaceValue ExpectedNumberOfPoints ExpectedNumberOfCells";
      std::cout << "[GenerateTriangleFaces] [ProjectToIsoSurface] ";
      std::cout << "[SurfaceDistanceThreshold] [StepLength] [StepLengthRelax] [MaximumNumberOfSteps]";
      std::cout << std::endl;
      return EXIT_FAILURE;
    }
  int arg = 1;
  char * FilenameInputImage = argv[arg++];
  char * FilenameOutputMesh = argv[arg++];
  PixelType IsoSurfaceValue = atoi( argv[arg++] );
  unsigned int ExpectedNumberOfPoints = atoi( argv[arg++] );
  unsigned int ExpectedNumberOfCells = atoi( argv[arg++] );
  bool GenerateTriangleFaces = true;
  if (argc > arg) GenerateTriangleFaces = atoi( argv[arg++] );
  bool ProjectToIsoSurface = true;
  if (argc > arg) ProjectToIsoSurface = atoi( argv[arg++] );
  double SurfaceDistanceThreshold = 0.5;
  if (argc > arg) SurfaceDistanceThreshold = atof( argv[arg++] );
  double StepLength = 0.25;
  if (argc > arg) StepLength = atof( argv[arg++] );
  double StepLengthRelax = 0.95;
  if (argc > arg) StepLengthRelax = atof( argv[arg++] );
  unsigned int MaximumNumberOfSteps = 50;
  if (argc > arg) MaximumNumberOfSteps = atoi( argv[arg++] );

  // Read input image
  std::cout << "Reading input image: " << FilenameInputImage << std::endl;
  ImageFileReaderType::Pointer reader = ImageFileReaderType::New();
  reader->SetFileName( FilenameInputImage );
  reader->UpdateLargestPossibleRegion();
  ImageType::Pointer input = reader->GetOutput();
  input->DisconnectPipeline();

  // Create output mesh
  MeshType::Pointer outputMesh = NULL;
  itk::TimeProbe time;
#if USE_MARCHING_CUBES

  // Create marching cubes
  std::cout << "Creating marching cubes mesh..." << std::endl;
  BinaryThresholdFilterType::Pointer threshold = BinaryThresholdFilterType::New();
  threshold->SetInput( input );
  threshold->SetLowerThreshold( IsoSurfaceValue );
  threshold->SetUpperThreshold( itk::NumericTraits<PixelType>::max() );
  threshold->SetInsideValue( itk::NumericTraits<PixelType>::One );
  threshold->SetOutsideValue( itk::NumericTraits<PixelType>::Zero );
  threshold->UpdateLargestPossibleRegion();
  MarchingCubesType::Pointer marching = MarchingCubesType::New();
  marching->SetInput( threshold->GetOutput() );
  time.Start();
  marching->Update();
  time.Stop();
  outputMesh = marching->GetOutput();
  outputMesh->DisconnectPipeline();
#else

  // Create cuberille mesh filter
  std::cout << "Creating cuberille mesh..." << std::endl;
  CuberilleType::Pointer cuberille = CuberilleType::New();
  cuberille->SetInput( input );
  cuberille->SetIsoSurfaceValue( IsoSurfaceValue );
  InterpolatorType::Pointer interpolator = InterpolatorType::New();
#if USE_BSPLINE_INTERPOLATOR
  interpolator->SetSplineOrder( 3 );
#endif
  cuberille->SetInterpolator( interpolator );
  cuberille->SetGenerateTriangleFaces( GenerateTriangleFaces );
  cuberille->SetProjectVerticesToIsoSurface( ProjectToIsoSurface );
  cuberille->SetProjectVertexSurfaceDistanceThreshold( SurfaceDistanceThreshold );
  cuberille->SetProjectVertexStepLength( StepLength );
  cuberille->SetProjectVertexStepLengthRelaxationFactor( StepLengthRelax );
  cuberille->SetProjectVertexMaximumNumberOfSteps( MaximumNumberOfSteps );
  time.Start();
  cuberille->Update();
  time.Stop();
  outputMesh = cuberille->GetOutput();
  outputMesh->DisconnectPipeline();
#endif

#if USE_DECIMATION
  // Decimation
  typedef itk::NumberOfFacesCriterion< MeshType > DecimationCriterionType;
  DecimationCriterionType::Pointer decimateCriterion = DecimationCriterionType::New();
  decimateCriterion->SetTopologicalChange( false );
  decimateCriterion->SetNumberOfElements( 2000 );
  typedef itk::QuadEdgeMeshQuadricDecimation< MeshType, MeshType, DecimationCriterionType > DecimationType;
  DecimationType::Pointer decimate = DecimationType::New();
  decimate->SetInput( outputMesh );
  decimate->SetCriterion( decimateCriterion );
  decimate->Update();
#endif

  // Write mesh
  std::cout << "Writing output mesh: " << FilenameOutputMesh << std::endl;
  MeshFileWriterType::Pointer writer = MeshFileWriterType::New();
#if USE_DECIMATION
  writer->SetInput( decimate->GetOutput() );
#else
  writer->SetInput( outputMesh );
#endif
  writer->SetFileName( FilenameOutputMesh );
  writer->Update();

  // Assert number of points/cells
  std::cout << "Polygonization took " << time.GetMeanTime() << " seconds" << std::endl;
  std::cout << "Mesh has " << outputMesh->GetNumberOfPoints() << " vertices ";
  std::cout << "and " << outputMesh->GetNumberOfCells() << " cells" << std::endl;
  if ( ExpectedNumberOfPoints > 0 && outputMesh->GetNumberOfPoints() != ExpectedNumberOfPoints )
    {
    std::cerr << "ERROR: Expected mesh with " << ExpectedNumberOfPoints
              << " points, but found " << outputMesh->GetNumberOfPoints() << std::endl;
    return EXIT_FAILURE;
    }
  if ( ExpectedNumberOfCells > 0 && outputMesh->GetNumberOfCells() != ExpectedNumberOfCells )
    {
    std::cerr << "ERROR: Expected mesh with " << ExpectedNumberOfCells
              << " cells, but found " << outputMesh->GetNumberOfCells() << std::endl;
    return EXIT_FAILURE;
    }
  return EXIT_SUCCESS;
  }
catch (itk::ExceptionObject & err)
  {
  std::cerr << "ExceptionObject caught !" << std::endl;
  std::cerr << err << std::endl;
  return EXIT_FAILURE;
  }
}
<commit_msg>COMP: QuadEdgeMeshQuadricDecimation renamed to QuadricDecimationQuadEdgeMeshFilter.<commit_after>/*=========================================================================

Program:   Insight Segmentation & Registration Toolkit
Module:    $RCSfile: CuberilleTest01.cxx,v $
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) Insight Software Consortium. All rights reserved.
See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

   This software is distributed WITHOUT ANY WARRANTY; without even
   the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
   PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#define USE_BSPLINE_INTERPOLATOR 0
#define USE_MARCHING_CUBES 0
#define USE_QUAD_EDGE_MESH 0
#define USE_DECIMATION 0

#include <iostream>
#include <sstream>

#include "itkTimeProbe.h"
#include "itkImage.h"
#include "itkMesh.h"
#include "itkQuadEdgeMesh.h"
#include "itkCuberilleImageToMeshFilter.h"
#include "itkBinaryThresholdImageFilter.h"
#include "itkBinaryMask3DMeshSource.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkVTKPolyDataWriter.h"
#include "itkLinearInterpolateImageFunction.h"
#include "itkBSplineInterpolateImageFunction.h"
#include "itkQuadricDecimationQuadEdgeMeshFilter.h"
#include "itkQuadEdgeMeshDecimationCriteria.h"

int CuberilleTest01(int argc, char * argv [])
{
try
  {
  // Typedefs
  const unsigned int Dimension = 3;
  typedef unsigned char PixelType;
  //typedef signed short PixelType;
  typedef itk::Image< PixelType, Dimension > ImageType;
#if USE_QUAD_EDGE_MESH | USE_DECIMATION
  typedef itk::QuadEdgeMesh< PixelType, Dimension > MeshType;
#else
  typedef itk::Mesh< PixelType, Dimension > MeshType;
#endif
  typedef itk::ImageFileReader< ImageType > ImageFileReaderType;
  typedef itk::VTKPolyDataWriter< MeshType > MeshFileWriterType;
#if USE_BSPLINE_INTERPOLATOR
  typedef itk::BSplineInterpolateImageFunction< ImageType, float, float > InterpolatorType;
#else
  typedef itk::LinearInterpolateImageFunction< ImageType > InterpolatorType;
#endif
  typedef itk::CuberilleImageToMeshFilter< ImageType, MeshType, InterpolatorType > CuberilleType;
  typedef itk::BinaryMask3DMeshSource< ImageType, MeshType > MarchingCubesType;
  typedef itk::BinaryThresholdImageFilter< ImageType, ImageType > BinaryThresholdFilterType;

  // Read command-line parameters
  if ( argc < 6 )
    {
      std::cout << "USAGE: " << argv[0];
      std::cout << "InputImage OutputMesh IsoSurfaceValue ExpectedNumberOfPoints ExpectedNumberOfCells";
      std::cout << "[GenerateTriangleFaces] [ProjectToIsoSurface] ";
      std::cout << "[SurfaceDistanceThreshold] [StepLength] [StepLengthRelax] [MaximumNumberOfSteps]";
      std::cout << std::endl;
      return EXIT_FAILURE;
    }
  int arg = 1;
  char * FilenameInputImage = argv[arg++];
  char * FilenameOutputMesh = argv[arg++];
  PixelType IsoSurfaceValue = atoi( argv[arg++] );
  unsigned int ExpectedNumberOfPoints = atoi( argv[arg++] );
  unsigned int ExpectedNumberOfCells = atoi( argv[arg++] );
  bool GenerateTriangleFaces = true;
  if (argc > arg) GenerateTriangleFaces = atoi( argv[arg++] );
  bool ProjectToIsoSurface = true;
  if (argc > arg) ProjectToIsoSurface = atoi( argv[arg++] );
  double SurfaceDistanceThreshold = 0.5;
  if (argc > arg) SurfaceDistanceThreshold = atof( argv[arg++] );
  double StepLength = 0.25;
  if (argc > arg) StepLength = atof( argv[arg++] );
  double StepLengthRelax = 0.95;
  if (argc > arg) StepLengthRelax = atof( argv[arg++] );
  unsigned int MaximumNumberOfSteps = 50;
  if (argc > arg) MaximumNumberOfSteps = atoi( argv[arg++] );

  // Read input image
  std::cout << "Reading input image: " << FilenameInputImage << std::endl;
  ImageFileReaderType::Pointer reader = ImageFileReaderType::New();
  reader->SetFileName( FilenameInputImage );
  reader->UpdateLargestPossibleRegion();
  ImageType::Pointer input = reader->GetOutput();
  input->DisconnectPipeline();

  // Create output mesh
  MeshType::Pointer outputMesh = NULL;
  itk::TimeProbe time;
#if USE_MARCHING_CUBES

  // Create marching cubes
  std::cout << "Creating marching cubes mesh..." << std::endl;
  BinaryThresholdFilterType::Pointer threshold = BinaryThresholdFilterType::New();
  threshold->SetInput( input );
  threshold->SetLowerThreshold( IsoSurfaceValue );
  threshold->SetUpperThreshold( itk::NumericTraits<PixelType>::max() );
  threshold->SetInsideValue( itk::NumericTraits<PixelType>::One );
  threshold->SetOutsideValue( itk::NumericTraits<PixelType>::Zero );
  threshold->UpdateLargestPossibleRegion();
  MarchingCubesType::Pointer marching = MarchingCubesType::New();
  marching->SetInput( threshold->GetOutput() );
  time.Start();
  marching->Update();
  time.Stop();
  outputMesh = marching->GetOutput();
  outputMesh->DisconnectPipeline();
#else

  // Create cuberille mesh filter
  std::cout << "Creating cuberille mesh..." << std::endl;
  CuberilleType::Pointer cuberille = CuberilleType::New();
  cuberille->SetInput( input );
  cuberille->SetIsoSurfaceValue( IsoSurfaceValue );
  InterpolatorType::Pointer interpolator = InterpolatorType::New();
#if USE_BSPLINE_INTERPOLATOR
  interpolator->SetSplineOrder( 3 );
#endif
  cuberille->SetInterpolator( interpolator );
  cuberille->SetGenerateTriangleFaces( GenerateTriangleFaces );
  cuberille->SetProjectVerticesToIsoSurface( ProjectToIsoSurface );
  cuberille->SetProjectVertexSurfaceDistanceThreshold( SurfaceDistanceThreshold );
  cuberille->SetProjectVertexStepLength( StepLength );
  cuberille->SetProjectVertexStepLengthRelaxationFactor( StepLengthRelax );
  cuberille->SetProjectVertexMaximumNumberOfSteps( MaximumNumberOfSteps );
  time.Start();
  cuberille->Update();
  time.Stop();
  outputMesh = cuberille->GetOutput();
  outputMesh->DisconnectPipeline();
#endif

#if USE_DECIMATION
  // Decimation
  typedef itk::NumberOfFacesCriterion< MeshType > DecimationCriterionType;
  DecimationCriterionType::Pointer decimateCriterion = DecimationCriterionType::New();
  decimateCriterion->SetTopologicalChange( false );
  decimateCriterion->SetNumberOfElements( 2000 );
  typedef itk::QuadricDecimationQuadEdgeMeshFilter< MeshType, MeshType, DecimationCriterionType > DecimationType;
  DecimationType::Pointer decimate = DecimationType::New();
  decimate->SetInput( outputMesh );
  decimate->SetCriterion( decimateCriterion );
  decimate->Update();
#endif

  // Write mesh
  std::cout << "Writing output mesh: " << FilenameOutputMesh << std::endl;
  MeshFileWriterType::Pointer writer = MeshFileWriterType::New();
#if USE_DECIMATION
  writer->SetInput( decimate->GetOutput() );
#else
  writer->SetInput( outputMesh );
#endif
  writer->SetFileName( FilenameOutputMesh );
  writer->Update();

  // Assert number of points/cells
  std::cout << "Polygonization took " << time.GetMeanTime() << " seconds" << std::endl;
  std::cout << "Mesh has " << outputMesh->GetNumberOfPoints() << " vertices ";
  std::cout << "and " << outputMesh->GetNumberOfCells() << " cells" << std::endl;
  if ( ExpectedNumberOfPoints > 0 && outputMesh->GetNumberOfPoints() != ExpectedNumberOfPoints )
    {
    std::cerr << "ERROR: Expected mesh with " << ExpectedNumberOfPoints
              << " points, but found " << outputMesh->GetNumberOfPoints() << std::endl;
    return EXIT_FAILURE;
    }
  if ( ExpectedNumberOfCells > 0 && outputMesh->GetNumberOfCells() != ExpectedNumberOfCells )
    {
    std::cerr << "ERROR: Expected mesh with " << ExpectedNumberOfCells
              << " cells, but found " << outputMesh->GetNumberOfCells() << std::endl;
    return EXIT_FAILURE;
    }
  return EXIT_SUCCESS;
  }
catch (itk::ExceptionObject & err)
  {
  std::cerr << "ExceptionObject caught !" << std::endl;
  std::cerr << err << std::endl;
  return EXIT_FAILURE;
  }
}
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#define USE_BSPLINE_INTERPOLATOR 0
#define USE_MARCHING_CUBES 0
#define USE_QUAD_EDGE_MESH 0
#define USE_DECIMATION 0

#include "itkTimeProbe.h"
#include "itkImage.h"
#include "itkMesh.h"
#include "itkQuadEdgeMesh.h"
#include "itkCuberilleImageToMeshFilter.h"
#include "itkBinaryThresholdImageFilter.h"
#include "itkBinaryMask3DMeshSource.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkVTKPolyDataWriter.h"
#include "itkLinearInterpolateImageFunction.h"
#include "itkBSplineInterpolateImageFunction.h"
#include "itkQuadricDecimationQuadEdgeMeshFilter.h"
#include "itkQuadEdgeMeshDecimationCriteria.h"
#include "itkTestingMacros.h"

int CuberilleTest01 (int argc, char * argv [] )
{
  if( argc < 6 )
    {
    std::cout << "Usage: " << argv[0];
    std::cout << "InputImage OutputMesh IsoSurfaceValue ExpectedNumberOfPoints ExpectedNumberOfCells";
    std::cout << "[GenerateTriangleFaces] [ProjectToIsoSurface] ";
    std::cout << "[SurfaceDistanceThreshold] [StepLength] [StepLengthRelax] [MaximumNumberOfSteps]";
    std::cout << std::endl;
    return EXIT_FAILURE;
    }

  // Typedefs
  const unsigned int Dimension = 3;
  using PixelType = unsigned char;
  //using PixelType = signed short;
  using ImageType = itk::Image< PixelType, Dimension >;

#if USE_QUAD_EDGE_MESH | USE_DECIMATION
  using MeshType = itk::QuadEdgeMesh< PixelType, Dimension >;
#else
  using MeshType = itk::Mesh< PixelType, Dimension >;
#endif
  using ImageFileReaderType = itk::ImageFileReader< ImageType >;
  using MeshFileWriterType = itk::VTKPolyDataWriter< MeshType >;
#if USE_BSPLINE_INTERPOLATOR
  using InterpolatorType = itk::BSplineInterpolateImageFunction< ImageType, float, float >;
#else
  using InterpolatorType = itk::LinearInterpolateImageFunction< ImageType >;
#endif
  using CuberilleType = itk::CuberilleImageToMeshFilter< ImageType, MeshType, InterpolatorType >;

  // Read command-line parameters
  int arg = 1;
  char * filenameInputImage = argv[arg++];
  char * filenameOutputMesh = argv[arg++];
  PixelType isoSurfaceValue = atoi( argv[arg++] );
  unsigned int expectedNumberOfPoints = atoi( argv[arg++] );
  unsigned int expectedNumberOfCells = atoi( argv[arg++] );

  bool generateTriangleFaces = true;
  if( argc > arg )
    {
    generateTriangleFaces = atoi( argv[arg++] );
    }

  bool projectToIsoSurface = true;
  if( argc > arg )
    {
    projectToIsoSurface = atoi( argv[arg++] );
    }

  double surfaceDistanceThreshold = 0.5;
  if( argc > arg )
    {
    surfaceDistanceThreshold = atof( argv[arg++] );
    }

  double stepLength = 0.25;
  if( argc > arg )
    {
    stepLength = atof( argv[arg++] );
    }

  double stepLengthRelax = 0.95;
  if( argc > arg )
    {
    stepLengthRelax = atof( argv[arg++] );
    }

  unsigned int maximumNumberOfSteps = 50;
  if( argc > arg )
    {
    maximumNumberOfSteps = atoi( argv[arg++] );
    }

  // Read input image
  ImageFileReaderType::Pointer reader = ImageFileReaderType::New();
  reader->SetFileName( filenameInputImage );

  TRY_EXPECT_NO_EXCEPTION( reader->UpdateLargestPossibleRegion() );

  ImageType::Pointer input = reader->GetOutput();
  input->DisconnectPipeline();

  // Create output mesh
  MeshType::Pointer outputMesh = nullptr;
  itk::TimeProbe time;
#if USE_MARCHING_CUBES

  // Create marching cubes mesh
  BinaryThresholdFilterType::Pointer threshold = BinaryThresholdFilterType::New();
  threshold->SetInput( input );
  threshold->SetLowerThreshold( IsoSurfaceValue );
  threshold->SetUpperThreshold( itk::NumericTraits<PixelType>::max() );
  threshold->SetInsideValue( itk::NumericTraits<PixelType>::One );
  threshold->SetOutsideValue( itk::NumericTraits<PixelType>::Zero );
  threshold->UpdateLargestPossibleRegion();

  MarchingCubesType::Pointer marching = MarchingCubesType::New();
  marching->SetInput( threshold->GetOutput() );

  time.Start();

  TRY_EXPECT_NO_EXCEPTION( marching->Update() );

  time.Stop();

  outputMesh = marching->GetOutput();
  outputMesh->DisconnectPipeline();
#else

  // Create cuberille mesh filter
  CuberilleType::Pointer cuberille = CuberilleType::New();

  EXERCISE_BASIC_OBJECT_METHODS( cuberille, CuberilleImageToMeshFilter,
    ImageToMeshFilter );

  cuberille->SetInput( input );

  cuberille->SetIsoSurfaceValue( isoSurfaceValue );
  TEST_SET_GET_VALUE( isoSurfaceValue, cuberille->GetIsoSurfaceValue() );

  InterpolatorType::Pointer interpolator = InterpolatorType::New();
#if USE_BSPLINE_INTERPOLATOR
  unsigned int splineOrder = 3;
  interpolator->SetSplineOrder( splineOrder );
#endif
  cuberille->SetInterpolator( interpolator );
  TEST_SET_GET_VALUE( interpolator, cuberille->GetInterpolator() );

  cuberille->SetGenerateTriangleFaces( generateTriangleFaces );
  TEST_SET_GET_VALUE( generateTriangleFaces,
    cuberille->GetGenerateTriangleFaces() );

  cuberille->SetProjectVerticesToIsoSurface( projectToIsoSurface );
  TEST_SET_GET_VALUE( projectToIsoSurface,
    cuberille->GetProjectVerticesToIsoSurface() );

  cuberille->SetProjectVertexSurfaceDistanceThreshold( surfaceDistanceThreshold );
  TEST_SET_GET_VALUE( surfaceDistanceThreshold,
    cuberille->GetProjectVertexSurfaceDistanceThreshold() );

  cuberille->SetProjectVertexStepLength( stepLength );
  TEST_SET_GET_VALUE( stepLength, cuberille->GetProjectVertexStepLength() );

  cuberille->SetProjectVertexStepLengthRelaxationFactor( stepLengthRelax );
  TEST_SET_GET_VALUE( stepLengthRelax,
    cuberille->GetProjectVertexStepLengthRelaxationFactor() );

  cuberille->SetProjectVertexMaximumNumberOfSteps( maximumNumberOfSteps );
  TEST_SET_GET_VALUE( maximumNumberOfSteps,
    cuberille->GetProjectVertexMaximumNumberOfSteps() );

  time.Start();

  TRY_EXPECT_NO_EXCEPTION( cuberille->Update() );

  time.Stop();

  outputMesh = cuberille->GetOutput();

  outputMesh->DisconnectPipeline();

#endif

#if USE_DECIMATION
  // Decimation
  using DecimationCriterionType = itk::NumberOfFacesCriterion< MeshType >;
  DecimationCriterionType::Pointer decimateCriterion = DecimationCriterionType::New();
  decimateCriterion->SetTopologicalChange( false );
  decimateCriterion->SetNumberOfElements( 2000 );

  using DecimationType = itk::QuadricDecimationQuadEdgeMeshFilter< MeshType, MeshType, DecimationCriterionType >;
  DecimationType::Pointer decimate = DecimationType::New();
  decimate->SetCriterion( decimateCriterion );

  decimate->SetInput( outputMesh );

  TRY_EXPECT_NO_EXCEPTION( decimate->Update() );
#endif

  // Write mesh
  MeshFileWriterType::Pointer writer = MeshFileWriterType::New();
#if USE_DECIMATION
  writer->SetInput( decimate->GetOutput() );
#else
  writer->SetInput( outputMesh );
#endif
  writer->SetFileName( filenameOutputMesh );

  TRY_EXPECT_NO_EXCEPTION( writer->Update() );

  // Assert number of points/cells
  std::cout << "Polygonization took " << time.GetMean() << " seconds" << std::endl;
  std::cout << "Mesh has " << outputMesh->GetNumberOfPoints() << " vertices ";
  std::cout << "and " << outputMesh->GetNumberOfCells() << " cells" << std::endl;
  if( expectedNumberOfPoints > 0 && outputMesh->GetNumberOfPoints() != expectedNumberOfPoints )
    {
    std::cerr << "Test failed!" << std::endl;
    std::cerr << "Error: Expected mesh with " << expectedNumberOfPoints
              << " points, but found " << outputMesh->GetNumberOfPoints() << std::endl;
    return EXIT_FAILURE;
    }
  if( expectedNumberOfCells > 0 && outputMesh->GetNumberOfCells() != expectedNumberOfCells )
    {
    std::cerr << "Test failed!" << std::endl;
    std::cerr << "Error: Expected mesh with " << expectedNumberOfCells
              << " cells, but found " << outputMesh->GetNumberOfCells() << std::endl;
    return EXIT_FAILURE;
    }

  std::cout << "Test finished" << std::endl;
  return EXIT_SUCCESS;
}
<commit_msg>STYLE: Prefer constexpr for const numeric literals<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#define USE_BSPLINE_INTERPOLATOR 0
#define USE_MARCHING_CUBES 0
#define USE_QUAD_EDGE_MESH 0
#define USE_DECIMATION 0

#include "itkTimeProbe.h"
#include "itkImage.h"
#include "itkMesh.h"
#include "itkQuadEdgeMesh.h"
#include "itkCuberilleImageToMeshFilter.h"
#include "itkBinaryThresholdImageFilter.h"
#include "itkBinaryMask3DMeshSource.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkVTKPolyDataWriter.h"
#include "itkLinearInterpolateImageFunction.h"
#include "itkBSplineInterpolateImageFunction.h"
#include "itkQuadricDecimationQuadEdgeMeshFilter.h"
#include "itkQuadEdgeMeshDecimationCriteria.h"
#include "itkTestingMacros.h"

int CuberilleTest01 (int argc, char * argv [] )
{
  if( argc < 6 )
    {
    std::cout << "Usage: " << argv[0];
    std::cout << "InputImage OutputMesh IsoSurfaceValue ExpectedNumberOfPoints ExpectedNumberOfCells";
    std::cout << "[GenerateTriangleFaces] [ProjectToIsoSurface] ";
    std::cout << "[SurfaceDistanceThreshold] [StepLength] [StepLengthRelax] [MaximumNumberOfSteps]";
    std::cout << std::endl;
    return EXIT_FAILURE;
    }

  // Typedefs
  constexpr unsigned int Dimension = 3;
  using PixelType = unsigned char;
  //using PixelType = signed short;
  using ImageType = itk::Image< PixelType, Dimension >;

#if USE_QUAD_EDGE_MESH | USE_DECIMATION
  using MeshType = itk::QuadEdgeMesh< PixelType, Dimension >;
#else
  using MeshType = itk::Mesh< PixelType, Dimension >;
#endif
  using ImageFileReaderType = itk::ImageFileReader< ImageType >;
  using MeshFileWriterType = itk::VTKPolyDataWriter< MeshType >;
#if USE_BSPLINE_INTERPOLATOR
  using InterpolatorType = itk::BSplineInterpolateImageFunction< ImageType, float, float >;
#else
  using InterpolatorType = itk::LinearInterpolateImageFunction< ImageType >;
#endif
  using CuberilleType = itk::CuberilleImageToMeshFilter< ImageType, MeshType, InterpolatorType >;

  // Read command-line parameters
  int arg = 1;
  char * filenameInputImage = argv[arg++];
  char * filenameOutputMesh = argv[arg++];
  PixelType isoSurfaceValue = atoi( argv[arg++] );
  unsigned int expectedNumberOfPoints = atoi( argv[arg++] );
  unsigned int expectedNumberOfCells = atoi( argv[arg++] );

  bool generateTriangleFaces = true;
  if( argc > arg )
    {
    generateTriangleFaces = atoi( argv[arg++] );
    }

  bool projectToIsoSurface = true;
  if( argc > arg )
    {
    projectToIsoSurface = atoi( argv[arg++] );
    }

  double surfaceDistanceThreshold = 0.5;
  if( argc > arg )
    {
    surfaceDistanceThreshold = atof( argv[arg++] );
    }

  double stepLength = 0.25;
  if( argc > arg )
    {
    stepLength = atof( argv[arg++] );
    }

  double stepLengthRelax = 0.95;
  if( argc > arg )
    {
    stepLengthRelax = atof( argv[arg++] );
    }

  unsigned int maximumNumberOfSteps = 50;
  if( argc > arg )
    {
    maximumNumberOfSteps = atoi( argv[arg++] );
    }

  // Read input image
  ImageFileReaderType::Pointer reader = ImageFileReaderType::New();
  reader->SetFileName( filenameInputImage );

  TRY_EXPECT_NO_EXCEPTION( reader->UpdateLargestPossibleRegion() );

  ImageType::Pointer input = reader->GetOutput();
  input->DisconnectPipeline();

  // Create output mesh
  MeshType::Pointer outputMesh = nullptr;
  itk::TimeProbe time;
#if USE_MARCHING_CUBES

  // Create marching cubes mesh
  BinaryThresholdFilterType::Pointer threshold = BinaryThresholdFilterType::New();
  threshold->SetInput( input );
  threshold->SetLowerThreshold( IsoSurfaceValue );
  threshold->SetUpperThreshold( itk::NumericTraits<PixelType>::max() );
  threshold->SetInsideValue( itk::NumericTraits<PixelType>::One );
  threshold->SetOutsideValue( itk::NumericTraits<PixelType>::Zero );
  threshold->UpdateLargestPossibleRegion();

  MarchingCubesType::Pointer marching = MarchingCubesType::New();
  marching->SetInput( threshold->GetOutput() );

  time.Start();

  TRY_EXPECT_NO_EXCEPTION( marching->Update() );

  time.Stop();

  outputMesh = marching->GetOutput();
  outputMesh->DisconnectPipeline();
#else

  // Create cuberille mesh filter
  CuberilleType::Pointer cuberille = CuberilleType::New();

  EXERCISE_BASIC_OBJECT_METHODS( cuberille, CuberilleImageToMeshFilter,
    ImageToMeshFilter );

  cuberille->SetInput( input );

  cuberille->SetIsoSurfaceValue( isoSurfaceValue );
  TEST_SET_GET_VALUE( isoSurfaceValue, cuberille->GetIsoSurfaceValue() );

  InterpolatorType::Pointer interpolator = InterpolatorType::New();
#if USE_BSPLINE_INTERPOLATOR
  unsigned int splineOrder = 3;
  interpolator->SetSplineOrder( splineOrder );
#endif
  cuberille->SetInterpolator( interpolator );
  TEST_SET_GET_VALUE( interpolator, cuberille->GetInterpolator() );

  cuberille->SetGenerateTriangleFaces( generateTriangleFaces );
  TEST_SET_GET_VALUE( generateTriangleFaces,
    cuberille->GetGenerateTriangleFaces() );

  cuberille->SetProjectVerticesToIsoSurface( projectToIsoSurface );
  TEST_SET_GET_VALUE( projectToIsoSurface,
    cuberille->GetProjectVerticesToIsoSurface() );

  cuberille->SetProjectVertexSurfaceDistanceThreshold( surfaceDistanceThreshold );
  TEST_SET_GET_VALUE( surfaceDistanceThreshold,
    cuberille->GetProjectVertexSurfaceDistanceThreshold() );

  cuberille->SetProjectVertexStepLength( stepLength );
  TEST_SET_GET_VALUE( stepLength, cuberille->GetProjectVertexStepLength() );

  cuberille->SetProjectVertexStepLengthRelaxationFactor( stepLengthRelax );
  TEST_SET_GET_VALUE( stepLengthRelax,
    cuberille->GetProjectVertexStepLengthRelaxationFactor() );

  cuberille->SetProjectVertexMaximumNumberOfSteps( maximumNumberOfSteps );
  TEST_SET_GET_VALUE( maximumNumberOfSteps,
    cuberille->GetProjectVertexMaximumNumberOfSteps() );

  time.Start();

  TRY_EXPECT_NO_EXCEPTION( cuberille->Update() );

  time.Stop();

  outputMesh = cuberille->GetOutput();

  outputMesh->DisconnectPipeline();

#endif

#if USE_DECIMATION
  // Decimation
  using DecimationCriterionType = itk::NumberOfFacesCriterion< MeshType >;
  DecimationCriterionType::Pointer decimateCriterion = DecimationCriterionType::New();
  decimateCriterion->SetTopologicalChange( false );
  decimateCriterion->SetNumberOfElements( 2000 );

  using DecimationType = itk::QuadricDecimationQuadEdgeMeshFilter< MeshType, MeshType, DecimationCriterionType >;
  DecimationType::Pointer decimate = DecimationType::New();
  decimate->SetCriterion( decimateCriterion );

  decimate->SetInput( outputMesh );

  TRY_EXPECT_NO_EXCEPTION( decimate->Update() );
#endif

  // Write mesh
  MeshFileWriterType::Pointer writer = MeshFileWriterType::New();
#if USE_DECIMATION
  writer->SetInput( decimate->GetOutput() );
#else
  writer->SetInput( outputMesh );
#endif
  writer->SetFileName( filenameOutputMesh );

  TRY_EXPECT_NO_EXCEPTION( writer->Update() );

  // Assert number of points/cells
  std::cout << "Polygonization took " << time.GetMean() << " seconds" << std::endl;
  std::cout << "Mesh has " << outputMesh->GetNumberOfPoints() << " vertices ";
  std::cout << "and " << outputMesh->GetNumberOfCells() << " cells" << std::endl;
  if( expectedNumberOfPoints > 0 && outputMesh->GetNumberOfPoints() != expectedNumberOfPoints )
    {
    std::cerr << "Test failed!" << std::endl;
    std::cerr << "Error: Expected mesh with " << expectedNumberOfPoints
              << " points, but found " << outputMesh->GetNumberOfPoints() << std::endl;
    return EXIT_FAILURE;
    }
  if( expectedNumberOfCells > 0 && outputMesh->GetNumberOfCells() != expectedNumberOfCells )
    {
    std::cerr << "Test failed!" << std::endl;
    std::cerr << "Error: Expected mesh with " << expectedNumberOfCells
              << " cells, but found " << outputMesh->GetNumberOfCells() << std::endl;
    return EXIT_FAILURE;
    }

  std::cout << "Test finished" << std::endl;
  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>//----------------------------------------------------------------------------
// Author:		Martin Klemsa
//----------------------------------------------------------------------------
#ifndef _ctoolhu_std_ext_included_
#define _ctoolhu_std_ext_included_

#include <algorithm>
#include <cassert>
#include <numeric>
#include <type_traits>

//this is a std extension by purpose, so will stay outside the Ctoolhu namespace

namespace std_ext {

	template <class Container, typename Sum>
	Sum accumulate(const Container &c, Sum init)
	{
		return std::accumulate(std::cbegin(c), std::cend(c), init);
	}

	template <class Container, typename Sum, class Reductor>
	Sum accumulate(const Container &c, Sum init, Reductor &&r)
	{
		return std::accumulate(std::cbegin(c), std::cend(c), init, std::forward<Reductor>(r));
	}

	template <class Container, class Predicate>
	bool all_of(const Container &c, const Predicate &p)
	{
		return std::all_of(std::cbegin(c), std::cend(c), p);
	}

	template <class Container, class Predicate>
	bool any_of(const Container &c, const Predicate &p)
	{
		return std::any_of(std::cbegin(c), std::cend(c), p);
	}

	template <class LookupContainer, class T>
	bool binary_search(const LookupContainer &c, const T &v)
	{
		return std::binary_search(std::cbegin(c), std::cend(c), v);
	}

	template <class SourceContainer, class OutputIterator>
	auto copy(const SourceContainer &src, OutputIterator &&dst)
	{
		return std::copy(std::cbegin(src), std::cend(src), std::forward<OutputIterator>(dst));
	}

	template <class SourceContainer, class OutputIterator, class Predicate>
	auto copy_if(const SourceContainer &src, OutputIterator &&dst, Predicate &&p)
	{
		return std::copy_if(std::cbegin(src), std::cend(src), std::forward<OutputIterator>(dst), std::forward<Predicate>(p));
	}

	template <class Container, class Value>
	auto count(const Container &c, Value v)
	{
		return std::count(std::cbegin(c), std::cend(c), v);
	}

	template <class Container, class Predicate>
	auto count_if(const Container &c, Predicate &&p)
	{
		return std::count_if(std::cbegin(c), std::cend(c), std::forward<Predicate>(p));
	}

	template <class Container, class Value>
	auto count_sorted(const Container &c, Value v)
	{
		return std::distance(std::lower_bound(std::cbegin(c), std::cend(c), v), std::upper_bound(std::cbegin(c), std::cend(c), v));
	}

	template <class Container, class Value>
	auto find(const Container &c, const Value &v)
	{
		return std::find(std::cbegin(c), std::cend(c), v);
	}

	template <class Container, class Predicate>
	auto find_if(Container &c, Predicate &&p)
	{
		return std::find_if(std::begin(c), std::end(c), std::forward<Predicate>(p));
	}

	template<class Container, class Value>
	auto insert_sorted(Container& c, const Value &v)
	{
		return c.insert(std::upper_bound(c.begin(), c.end(), v), v);
	}

	template <class Container>
	auto max_element(const Container &c) noexcept
	{
		return std::max_element(std::cbegin(c), std::cend(c));
	}

	template <class Container, class Predicate>
	auto max_element(const Container &c, Predicate &&p) noexcept
	{
		return std::max_element(std::cbegin(c), std::cend(c), std::forward<Predicate>(p));
	}

	template <class Container>
	auto min_element(const Container &c) noexcept
	{
		return std::min_element(std::cbegin(c), std::cend(c));
	}

	template <class Container, class Predicate>
	typename Container::iterator remove_if(Container &c, Predicate &&p)
	{
		return std::remove_if(std::begin(c), std::end(c), std::forward<Predicate>(p));
	}

	template <class Container>
	void sort(Container &c)
	{
		std::sort(std::begin(c), std::end(c));
	}

	template <class Container, class Comparator>
	void sort(Container &c, Comparator &&comp)
	{
		std::sort(std::begin(c), std::end(c), std::forward<Comparator>(comp));
	}

	template <class Container, class OutputIterator, class Reductor>
	void transform(const Container &src, OutputIterator &&dst, Reductor &&r)
	{
		std::transform(std::cbegin(src), std::cend(src), std::forward<OutputIterator>(dst), std::forward<Reductor>(r));
	}

	template <class Container>
	auto unique(Container &c)
	{
		assert(std::is_sorted(std::cbegin(c), std::cend(c)) && "Container should be sorted");
		return std::unique(std::begin(c), std::end(c));
	}

	template <class Container, class Comparator>
	auto unique(Container &c, Comparator &&comp)
	{
		return std::unique(std::begin(c), std::end(c), std::forward<Comparator>(comp));
	}

	//utilities

	template <class LookupContainer, class T, class Compare = std::less<>>
	auto binary_find(const LookupContainer &c, const T &val, Compare comp = {})
	{
		auto last = std::cend(c);
		auto first = std::lower_bound(std::cbegin(c), last, val, comp);
		return first != last && !comp(val, *first) ? first : last;
	}

	template <class Destination, class Source>
	auto concat(Destination &d, const Source &s)
	{
		d.reserve(d.size() + s.size());
		return std::copy(std::cbegin(s), std::cend(s), std::back_inserter(d));
	}

	template <class Destination, class Source>
	auto concat_move(Destination &d, Source &s)
	{
		d.reserve(d.size() + s.size());
		return std::move(std::begin(s), std::end(s), std::back_inserter(d));
	}

	template <class LookupContainer, class Value>
	bool contains(const LookupContainer &c, Value &&v)
	{
		return std::find(std::cbegin(c), std::cend(c), std::forward<Value>(v)) != std::cend(c);
	}

	template <class HayContainer, class NeedleContainer>
	bool contains_any(const HayContainer &haystack, const NeedleContainer &needles)
	{
		return any_of(needles, [&haystack](auto const &n) {
			return contains(haystack, n);
		});
	}

	template <class Container, class Value>
	auto erase(Container &c, const Value &v)
	{
		return c.erase(find(c, v));
	}

	template <class Container, class Predicate>
	auto erase_if(Container &c, Predicate &&p)
	{
		return c.erase(std::remove_if(std::begin(c), std::end(c), std::forward<Predicate>(p)), std::end(c));
	}

	//removes duplicate values from given sorted container
	template <class Container>
	void erase_duplicates(Container &c)
	{
		c.erase(unique(c), std::end(c));
	}

	//removes duplicate values from given sorted container
	template <class Container, class Comparator>
	void erase_duplicates(Container &c, Comparator &&comp)
	{
		c.erase(unique(c, std::forward<Comparator>(comp)), std::end(c));
	}

	template <class Container>
	bool next_k_permutation(Container &c, int choose)
	{
		auto const first = std::begin(c);
		auto const last = std::end(c);
		std::reverse(first + choose, last);
		return std::next_permutation(first, last);
	}

	template <class Container>
	bool next_combination(Container &c, int choose)
	{
		bool result;
		auto const first = std::begin(c);
		do {
			result = next_k_permutation(c, choose);
		} while (std::adjacent_find(first, first + choose, std::greater{}) != first + choose);
		return result;
	}
}

#endif //file guard
<commit_msg>Fixed missing include header for targeting C++20<commit_after>//----------------------------------------------------------------------------
// Author:		Martin Klemsa
//----------------------------------------------------------------------------
#ifndef _ctoolhu_std_ext_included_
#define _ctoolhu_std_ext_included_

#include <algorithm>
#include <cassert>
#include <iterator>
#include <numeric>
#include <type_traits>

//this is a std extension by purpose, so will stay outside the Ctoolhu namespace

namespace std_ext {

	template <class Container, typename Sum>
	Sum accumulate(const Container &c, Sum init)
	{
		return std::accumulate(std::cbegin(c), std::cend(c), init);
	}

	template <class Container, typename Sum, class Reductor>
	Sum accumulate(const Container &c, Sum init, Reductor &&r)
	{
		return std::accumulate(std::cbegin(c), std::cend(c), init, std::forward<Reductor>(r));
	}

	template <class Container, class Predicate>
	bool all_of(const Container &c, const Predicate &p)
	{
		return std::all_of(std::cbegin(c), std::cend(c), p);
	}

	template <class Container, class Predicate>
	bool any_of(const Container &c, const Predicate &p)
	{
		return std::any_of(std::cbegin(c), std::cend(c), p);
	}

	template <class LookupContainer, class T>
	bool binary_search(const LookupContainer &c, const T &v)
	{
		return std::binary_search(std::cbegin(c), std::cend(c), v);
	}

	template <class SourceContainer, class OutputIterator>
	auto copy(const SourceContainer &src, OutputIterator &&dst)
	{
		return std::copy(std::cbegin(src), std::cend(src), std::forward<OutputIterator>(dst));
	}

	template <class SourceContainer, class OutputIterator, class Predicate>
	auto copy_if(const SourceContainer &src, OutputIterator &&dst, Predicate &&p)
	{
		return std::copy_if(std::cbegin(src), std::cend(src), std::forward<OutputIterator>(dst), std::forward<Predicate>(p));
	}

	template <class Container, class Value>
	auto count(const Container &c, Value v)
	{
		return std::count(std::cbegin(c), std::cend(c), v);
	}

	template <class Container, class Predicate>
	auto count_if(const Container &c, Predicate &&p)
	{
		return std::count_if(std::cbegin(c), std::cend(c), std::forward<Predicate>(p));
	}

	template <class Container, class Value>
	auto count_sorted(const Container &c, Value v)
	{
		return std::distance(std::lower_bound(std::cbegin(c), std::cend(c), v), std::upper_bound(std::cbegin(c), std::cend(c), v));
	}

	template <class Container, class Value>
	auto find(const Container &c, const Value &v)
	{
		return std::find(std::cbegin(c), std::cend(c), v);
	}

	template <class Container, class Predicate>
	auto find_if(Container &c, Predicate &&p)
	{
		return std::find_if(std::begin(c), std::end(c), std::forward<Predicate>(p));
	}

	template<class Container, class Value>
	auto insert_sorted(Container& c, const Value &v)
	{
		return c.insert(std::upper_bound(c.begin(), c.end(), v), v);
	}

	template <class Container>
	auto max_element(const Container &c) noexcept
	{
		return std::max_element(std::cbegin(c), std::cend(c));
	}

	template <class Container, class Predicate>
	auto max_element(const Container &c, Predicate &&p) noexcept
	{
		return std::max_element(std::cbegin(c), std::cend(c), std::forward<Predicate>(p));
	}

	template <class Container>
	auto min_element(const Container &c) noexcept
	{
		return std::min_element(std::cbegin(c), std::cend(c));
	}

	template <class Container, class Predicate>
	typename Container::iterator remove_if(Container &c, Predicate &&p)
	{
		return std::remove_if(std::begin(c), std::end(c), std::forward<Predicate>(p));
	}

	template <class Container>
	void sort(Container &c)
	{
		std::sort(std::begin(c), std::end(c));
	}

	template <class Container, class Comparator>
	void sort(Container &c, Comparator &&comp)
	{
		std::sort(std::begin(c), std::end(c), std::forward<Comparator>(comp));
	}

	template <class Container, class OutputIterator, class Reductor>
	void transform(const Container &src, OutputIterator &&dst, Reductor &&r)
	{
		std::transform(std::cbegin(src), std::cend(src), std::forward<OutputIterator>(dst), std::forward<Reductor>(r));
	}

	template <class Container>
	auto unique(Container &c)
	{
		assert(std::is_sorted(std::cbegin(c), std::cend(c)) && "Container should be sorted");
		return std::unique(std::begin(c), std::end(c));
	}

	template <class Container, class Comparator>
	auto unique(Container &c, Comparator &&comp)
	{
		return std::unique(std::begin(c), std::end(c), std::forward<Comparator>(comp));
	}

	//utilities

	template <class LookupContainer, class T, class Compare = std::less<>>
	auto binary_find(const LookupContainer &c, const T &val, Compare comp = {})
	{
		auto last = std::cend(c);
		auto first = std::lower_bound(std::cbegin(c), last, val, comp);
		return first != last && !comp(val, *first) ? first : last;
	}

	template <class Destination, class Source>
	auto concat(Destination &d, const Source &s)
	{
		d.reserve(d.size() + s.size());
		return std::copy(std::cbegin(s), std::cend(s), std::back_inserter(d));
	}

	template <class Destination, class Source>
	auto concat_move(Destination &d, Source &s)
	{
		d.reserve(d.size() + s.size());
		return std::move(std::begin(s), std::end(s), std::back_inserter(d));
	}

	template <class LookupContainer, class Value>
	bool contains(const LookupContainer &c, Value &&v)
	{
		return std::find(std::cbegin(c), std::cend(c), std::forward<Value>(v)) != std::cend(c);
	}

	template <class HayContainer, class NeedleContainer>
	bool contains_any(const HayContainer &haystack, const NeedleContainer &needles)
	{
		return any_of(needles, [&haystack](auto const &n) {
			return contains(haystack, n);
		});
	}

	template <class Container, class Value>
	auto erase(Container &c, const Value &v)
	{
		return c.erase(find(c, v));
	}

	template <class Container, class Predicate>
	auto erase_if(Container &c, Predicate &&p)
	{
		return c.erase(std::remove_if(std::begin(c), std::end(c), std::forward<Predicate>(p)), std::end(c));
	}

	//removes duplicate values from given sorted container
	template <class Container>
	void erase_duplicates(Container &c)
	{
		c.erase(unique(c), std::end(c));
	}

	//removes duplicate values from given sorted container
	template <class Container, class Comparator>
	void erase_duplicates(Container &c, Comparator &&comp)
	{
		c.erase(unique(c, std::forward<Comparator>(comp)), std::end(c));
	}

	template <class Container>
	bool next_k_permutation(Container &c, int choose)
	{
		auto const first = std::begin(c);
		auto const last = std::end(c);
		std::reverse(first + choose, last);
		return std::next_permutation(first, last);
	}

	template <class Container>
	bool next_combination(Container &c, int choose)
	{
		bool result;
		auto const first = std::begin(c);
		do {
			result = next_k_permutation(c, choose);
		} while (std::adjacent_find(first, first + choose, std::greater{}) != first + choose);
		return result;
	}
}

#endif //file guard
<|endoftext|>


<commit_before>#include "apfOmega_h.h"

#include <cassert>

#include <apfMesh2.h>
#include <apfNumbering.h>
#include <PCU.h>

namespace osh {

static void connectivityFromAPF(
    apf::Mesh* am,
    osh_t om,
    apf::Numbering* local,
    unsigned ent_dim)
{
  unsigned verts_per_elem = ent_dim + 1;
  unsigned nents = (unsigned) am->count(ent_dim);
  unsigned* conn = osh_build_ents(om, ent_dim, nents);
  apf::MeshEntity* e;
  apf::MeshIterator* it = am->begin(ent_dim);
  unsigned i = 0;
  while ((e = am->iterate(it))) {
    apf::NewArray<int> e2v;
    apf::getElementNumbers(local, e, e2v);
    for (unsigned j = 0; j < verts_per_elem; ++j)
      conn[i * verts_per_elem + j] = (unsigned) e2v[j];
    ++i;
  }
  am->end(it);
}

static void classificationFromAPF(
    apf::Mesh* am,
    osh_t om,
    unsigned ent_dim)
{
  unsigned* class_dim = osh_new_label(om, ent_dim, "class_dim", 1);
  unsigned* class_id = osh_new_label(om, ent_dim, "class_id", 1);
  apf::MeshEntity* e;
  apf::MeshIterator* it = am->begin(ent_dim);
  unsigned i = 0;
  while ((e = am->iterate(it))) {
    apf::ModelEntity* ge = am->toModel(e);
    class_dim[i] = am->getModelType(ge);
    class_id[i] = am->getModelTag(ge);
    ++i;
  }
  am->end(it);
}

static void globalFromAPF(
    apf::Mesh* am,
    osh_t om,
    unsigned ent_dim)
{
  apf::GlobalNumbering* glob_n = apf::makeGlobal(
      apf::numberOwnedDimension(am, "osh_global", ent_dim));
  apf::synchronize(glob_n);
  unsigned long* global = osh_new_global(om, ent_dim);
  apf::MeshEntity* e;
  apf::MeshIterator* it = am->begin(ent_dim);
  unsigned i = 0;
  while ((e = am->iterate(it))) {
    global[i] = apf::getNumber(glob_n, apf::Node(e, 0));
    ++i;
  }
  am->end(it);
  apf::destroyGlobalNumbering(glob_n);
}

static void coordinatesFromAPF(
    apf::Mesh* am,
    osh_t om)
{
  osh_new_field(om, 0, "coordinates", 3);
  double* coords = osh_get_field(om, 0, "coordinates");
  apf::MeshEntity* v;
  apf::MeshIterator* it = am->begin(0);
  unsigned i = 0;
  while ((v = am->iterate(it))) {
    apf::Vector3 pt;
    am->getPoint(v, 0, pt);
    pt.toArray(coords + i * 3);
    ++i;
  }
  am->end(it);
}

static void parametricFromAPF(
    apf::Mesh* am,
    osh_t om)
{
  osh_new_field(om, 0, "param", 3);
  double* param = osh_get_field(om, 0, "param");
  apf::MeshEntity* v;
  apf::MeshIterator* it = am->begin(0);
  unsigned i = 0;
  while ((v = am->iterate(it))) {
    apf::Vector3 pt;
    am->getParam(v, pt);
    pt.toArray(param + i * 3);
    ++i;
  }
  am->end(it);
}

osh_t fromAPF(apf::Mesh* am)
{
  apf::Numbering* local = apf::numberOverlapNodes(am, "osh_id");
  unsigned dim = (unsigned) am->getDimension();
  osh_t om = osh_new(dim);
  unsigned nverts = am->count(0);
  osh_build_ents(om, 0, nverts);
  coordinatesFromAPF(am, om);
  parametricFromAPF(am, om);
  for (unsigned d = 1; d <= dim; ++d)
    connectivityFromAPF(am, om, local, d);
  for (unsigned d = 0; d <= dim; ++d) {
    classificationFromAPF(am, om, d);
    globalFromAPF(am, om, d);
  }
  return om;
}

static apf::MeshEntity** verticesToAPF(
    osh_t om,
    apf::Mesh2* am)
{
  apf::MeshEntity** verts = new apf::MeshEntity*[osh_nverts(om)];
  double const* coords = osh_coords(om);
  double const* param = osh_get_field(om, 0, "param");
  unsigned const* class_dim = osh_get_label(om, 0, "class_dim");
  unsigned const* class_id = osh_get_label(om, 0, "class_id");
  for (unsigned i = 0; i < osh_nverts(om); ++i) {
    apf::ModelEntity* ge = am->findModelEntity(class_dim[i], class_id[i]);
    apf::Vector3 x(coords + i * 3);
    apf::Vector3 p(param + i * 3);
    verts[i] = am->createVertex(ge, x, p);
  }
  assert(am->count(0) == osh_nverts(om));
  return verts;
}

static apf::MeshEntity** entitiesToAPF(
    osh_t om,
    apf::Mesh2* am,
    apf::MeshEntity* const* verts,
    unsigned ent_dim)
{
  apf::MeshEntity** ents = new apf::MeshEntity*[osh_count(om, ent_dim)];
  unsigned const* down = osh_down(om, ent_dim, 0);
  unsigned const* class_dim = osh_get_label(om, ent_dim, "class_dim");
  unsigned const* class_id = osh_get_label(om, ent_dim, "class_id");
  apf::Mesh::Type t = apf::Mesh::simplexTypes[ent_dim];
  unsigned nverts_per_ent = ent_dim + 1;
  for (unsigned i = 0; i < osh_count(om, ent_dim); ++i) {
    apf::ModelEntity* ge = am->findModelEntity(class_dim[i], class_id[i]);
    apf::Downward ev;
    for (unsigned j = 0; j < nverts_per_ent; ++j)
      ev[j] = verts[down[i * nverts_per_ent + j]];
    ents[i] = apf::buildElement(am, ge, t, ev);
  }
  return ents;
}

static void ownersToAPF(
    osh_t om,
    apf::Mesh2* am,
    apf::MeshEntity* const* ents,
    unsigned ent_dim)
{
  unsigned const* own_ranks = osh_own_rank(om, ent_dim);
  unsigned const* own_ids = osh_own_id(om, ent_dim);
  /* currently MDS defines ownership between pairs of ranks
     only, which is insufficient to represent ghosting
     ownership of elements.
     therefore we will tag the owners to the entities,
     which can later be used by an apf::Sharing */
  apf::MeshTag* own_tag = am->findTag("owner");
  if (!own_tag)
    own_tag = am->createIntTag("owner", 1);
  for (unsigned i = 0; i < osh_count(om, ent_dim); ++i) {
    int yay_for_signed = own_ranks[i];
    am->setIntTag(ents[i], own_tag, &yay_for_signed);
  }
  PCU_Comm_Begin();
  for (unsigned i = 0; i < osh_count(om, ent_dim); ++i) {
    PCU_COMM_PACK(own_ranks[i], own_ids[i]);
    PCU_COMM_PACK(own_ranks[i], ents[i]);
  }
  PCU_Comm_Send();
  while (PCU_Comm_Receive()) {
    unsigned own_id;
    PCU_COMM_UNPACK(own_id);
    apf::MeshEntity* r;
    PCU_COMM_UNPACK(r);
    int from = PCU_Comm_Sender();
    if (from == PCU_Comm_Self()) {
      assert((unsigned)from == own_ranks[own_id]);
      continue;
    }
    apf::MeshEntity* owner = ents[own_id];
    am->addRemote(owner, from, r);
  }
  PCU_Comm_Begin();
  for (unsigned i = 0; i < osh_count(om, ent_dim); ++i)
    if (own_ranks[i] == (unsigned)(PCU_Comm_Self())) {
      apf::Copies remotes;
      am->getRemotes(ents[i], remotes);
      unsigned ncopies = remotes.size();
      int self = PCU_Comm_Self();
      APF_ITERATE(apf::Copies, remotes, it) {
        PCU_COMM_PACK(it->first, it->second);
        PCU_COMM_PACK(it->first, ncopies);
        PCU_COMM_PACK(it->first, self);
        PCU_COMM_PACK(it->first, ents[i]);
        APF_ITERATE(apf::Copies, remotes, it2)
          if (it2->first != it->first) {
            PCU_COMM_PACK(it->first, it2->first);
            PCU_COMM_PACK(it->first, it2->second);
          }
      }
    }
  PCU_Comm_Send();
  while (PCU_Comm_Receive()) {
    apf::MeshEntity* e;
    PCU_COMM_UNPACK(e);
    unsigned ncopies;
    PCU_COMM_UNPACK(ncopies);
    for (unsigned i = 0; i < ncopies; ++i) {
      int p;
      PCU_COMM_UNPACK(p);
      apf::MeshEntity* r;
      PCU_COMM_UNPACK(r);
      am->addRemote(e, p, r);
    }
  }
}

void toAPF(osh_t om, apf::Mesh2* am)
{
  apf::MeshEntity** ents[4];
  ents[0] = verticesToAPF(om, am);
  for (unsigned d = 1; d <= osh_dim(om); ++d)
    ents[d] = entitiesToAPF(om, am, ents[0], d);
  for (unsigned d = 0; d <= osh_dim(om); ++d) {
    ownersToAPF(om, am, ents[d], d);
    apf::initResidence(am, d);
  }
  for (unsigned d = 0; d <= osh_dim(om); ++d)
    delete [] ents[d];
  am->acceptChanges();
}

};
<commit_msg>draft code to copy fields to/from omega_h<commit_after>#include "apfOmega_h.h"

#include <cassert>

#include <apfMesh2.h>
#include <apfNumbering.h>
#include <PCU.h>
#include <apf.h>

namespace osh {

static void connectivityFromAPF(
    apf::Mesh* am,
    osh_t om,
    apf::Numbering* local,
    unsigned ent_dim)
{
  unsigned verts_per_elem = ent_dim + 1;
  unsigned nents = (unsigned) am->count(ent_dim);
  unsigned* conn = osh_build_ents(om, ent_dim, nents);
  apf::MeshEntity* e;
  apf::MeshIterator* it = am->begin(ent_dim);
  unsigned i = 0;
  while ((e = am->iterate(it))) {
    apf::NewArray<int> e2v;
    apf::getElementNumbers(local, e, e2v);
    for (unsigned j = 0; j < verts_per_elem; ++j)
      conn[i * verts_per_elem + j] = (unsigned) e2v[j];
    ++i;
  }
  am->end(it);
}

static void classificationFromAPF(
    apf::Mesh* am,
    osh_t om,
    unsigned ent_dim)
{
  unsigned* class_dim = osh_new_label(om, ent_dim, "class_dim", 1);
  unsigned* class_id = osh_new_label(om, ent_dim, "class_id", 1);
  apf::MeshEntity* e;
  apf::MeshIterator* it = am->begin(ent_dim);
  unsigned i = 0;
  while ((e = am->iterate(it))) {
    apf::ModelEntity* ge = am->toModel(e);
    class_dim[i] = am->getModelType(ge);
    class_id[i] = am->getModelTag(ge);
    ++i;
  }
  am->end(it);
}

static void globalFromAPF(
    apf::Mesh* am,
    osh_t om,
    unsigned ent_dim)
{
  apf::GlobalNumbering* glob_n = apf::makeGlobal(
      apf::numberOwnedDimension(am, "osh_global", ent_dim));
  apf::synchronize(glob_n);
  unsigned long* global = osh_new_global(om, ent_dim);
  apf::MeshEntity* e;
  apf::MeshIterator* it = am->begin(ent_dim);
  unsigned i = 0;
  while ((e = am->iterate(it))) {
    global[i] = apf::getNumber(glob_n, apf::Node(e, 0));
    ++i;
  }
  am->end(it);
  apf::destroyGlobalNumbering(glob_n);
}

static void fieldFromAPF(
    apf::Mesh* am,
    osh_t om,
    apf::Field* f)
{
  char const* name = apf::getName(f);
  int nc = apf::countComponents(f);
  osh_new_field(om, 0, name, nc);
  double* data = osh_get_field(om, 0, name);
  apf::MeshEntity* v;
  apf::MeshIterator* it = am->begin(0);
  unsigned i = 0;
  while ((v = am->iterate(it))) {
    apf::getComponents(f, v, 0, data + i * nc);
    ++i;
  }
  am->end(it);
}

static void fieldToAPF(
    apf::Mesh* am,
    osh_t om,
    char const* name)
{
  unsigned nc = osh_components(om, 0, name);
  double* data = osh_get_field(om, 0, name);
  apf::Field* f = apf::createPackedField(am, name, nc);
  apf::MeshEntity* v;
  apf::MeshIterator* it = am->begin(0);
  unsigned i = 0;
  while ((v = am->iterate(it))) {
    apf::setComponents(f, v, 0, data + i * nc);
    ++i;
  }
  am->end(it);
}

static void fieldsFromAPF(
    apf::Mesh* am,
    osh_t om)
{
  for (int i = 0; i < am->countFields(); ++i)
    if (apf::getShape(am->getField(i)) == am->getShape())
      fieldFromAPF(am, om, am->getField(i));
}

static void fieldsToAPF(
    apf::Mesh* am,
    osh_t om)
{
  unsigned nf = osh_nfields(om, 0);
  for (unsigned i = 0; i < nf; ++i) {
    std::string name = osh_field(om, 0, i);
    if (name != "coordinates" && name != "param")
      fieldToAPF(am, om, name.c_str());
  }
}

static void coordinatesFromAPF(
    apf::Mesh* am,
    osh_t om)
{
  fieldFromAPF(am, om, am->getCoordinateField());
}

static void parametricFromAPF(
    apf::Mesh* am,
    osh_t om)
{
  osh_new_field(om, 0, "param", 3);
  double* param = osh_get_field(om, 0, "param");
  apf::MeshEntity* v;
  apf::MeshIterator* it = am->begin(0);
  unsigned i = 0;
  while ((v = am->iterate(it))) {
    apf::Vector3 pt;
    am->getParam(v, pt);
    pt.toArray(param + i * 3);
    ++i;
  }
  am->end(it);
}

osh_t fromAPF(apf::Mesh* am)
{
  apf::Numbering* local = apf::numberOverlapNodes(am, "osh_id");
  unsigned dim = (unsigned) am->getDimension();
  osh_t om = osh_new(dim);
  unsigned nverts = am->count(0);
  osh_build_ents(om, 0, nverts);
  coordinatesFromAPF(am, om);
  parametricFromAPF(am, om);
  for (unsigned d = 1; d <= dim; ++d)
    connectivityFromAPF(am, om, local, d);
  for (unsigned d = 0; d <= dim; ++d) {
    classificationFromAPF(am, om, d);
    globalFromAPF(am, om, d);
  }
  fieldsFromAPF(am, om);
  return om;
}

static apf::MeshEntity** verticesToAPF(
    osh_t om,
    apf::Mesh2* am)
{
  apf::MeshEntity** verts = new apf::MeshEntity*[osh_nverts(om)];
  double const* coords = osh_coords(om);
  double const* param = osh_get_field(om, 0, "param");
  unsigned const* class_dim = osh_get_label(om, 0, "class_dim");
  unsigned const* class_id = osh_get_label(om, 0, "class_id");
  for (unsigned i = 0; i < osh_nverts(om); ++i) {
    apf::ModelEntity* ge = am->findModelEntity(class_dim[i], class_id[i]);
    apf::Vector3 x(coords + i * 3);
    apf::Vector3 p(param + i * 3);
    verts[i] = am->createVertex(ge, x, p);
  }
  assert(am->count(0) == osh_nverts(om));
  return verts;
}

static apf::MeshEntity** entitiesToAPF(
    osh_t om,
    apf::Mesh2* am,
    apf::MeshEntity* const* verts,
    unsigned ent_dim)
{
  apf::MeshEntity** ents = new apf::MeshEntity*[osh_count(om, ent_dim)];
  unsigned const* down = osh_down(om, ent_dim, 0);
  unsigned const* class_dim = osh_get_label(om, ent_dim, "class_dim");
  unsigned const* class_id = osh_get_label(om, ent_dim, "class_id");
  apf::Mesh::Type t = apf::Mesh::simplexTypes[ent_dim];
  unsigned nverts_per_ent = ent_dim + 1;
  for (unsigned i = 0; i < osh_count(om, ent_dim); ++i) {
    apf::ModelEntity* ge = am->findModelEntity(class_dim[i], class_id[i]);
    apf::Downward ev;
    for (unsigned j = 0; j < nverts_per_ent; ++j)
      ev[j] = verts[down[i * nverts_per_ent + j]];
    ents[i] = apf::buildElement(am, ge, t, ev);
  }
  return ents;
}

static void ownersToAPF(
    osh_t om,
    apf::Mesh2* am,
    apf::MeshEntity* const* ents,
    unsigned ent_dim)
{
  unsigned const* own_ranks = osh_own_rank(om, ent_dim);
  unsigned const* own_ids = osh_own_id(om, ent_dim);
  /* currently MDS defines ownership between pairs of ranks
     only, which is insufficient to represent ghosting
     ownership of elements.
     therefore we will tag the owners to the entities,
     which can later be used by an apf::Sharing */
  apf::MeshTag* own_tag = am->findTag("owner");
  if (!own_tag)
    own_tag = am->createIntTag("owner", 1);
  for (unsigned i = 0; i < osh_count(om, ent_dim); ++i) {
    int yay_for_signed = own_ranks[i];
    am->setIntTag(ents[i], own_tag, &yay_for_signed);
  }
  PCU_Comm_Begin();
  for (unsigned i = 0; i < osh_count(om, ent_dim); ++i) {
    PCU_COMM_PACK(own_ranks[i], own_ids[i]);
    PCU_COMM_PACK(own_ranks[i], ents[i]);
  }
  PCU_Comm_Send();
  while (PCU_Comm_Receive()) {
    unsigned own_id;
    PCU_COMM_UNPACK(own_id);
    apf::MeshEntity* r;
    PCU_COMM_UNPACK(r);
    int from = PCU_Comm_Sender();
    if (from == PCU_Comm_Self()) {
      assert((unsigned)from == own_ranks[own_id]);
      continue;
    }
    apf::MeshEntity* owner = ents[own_id];
    am->addRemote(owner, from, r);
  }
  PCU_Comm_Begin();
  for (unsigned i = 0; i < osh_count(om, ent_dim); ++i)
    if (own_ranks[i] == (unsigned)(PCU_Comm_Self())) {
      apf::Copies remotes;
      am->getRemotes(ents[i], remotes);
      unsigned ncopies = remotes.size();
      int self = PCU_Comm_Self();
      APF_ITERATE(apf::Copies, remotes, it) {
        PCU_COMM_PACK(it->first, it->second);
        PCU_COMM_PACK(it->first, ncopies);
        PCU_COMM_PACK(it->first, self);
        PCU_COMM_PACK(it->first, ents[i]);
        APF_ITERATE(apf::Copies, remotes, it2)
          if (it2->first != it->first) {
            PCU_COMM_PACK(it->first, it2->first);
            PCU_COMM_PACK(it->first, it2->second);
          }
      }
    }
  PCU_Comm_Send();
  while (PCU_Comm_Receive()) {
    apf::MeshEntity* e;
    PCU_COMM_UNPACK(e);
    unsigned ncopies;
    PCU_COMM_UNPACK(ncopies);
    for (unsigned i = 0; i < ncopies; ++i) {
      int p;
      PCU_COMM_UNPACK(p);
      apf::MeshEntity* r;
      PCU_COMM_UNPACK(r);
      am->addRemote(e, p, r);
    }
  }
}

void toAPF(osh_t om, apf::Mesh2* am)
{
  apf::MeshEntity** ents[4];
  ents[0] = verticesToAPF(om, am);
  for (unsigned d = 1; d <= osh_dim(om); ++d)
    ents[d] = entitiesToAPF(om, am, ents[0], d);
  for (unsigned d = 0; d <= osh_dim(om); ++d) {
    ownersToAPF(om, am, ents[d], d);
    apf::initResidence(am, d);
  }
  for (unsigned d = 0; d <= osh_dim(om); ++d)
    delete [] ents[d];
  am->acceptChanges();
  fieldsToAPF(am, om);
}

};
<|endoftext|>


<commit_before>#include "core/hashgen_sprator.h"

#include <vector>

#include "core/image.h"
#include "core/rgb.h"
#include "core/assert.h"
#include "core/rng.h"
#include "core/numeric.h"
#include "core/image_draw.h"
#include "core/cint.h"
#include "core/table_bool.h"
#include "core/generator_cell.h"

namespace
{
    using namespace euphoria::core;

    Rgbi
    BorderColor(Rgbi base)
    {
        auto h = hsl(rgb(base));
        h.h -= Angle::FromDegrees(15);
        h.l *= 0.4;
        return rgbi(rgb(h));
    }

    template
    <
        typename TGenerator,
        typename I
    >
    void
    RenderSpratorImpl
    (
        Image* image,
        I code,
        int half_width = 4,
        int height = 8
    )
    {
        // todo(Gustav): figure out color (randomly?)
        const auto foreground_color = Color::White;
        const auto background_color = Color::Black;
        const auto border_color = BorderColor(foreground_color);

        auto half_side = BoolTable::FromWidthHeight(half_width, height);

        {
            auto generator = TGenerator{code};
            SetWhiteNoise
            (
                &half_side,
                Fourway<BorderSetupRule>{BorderSetupRule::Random},
                [&]() -> bool { return generator.Next() < 0.5f; }
            );
        }

        {
            generator::Rules rules;
            generator::AddComplexRules
            (
                &rules,
                3,
                [](bool current, const Wallcounter& wc) -> std::optional<bool>
                {
                    const auto c = wc.Count
                    (
                        1,
                        false,
                        NeighborhoodAlgorithm::Plus
                    );
                    if(current)
                    {
                        if(c == 2 || c == 3 ) { return true; }
                        else { return false; }
                    }
                    else
                    {
                        if(c <= 1) { return true; }
                        else { return false; }
                    }
                }
            );

            auto cell = generator::CellularAutomata(&rules, &half_side, Fourway<OutsideRule>{OutsideRule::Empty});
            while(cell.HasMoreWork())
            {
                cell.Work();
            }
        }

        // flip
        const auto width = half_width * 2;
        auto res = BoolTable::FromWidthHeight(width+2, height+2, false);

        // copy from small table to big table
        for(int y=0; y<height; y+=1)
        for(int x=0; x<half_width; x+=1)
        {
            res(x+1,y+1) = half_side(x, y);
            res(width-(x+1)+1,y+1) = half_side(x,y);
        }

        Clear(image, background_color);
        auto calculate_scale = [](int image_scale, int table_scale) -> int
        {
            const auto image_scale_float = static_cast<float>(image_scale);
            const auto table_scale_float = static_cast<float>(table_scale);
            const auto scale_factor = image_scale_float / table_scale_float;
            return std::max(1, Floori(scale_factor));
        };

        auto scale = std::min
        (
            calculate_scale(image->GetWidth(), res.GetWidth()),
            calculate_scale(image->GetHeight(), res.GetHeight())
        );
        const auto border = BorderSettings{border_color};
        auto img = Draw(res, foreground_color, background_color, scale, border);
        PasteImage(image, vec2i{0, 0}, img);
    }
}

namespace euphoria::core
{
    void
    RenderSprator(Image* image, int code)
    {
        RenderSpratorImpl<xorshift32>(image, Cbit_signed_to_unsigned(code));
    }
}

<commit_msg>added comments<commit_after>#include "core/hashgen_sprator.h"

#include <vector>

#include "core/image.h"
#include "core/rgb.h"
#include "core/assert.h"
#include "core/rng.h"
#include "core/numeric.h"
#include "core/image_draw.h"
#include "core/cint.h"
#include "core/table_bool.h"
#include "core/generator_cell.h"

namespace
{
    using namespace euphoria::core;

    Rgbi
    BorderColor(Rgbi base)
    {
        auto h = hsl(rgb(base));
        h.h -= Angle::FromDegrees(15);
        h.l *= 0.4;
        return rgbi(rgb(h));
    }

    template
    <
        typename TGenerator,
        typename I
    >
    void
    RenderSpratorImpl
    (
        Image* image,
        I code,
        int half_width = 4,
        int height = 8
    )
    {
        // todo(Gustav): figure out color (randomly?)
        const auto foreground_color = Color::White;
        const auto background_color = Color::Black;
        const auto border_color = BorderColor(foreground_color);
        const int number_of_steps = 1;

        auto half_side = BoolTable::FromWidthHeight(half_width, height);

        // randomize world
        {
            auto generator = TGenerator{code};
            SetWhiteNoise
            (
                &half_side,
                Fourway<BorderSetupRule>{BorderSetupRule::Random},
                [&]() -> bool { return generator.Next() < 0.5f; }
            );
        }

        // apply sprator algorithm
        {
            generator::Rules rules;
            generator::AddComplexRules
            (
                &rules,
                number_of_steps,
                [](bool current, const Wallcounter& wc) -> std::optional<bool>
                {
                    const auto c = wc.Count
                    (
                        1,
                        false,
                        NeighborhoodAlgorithm::Plus
                    );
                    if(current)
                    {
                        if(c == 2 || c == 3 ) { return true; }
                        else { return false; }
                    }
                    else
                    {
                        if(c <= 1) { return true; }
                        else { return false; }
                    }
                }
            );

            auto cell = generator::CellularAutomata
            (
                &rules,
                &half_side,
                Fourway<OutsideRule>{OutsideRule::Empty}
            );

            while(cell.HasMoreWork()) { cell.Work(); }
        }

        // flip and copy from small table to big table
        const auto width = half_width * 2;
        auto result_table = BoolTable::FromWidthHeight(width+2, height+2, false);

        for(int y=0; y<height; y+=1)
        for(int x=0; x<half_width; x+=1)
        {
            result_table(x+1,y+1) = half_side(x, y);
            result_table(width-(x+1)+1,y+1) = half_side(x,y);
        }

        // draw image with border
        Clear(image, background_color);
        auto calculate_scale = [](int image_scale, int table_scale) -> int
        {
            const auto image_scale_float = static_cast<float>(image_scale);
            const auto table_scale_float = static_cast<float>(table_scale);
            const auto scale_factor = image_scale_float / table_scale_float;
            return std::max(1, Floori(scale_factor));
        };

        auto scale = std::min
        (
            calculate_scale(image->GetWidth(), result_table.GetWidth()),
            calculate_scale(image->GetHeight(), result_table.GetHeight())
        );
        const auto border = BorderSettings{border_color};
        auto img = Draw
        (
            result_table,
            foreground_color,
            background_color,
            scale,
            border
        );
        PasteImage(image, vec2i{0, 0}, img);
    }
}

namespace euphoria::core
{
    void
    RenderSprator(Image* image, int code)
    {
        RenderSpratorImpl<xorshift32>(image, Cbit_signed_to_unsigned(code));
    }
}

<|endoftext|>