<commit_before>/*
 * Copyright 2007-2018 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include "SocketFilter.hxx"
#include "Ptr.hxx"
#include "event/net/BufferedSocket.hxx"
#include "util/BindMethod.hxx"

/**
 * A wrapper for #BufferedSocket that can filter input and output.
 *
 * Unlike #BufferedSocket, this class "owns" the socket and closes it
 * automatically in its destructor.
 */
class FilteredSocket final : BufferedSocketHandler {
	BufferedSocket base;

#ifndef NDEBUG
	bool ended;
#endif

	/**
	 * The actual filter.  If this is nullptr, then this object behaves
	 * just like #BufferedSocket.
	 */
	SocketFilterPtr filter;

	BufferedSocketHandler *handler;

	/**
	 * Is there still data in the filter's output?  Once this turns
	 * from "false" to "true", the #BufferedSocket_handler method
	 * drained() will be invoked.
	 */
	bool drained;

public:
	explicit FilteredSocket(EventLoop &_event_loop) noexcept
		:base(_event_loop) {}

	~FilteredSocket() noexcept;

	EventLoop &GetEventLoop() noexcept {
		return base.GetEventLoop();
	}

	void Init(SocketDescriptor fd, FdType fd_type,
		  Event::Duration read_timeout,
		  Event::Duration write_timeout,
		  SocketFilterPtr filter,
		  BufferedSocketHandler &handler) noexcept;

	/**
	 * Initialize a "dummy" instance (without a filter) which cannot
	 * be used to schedule events (because there is no handler); the
	 * next Reinit() call finishes initialization.
	 */
	void InitDummy(SocketDescriptor _fd, FdType _fd_type,
		       SocketFilterPtr _filter={}) noexcept;

	void Reinit(Event::Duration read_timeout,
		    Event::Duration write_timeout,
		    BufferedSocketHandler &handler) noexcept;

	bool HasFilter() const noexcept {
		return filter != nullptr;
	}

	FdType GetType() const noexcept {
		return filter == nullptr
			? base.GetType()
			/* can't do splice() with a filter */
			: FdType::FD_NONE;
	}

	/**
	 * Install a callback that will be invoked as soon as the filter's
	 * protocol "handshake" is complete.  Before this time, no data
	 * transfer is possible.  If the handshake is already complete (or
	 * the filter has no handshake), the callback will be invoked
	 * synchronously by this method.
	 */
	void SetHandshakeCallback(BoundMethod<void() noexcept> callback) noexcept {
		if (filter != nullptr)
			filter->SetHandshakeCallback(callback);
		else
			callback();
	}

	void Shutdown() noexcept {
		base.Shutdown();
	}

	/**
	 * Close the physical socket, but do not destroy the input buffer.  To
	 * do the latter, call filtered_socket_destroy().
	 */
	void Close() noexcept {
		if (filter != nullptr)
			filter->OnClosed();

#ifndef NDEBUG
		/* work around bogus assertion failure */
		if (filter != nullptr && base.HasEnded())
			return;
#endif

		base.Close();
	}

	/**
	 * Just like Close(), but do not actually close the
	 * socket.  The caller is responsible for closing the socket (or
	 * scheduling it for reuse).
	 */
	void Abandon() noexcept {
		if (filter != nullptr)
			filter->OnClosed();

#ifndef NDEBUG
		/* work around bogus assertion failure */
		if (filter != nullptr && base.HasEnded())
			return;
#endif

		base.Abandon();
	}

	bool ClosedByPeer() noexcept {
		return base.ClosedByPeer();
	}

#ifndef NDEBUG
	bool HasEnded() const noexcept {
		return ended;
	}
#endif

	/**
	 * Destroy the object.  Prior to that, the socket must be removed
	 * by calling either filtered_socket_close() or
	 * filtered_socket_abandon().
	 */
	void Destroy() noexcept;

	/**
	 * Returns the socket descriptor and calls Abandon().  Returns -1
	 * if the input buffer is not empty.
	 */
	int AsFD() noexcept {
		return filter != nullptr
			? -1
			: base.AsFD();
	}

	/**
	 * Is the socket still connected?  This does not actually check
	 * whether the socket is connected, just whether it is known to be
	 * closed.
	 */
	bool IsConnected() const noexcept {
#ifndef NDEBUG
		/* work around bogus assertion failure */
		if (filter != nullptr && base.HasEnded())
			return false;
#endif

		return base.IsConnected();
	}

	/**
	 * Is the object still usable?  The socket may be closed already, but
	 * the input buffer may still have data.
	 */
	bool IsValid() const noexcept {
		return base.IsValid();
	}

	/**
	 * Accessor for #drained.
	 */
	bool IsDrained() const noexcept {
		assert(IsValid());

		return drained;
	}

	/**
	 * Is the input buffer empty?
	 */
	gcc_pure
	bool IsEmpty() const noexcept;

	/**
	 * Is the input buffer full?
	 */
	gcc_pure
	bool IsFull() const noexcept;

	/**
	 * Returns the number of bytes in the input buffer.
	 */
	gcc_pure
	size_t GetAvailable() const noexcept;

	WritableBuffer<void> ReadBuffer() const noexcept;

	/**
	 * Dispose the specified number of bytes from the input buffer.
	 * Call this after ReadBuffer().  It may be called repeatedly.
	 */
	void DisposeConsumed(size_t nbytes) noexcept;

	void SetDirect(bool _direct) noexcept {
		assert(!_direct || !HasFilter());

		base.SetDirect(_direct);
	}

	/**
	 * The caller wants to read more data from the socket.  There are
	 * four possible outcomes: a call to filtered_socket_handler.read,
	 * a call to filtered_socket_handler.direct, a call to
	 * filtered_socket_handler.error or (if there is no data available
	 * yet) an event gets scheduled and the function returns
	 * immediately.
	 */
	bool Read(bool expect_more) noexcept;

	ssize_t Write(const void *data, size_t length) noexcept;

	ssize_t WriteV(const struct iovec *v, size_t n) noexcept {
		assert(filter == nullptr);

		return base.WriteV(v, n);
	}

	ssize_t WriteFrom(int fd, FdType fd_type, size_t length) noexcept {
		assert(filter == nullptr);

		return base.WriteFrom(fd, fd_type, length);
	}

	gcc_pure
	bool IsReadyForWriting() const noexcept {
		assert(filter == nullptr);

		return base.IsReadyForWriting();
	}

	void ScheduleReadTimeout(bool expect_more,
				 Event::Duration timeout) noexcept {
		if (filter != nullptr)
			filter->ScheduleRead(expect_more, timeout);
		else
			base.ScheduleReadTimeout(expect_more, timeout);
	}

	/**
	 * Schedules reading on the socket with timeout disabled, to indicate
	 * that you are willing to read, but do not expect it yet.  No direct
	 * action is taken.  Use this to enable reading when you are still
	 * sending the request.  When you are finished sending the request,
	 * you should call filtered_socket_read() to enable the read timeout.
	 */
	void ScheduleReadNoTimeout(bool expect_more) noexcept {
		ScheduleReadTimeout(expect_more, Event::Duration(-1));
	}

	void ScheduleWrite() noexcept {
		if (filter != nullptr)
			filter->ScheduleWrite();
		else
			base.ScheduleWrite();
	}

	void UnscheduleWrite() noexcept {
		if (filter != nullptr)
			filter->UnscheduleWrite();
		else
			base.UnscheduleWrite();
	}

	gcc_pure
	bool InternalIsEmpty() const noexcept {
		assert(filter != nullptr);

		return base.IsEmpty();
	}

	gcc_pure
	bool InternalIsFull() const noexcept {
		assert(filter != nullptr);

		return base.IsFull();
	}

	gcc_pure
	size_t InternalGetAvailable() const noexcept {
		assert(filter != nullptr);

		return base.GetAvailable();
	}

	WritableBuffer<void> InternalReadBuffer() const noexcept {
		assert(filter != nullptr);

		return base.ReadBuffer();
	}

	void InternalConsumed(size_t nbytes) noexcept {
		assert(filter != nullptr);

		base.DisposeConsumed(nbytes);
	}

	bool InternalRead(bool expect_more) noexcept {
		assert(filter != nullptr);

#ifndef NDEBUG
		if (!base.IsConnected() && base.GetAvailable() == 0)
			/* work around assertion failure in
			   BufferedSocket::TryRead2() */
			return false;
#endif

		return base.Read(expect_more);
	}

	ssize_t InternalDirectWrite(const void *data, size_t length) noexcept {
		assert(filter != nullptr);

		return base.DirectWrite(data, length);
	}

	ssize_t InternalWrite(const void *data, size_t length) noexcept {
		assert(filter != nullptr);

		return base.Write(data, length);
	}

	/**
	 * A #SocketFilter must call this function whenever it adds data to
	 * its output buffer (only if it implements such a buffer).
	 */
	void InternalUndrained() noexcept {
		assert(filter != nullptr);
		assert(IsConnected());

		drained = false;
	}

	/**
	 * A #SocketFilter must call this function whenever its output buffer
	 * drains (only if it implements such a buffer).
	 */
	bool InternalDrained() noexcept;

	void InternalScheduleRead(bool expect_more,
				  Event::Duration timeout) noexcept {
		assert(filter != nullptr);

		base.ScheduleReadTimeout(expect_more, timeout);
	}

	void InternalScheduleWrite() noexcept {
		assert(filter != nullptr);

		base.ScheduleWrite();
	}

	void InternalUnscheduleWrite() noexcept {
		assert(filter != nullptr);

		base.UnscheduleWrite();
	}

	BufferedResult InvokeData() noexcept {
		assert(filter != nullptr);

		try {
			return handler->OnBufferedData();
		} catch (...) {
			handler->OnBufferedError(std::current_exception());
			return BufferedResult::CLOSED;
		}
	}

	bool InvokeClosed() noexcept {
		assert(filter != nullptr);

		return handler->OnBufferedClosed();
	}

	bool InvokeRemaining(size_t remaining) noexcept {
		assert(filter != nullptr);

		return handler->OnBufferedRemaining(remaining);
	}

	void InvokeEnd() noexcept {
		assert(filter != nullptr);
		assert(!ended);
		assert(base.HasEnded());

#ifndef NDEBUG
		ended = true;
#endif

		handler->OnBufferedEnd();
	}

	bool InvokeWrite() noexcept {
		assert(filter != nullptr);

		try {
			return handler->OnBufferedWrite();
		} catch (...) {
			handler->OnBufferedError(std::current_exception());
			return false;
		}
	}

	bool InvokeTimeout() noexcept;

	void InvokeError(std::exception_ptr e) noexcept {
		assert(filter != nullptr);

		handler->OnBufferedError(std::move(e));
	}

private:
	/* virtual methods from class BufferedSocketHandler */
	BufferedResult OnBufferedData() override;
	bool OnBufferedClosed() noexcept override;
	bool OnBufferedRemaining(size_t remaining) noexcept override;
	bool OnBufferedEnd() noexcept override;
	bool OnBufferedWrite() override;
	bool OnBufferedTimeout() noexcept override;
	enum write_result OnBufferedBroken() noexcept override;
	void OnBufferedError(std::exception_ptr e) noexcept override;
};
<commit_msg>fs/FilteredSocket: add method GetFilter()<commit_after>/*
 * Copyright 2007-2018 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include "SocketFilter.hxx"
#include "Ptr.hxx"
#include "event/net/BufferedSocket.hxx"
#include "util/BindMethod.hxx"

/**
 * A wrapper for #BufferedSocket that can filter input and output.
 *
 * Unlike #BufferedSocket, this class "owns" the socket and closes it
 * automatically in its destructor.
 */
class FilteredSocket final : BufferedSocketHandler {
	BufferedSocket base;

#ifndef NDEBUG
	bool ended;
#endif

	/**
	 * The actual filter.  If this is nullptr, then this object behaves
	 * just like #BufferedSocket.
	 */
	SocketFilterPtr filter;

	BufferedSocketHandler *handler;

	/**
	 * Is there still data in the filter's output?  Once this turns
	 * from "false" to "true", the #BufferedSocket_handler method
	 * drained() will be invoked.
	 */
	bool drained;

public:
	explicit FilteredSocket(EventLoop &_event_loop) noexcept
		:base(_event_loop) {}

	~FilteredSocket() noexcept;

	EventLoop &GetEventLoop() noexcept {
		return base.GetEventLoop();
	}

	void Init(SocketDescriptor fd, FdType fd_type,
		  Event::Duration read_timeout,
		  Event::Duration write_timeout,
		  SocketFilterPtr filter,
		  BufferedSocketHandler &handler) noexcept;

	/**
	 * Initialize a "dummy" instance (without a filter) which cannot
	 * be used to schedule events (because there is no handler); the
	 * next Reinit() call finishes initialization.
	 */
	void InitDummy(SocketDescriptor _fd, FdType _fd_type,
		       SocketFilterPtr _filter={}) noexcept;

	void Reinit(Event::Duration read_timeout,
		    Event::Duration write_timeout,
		    BufferedSocketHandler &handler) noexcept;

	bool HasFilter() const noexcept {
		return filter != nullptr;
	}

	const SocketFilter *GetFilter() const noexcept {
		return filter.get();
	}

	FdType GetType() const noexcept {
		return filter == nullptr
			? base.GetType()
			/* can't do splice() with a filter */
			: FdType::FD_NONE;
	}

	/**
	 * Install a callback that will be invoked as soon as the filter's
	 * protocol "handshake" is complete.  Before this time, no data
	 * transfer is possible.  If the handshake is already complete (or
	 * the filter has no handshake), the callback will be invoked
	 * synchronously by this method.
	 */
	void SetHandshakeCallback(BoundMethod<void() noexcept> callback) noexcept {
		if (filter != nullptr)
			filter->SetHandshakeCallback(callback);
		else
			callback();
	}

	void Shutdown() noexcept {
		base.Shutdown();
	}

	/**
	 * Close the physical socket, but do not destroy the input buffer.  To
	 * do the latter, call filtered_socket_destroy().
	 */
	void Close() noexcept {
		if (filter != nullptr)
			filter->OnClosed();

#ifndef NDEBUG
		/* work around bogus assertion failure */
		if (filter != nullptr && base.HasEnded())
			return;
#endif

		base.Close();
	}

	/**
	 * Just like Close(), but do not actually close the
	 * socket.  The caller is responsible for closing the socket (or
	 * scheduling it for reuse).
	 */
	void Abandon() noexcept {
		if (filter != nullptr)
			filter->OnClosed();

#ifndef NDEBUG
		/* work around bogus assertion failure */
		if (filter != nullptr && base.HasEnded())
			return;
#endif

		base.Abandon();
	}

	bool ClosedByPeer() noexcept {
		return base.ClosedByPeer();
	}

#ifndef NDEBUG
	bool HasEnded() const noexcept {
		return ended;
	}
#endif

	/**
	 * Destroy the object.  Prior to that, the socket must be removed
	 * by calling either filtered_socket_close() or
	 * filtered_socket_abandon().
	 */
	void Destroy() noexcept;

	/**
	 * Returns the socket descriptor and calls Abandon().  Returns -1
	 * if the input buffer is not empty.
	 */
	int AsFD() noexcept {
		return filter != nullptr
			? -1
			: base.AsFD();
	}

	/**
	 * Is the socket still connected?  This does not actually check
	 * whether the socket is connected, just whether it is known to be
	 * closed.
	 */
	bool IsConnected() const noexcept {
#ifndef NDEBUG
		/* work around bogus assertion failure */
		if (filter != nullptr && base.HasEnded())
			return false;
#endif

		return base.IsConnected();
	}

	/**
	 * Is the object still usable?  The socket may be closed already, but
	 * the input buffer may still have data.
	 */
	bool IsValid() const noexcept {
		return base.IsValid();
	}

	/**
	 * Accessor for #drained.
	 */
	bool IsDrained() const noexcept {
		assert(IsValid());

		return drained;
	}

	/**
	 * Is the input buffer empty?
	 */
	gcc_pure
	bool IsEmpty() const noexcept;

	/**
	 * Is the input buffer full?
	 */
	gcc_pure
	bool IsFull() const noexcept;

	/**
	 * Returns the number of bytes in the input buffer.
	 */
	gcc_pure
	size_t GetAvailable() const noexcept;

	WritableBuffer<void> ReadBuffer() const noexcept;

	/**
	 * Dispose the specified number of bytes from the input buffer.
	 * Call this after ReadBuffer().  It may be called repeatedly.
	 */
	void DisposeConsumed(size_t nbytes) noexcept;

	void SetDirect(bool _direct) noexcept {
		assert(!_direct || !HasFilter());

		base.SetDirect(_direct);
	}

	/**
	 * The caller wants to read more data from the socket.  There are
	 * four possible outcomes: a call to filtered_socket_handler.read,
	 * a call to filtered_socket_handler.direct, a call to
	 * filtered_socket_handler.error or (if there is no data available
	 * yet) an event gets scheduled and the function returns
	 * immediately.
	 */
	bool Read(bool expect_more) noexcept;

	ssize_t Write(const void *data, size_t length) noexcept;

	ssize_t WriteV(const struct iovec *v, size_t n) noexcept {
		assert(filter == nullptr);

		return base.WriteV(v, n);
	}

	ssize_t WriteFrom(int fd, FdType fd_type, size_t length) noexcept {
		assert(filter == nullptr);

		return base.WriteFrom(fd, fd_type, length);
	}

	gcc_pure
	bool IsReadyForWriting() const noexcept {
		assert(filter == nullptr);

		return base.IsReadyForWriting();
	}

	void ScheduleReadTimeout(bool expect_more,
				 Event::Duration timeout) noexcept {
		if (filter != nullptr)
			filter->ScheduleRead(expect_more, timeout);
		else
			base.ScheduleReadTimeout(expect_more, timeout);
	}

	/**
	 * Schedules reading on the socket with timeout disabled, to indicate
	 * that you are willing to read, but do not expect it yet.  No direct
	 * action is taken.  Use this to enable reading when you are still
	 * sending the request.  When you are finished sending the request,
	 * you should call filtered_socket_read() to enable the read timeout.
	 */
	void ScheduleReadNoTimeout(bool expect_more) noexcept {
		ScheduleReadTimeout(expect_more, Event::Duration(-1));
	}

	void ScheduleWrite() noexcept {
		if (filter != nullptr)
			filter->ScheduleWrite();
		else
			base.ScheduleWrite();
	}

	void UnscheduleWrite() noexcept {
		if (filter != nullptr)
			filter->UnscheduleWrite();
		else
			base.UnscheduleWrite();
	}

	gcc_pure
	bool InternalIsEmpty() const noexcept {
		assert(filter != nullptr);

		return base.IsEmpty();
	}

	gcc_pure
	bool InternalIsFull() const noexcept {
		assert(filter != nullptr);

		return base.IsFull();
	}

	gcc_pure
	size_t InternalGetAvailable() const noexcept {
		assert(filter != nullptr);

		return base.GetAvailable();
	}

	WritableBuffer<void> InternalReadBuffer() const noexcept {
		assert(filter != nullptr);

		return base.ReadBuffer();
	}

	void InternalConsumed(size_t nbytes) noexcept {
		assert(filter != nullptr);

		base.DisposeConsumed(nbytes);
	}

	bool InternalRead(bool expect_more) noexcept {
		assert(filter != nullptr);

#ifndef NDEBUG
		if (!base.IsConnected() && base.GetAvailable() == 0)
			/* work around assertion failure in
			   BufferedSocket::TryRead2() */
			return false;
#endif

		return base.Read(expect_more);
	}

	ssize_t InternalDirectWrite(const void *data, size_t length) noexcept {
		assert(filter != nullptr);

		return base.DirectWrite(data, length);
	}

	ssize_t InternalWrite(const void *data, size_t length) noexcept {
		assert(filter != nullptr);

		return base.Write(data, length);
	}

	/**
	 * A #SocketFilter must call this function whenever it adds data to
	 * its output buffer (only if it implements such a buffer).
	 */
	void InternalUndrained() noexcept {
		assert(filter != nullptr);
		assert(IsConnected());

		drained = false;
	}

	/**
	 * A #SocketFilter must call this function whenever its output buffer
	 * drains (only if it implements such a buffer).
	 */
	bool InternalDrained() noexcept;

	void InternalScheduleRead(bool expect_more,
				  Event::Duration timeout) noexcept {
		assert(filter != nullptr);

		base.ScheduleReadTimeout(expect_more, timeout);
	}

	void InternalScheduleWrite() noexcept {
		assert(filter != nullptr);

		base.ScheduleWrite();
	}

	void InternalUnscheduleWrite() noexcept {
		assert(filter != nullptr);

		base.UnscheduleWrite();
	}

	BufferedResult InvokeData() noexcept {
		assert(filter != nullptr);

		try {
			return handler->OnBufferedData();
		} catch (...) {
			handler->OnBufferedError(std::current_exception());
			return BufferedResult::CLOSED;
		}
	}

	bool InvokeClosed() noexcept {
		assert(filter != nullptr);

		return handler->OnBufferedClosed();
	}

	bool InvokeRemaining(size_t remaining) noexcept {
		assert(filter != nullptr);

		return handler->OnBufferedRemaining(remaining);
	}

	void InvokeEnd() noexcept {
		assert(filter != nullptr);
		assert(!ended);
		assert(base.HasEnded());

#ifndef NDEBUG
		ended = true;
#endif

		handler->OnBufferedEnd();
	}

	bool InvokeWrite() noexcept {
		assert(filter != nullptr);

		try {
			return handler->OnBufferedWrite();
		} catch (...) {
			handler->OnBufferedError(std::current_exception());
			return false;
		}
	}

	bool InvokeTimeout() noexcept;

	void InvokeError(std::exception_ptr e) noexcept {
		assert(filter != nullptr);

		handler->OnBufferedError(std::move(e));
	}

private:
	/* virtual methods from class BufferedSocketHandler */
	BufferedResult OnBufferedData() override;
	bool OnBufferedClosed() noexcept override;
	bool OnBufferedRemaining(size_t remaining) noexcept override;
	bool OnBufferedEnd() noexcept override;
	bool OnBufferedWrite() override;
	bool OnBufferedTimeout() noexcept override;
	enum write_result OnBufferedBroken() noexcept override;
	void OnBufferedError(std::exception_ptr e) noexcept override;
};
<|endoftext|>


<commit_before>#include <game-objects/Rope.h>
#include <components/PhysicsComponent.h>
#include <components/Texcoords.h>
#include <components/SplitDirectionComponent.h>
#include <iostream>
#include <game-objects/GameObjectTag.h>

class Rope::RopeImpl{

public:
	RopeImpl(){}
	~RopeImpl(){}

	b2RopeJointDef m_ropeDef;

	b2Body* createGround(b2Vec2 position,  b2World& box2dWorld){
		b2BodyDef bd;
		bd.type = b2_staticBody;
		bd.position = position;
		b2PolygonShape shape;
		shape.SetAsBox(0.1f, 0.1f);
		b2FixtureDef fd;
		fd.shape = &shape;
		b2Body* ground  = box2dWorld.CreateBody(&bd);
		ground->CreateFixture(&fd);
		return ground;
	}

	b2FixtureDef getRopeFixture(b2PolygonShape& shape){
		b2FixtureDef fd;
		fd.shape = &shape;
		fd.density = 20.0f;
		fd.friction = 0.2f;
		fd.filter.categoryBits = 0x0001;
		fd.filter.maskBits = 0xff - 0x0003;
		return fd;
	}

	void createEntity(tmx::MapObject mapObject, b2World& box2dWorld, anax::World& anaxWorld) {
		b2Vec2 statingPosition = tmx::SfToBoxVec(mapObject.GetPosition());
		b2Vec2 endPosition = tmx::SfToBoxVec(sf::Vector2f(mapObject.GetPosition().x + mapObject.GetAABB().width, mapObject.GetPosition().y + mapObject.GetAABB().height));

		b2Body* ground = createGround(statingPosition, box2dWorld);
		b2Body* prevBody = ground;
		{
			b2PolygonShape shape;
			shape.SetAsBox(0.5f, 0.125f);

			b2FixtureDef fd  = getRopeFixture(shape);

			b2RevoluteJointDef jd;
			jd.collideConnected = false;

//			const int32 N = statingPosition.x + 5;
			const int32 N = (mapObject.GetAABB().width / 30) / (1.0f ) + 1;

			const float32 y = statingPosition.y;
//			m_ropeDef.localAnchorA.Set(0.0f, x);

			std::cout << "n" << N << std::endl;
			for (int32 xVal = (statingPosition.x); xVal < N; ++xVal) {
				b2BodyDef bd;
				bd.type = b2_dynamicBody;
				bd.position.Set(xVal + 2, y );
				if (xVal == N - 1) {
					bd.type = b2_staticBody;
					shape.SetAsBox(0.1f, 0.1f);
					fd.density = 1.0f;
					bd.position.Set(endPosition.x, endPosition.y);
				}

				b2Body* body = box2dWorld.CreateBody(&bd);

				body->CreateFixture(&fd);

				auto objectEntity = anaxWorld.createEntity();
				auto& texCoordsComp = objectEntity.addComponent<Texcoords>();
				auto& physComp = objectEntity.addComponent<PhysicsComponent>();
				auto& splitDirectionComp = objectEntity.addComponent<SplitDirectionComponent>();

				b2Vec2 anchor(xVal + 2 , float32(y) );
				jd.Initialize(prevBody, body, anchor);
				box2dWorld.CreateJoint(&jd);

				physComp.physicsBody = body;

				objectEntity.activate();
				prevBody = body;
			}

		//	m_ropeDef.localAnchorB.SetZero();

			float32 extraLength = 0.01f;
			//m_ropeDef.maxLength = N - 1.0f + extraLength;
		//	m_ropeDef.bodyB = prevBody;
		}

		{
		//	m_ropeDef.bodyA = ground;
		//	box2dWorld.CreateJoint(&m_ropeDef);
		}
	}

};

Rope::Rope() : m_impl(new RopeImpl()) {

}

Rope::~Rope() {
}

void Rope::createEntity(const tmx::MapObject mapObject, b2World& box2dWorld, anax::World& anaxWorld) {
	m_impl->createEntity(mapObject, box2dWorld, anaxWorld);
}
<commit_msg>Refactor rope cod<commit_after>#include <game-objects/Rope.h>
#include <components/PhysicsComponent.h>
#include <components/Texcoords.h>
#include <components/SplitDirectionComponent.h>
#include <iostream>
#include <game-objects/GameObjectTag.h>
#include <math.h>
class Rope::RopeImpl{

public:
	RopeImpl(){}
	~RopeImpl(){}

	b2RopeJointDef m_ropeDef;

	b2Body* createGround(b2Vec2 position,  b2World& box2dWorld){
		b2BodyDef bd;
		bd.type = b2_staticBody;
		bd.position = position;
		b2PolygonShape shape;
		shape.SetAsBox(0.1f, 0.1f);
		b2FixtureDef fd;
		fd.shape = &shape;
		b2Body* ground  = box2dWorld.CreateBody(&bd);
		ground->CreateFixture(&fd);
		return ground;
	}

	b2FixtureDef getRopeFixture(b2PolygonShape& shape){
		b2FixtureDef fd;
		fd.shape = &shape;
		fd.density = 20.0f;
		fd.friction = 0.2f;
		return fd;
	}

	void createEntity(tmx::MapObject mapObject, b2World& box2dWorld, anax::World& anaxWorld) {
		b2Vec2 statingPosition = tmx::SfToBoxVec(mapObject.GetPosition());
		b2Vec2 endPosition = tmx::SfToBoxVec(sf::Vector2f(mapObject.GetPosition().x + mapObject.GetAABB().width, mapObject.GetPosition().y));

		b2Body* ground = createGround(statingPosition, box2dWorld);
		b2Body* prevBody = ground;
		{
			b2PolygonShape shape;
			shape.SetAsBox(0.5f, 0.125f);

			b2FixtureDef fd  = getRopeFixture(shape);

			b2RevoluteJointDef jd;
			jd.collideConnected = false;

			const int32 N = ceilf((mapObject.GetAABB().width / 30) / (1.0f )) + 1;

			for (int32 xVal = 1; xVal < N; ++xVal) {
				b2BodyDef bd;
				bd.type = b2_dynamicBody;
				bd.position.Set(statingPosition.x + xVal , statingPosition.y );
				if (xVal == N - 1) {
					bd.type = b2_staticBody;
					shape.SetAsBox(0.1f, 0.1f);
					fd.density = 1.0f;
					bd.position.Set(endPosition.x, endPosition.y);
				}

				b2Body* body = box2dWorld.CreateBody(&bd);

				body->CreateFixture(&fd);

				auto objectEntity = anaxWorld.createEntity();
				auto& texCoordsComp = objectEntity.addComponent<Texcoords>();
				auto& physComp = objectEntity.addComponent<PhysicsComponent>();
				auto& splitDirectionComp = objectEntity.addComponent<SplitDirectionComponent>();

				b2Vec2 anchor( statingPosition.x + xVal  , statingPosition.y );
				jd.Initialize(prevBody, body, anchor);
				box2dWorld.CreateJoint(&jd);

				physComp.physicsBody = body;

				objectEntity.activate();
				prevBody = body;
			}

		}

	}

};

Rope::Rope() : m_impl(new RopeImpl()) {

}

Rope::~Rope() {
}

void Rope::createEntity(const tmx::MapObject mapObject, b2World& box2dWorld, anax::World& anaxWorld) {
	m_impl->createEntity(mapObject, box2dWorld, anaxWorld);
}
<|endoftext|>


<commit_before>//----------------------------------*-C++-*----------------------------------//
/*!
 * \file   core/test/tstInterfaces.cpp
 * \author Stuart Slattery
 * \date   Thu Dec 01 16:50:04 2011
 * \brief  Unit tests for the data transfer pure virtual interfaces.
 */
//---------------------------------------------------------------------------//

#include <iostream>
#include <vector>
#include <cmath>
#include <sstream>

#include <Coupler_Point.hpp>
#include <Coupler_DataSource.hpp>
#include <Coupler_DataTarget.hpp>

#include "Teuchos_ArrayView.hpp"
#include "Teuchos_RCP.hpp"
#include "Teuchos_UnitTestHarness.hpp"
#include "Teuchos_DefaultComm.hpp"

//---------------------------------------------------------------------------//
// HELPER FUNCTIONS
//---------------------------------------------------------------------------//

template<class Ordinal>
Teuchos::RCP<const Teuchos::Comm<Ordinal> > getDefaultComm()
{
#ifdef HAVE_MPI
    return Teuchos::DefaultComm<Ordinal>::getComm();
#else
    return Teuchos::rcp(new Teuchos::SerialComm<Ordinal>() );
#endif
}

//---------------------------------------------------------------------------//
// DATA CLASS
//---------------------------------------------------------------------------//

class Data_Container
{
  public:

    typedef Coupler::Point<int,double>     PointType;

  private:

    double scalar_data;
    std::vector<double> distributed_data;
    std::vector<PointType> distributed_points;

  public:

    Data_Container()
      : scalar_data(0.0)
    { /* ... */ }

    ~Data_Container()
    { /* ... */ }

    void set_distributed_points(std::vector<PointType> points)
    {
	distributed_points = points;
    }

    Teuchos::ArrayView<PointType> get_distributed_points()
    {
	return distributed_points;
    }

    Teuchos::ArrayView<double> get_distributed_data()
    {
	distributed_data.resize(1);
	return Teuchos::ArrayView<double>(distributed_data);
    }

    void set_scalar_data(double data)
    {
	scalar_data = data;
    }

    double get_scalar_data()
    {
	return scalar_data;
    }
};

//---------------------------------------------------------------------------//
// INTERFACE IMPLEMENTATIONS
//---------------------------------------------------------------------------//

namespace Coupler {

// transfer data source implementation - this implementation specifies double
// as the data type
template<class DataType_T, class HandleType_T, class CoordinateType_T>
class test_DataSource
    : public DataSource<DataType_T, HandleType_T, CoordinateType_T>
{
  private:

    std::vector<double> private_data;

  public:

    typedef double                                   DataType;
    typedef int                                      HandleType;
    typedef double                                   CoordinateType;
    typedef int                                      OrdinalType;
    typedef Point<HandleType,CoordinateType>         PointType;
    typedef Teuchos::Comm<OrdinalType>               Communicator_t;
    typedef Teuchos::RCP<const Communicator_t>       RCP_Communicator;

    test_DataSource()
    { /* ... */ }

    ~test_DataSource()
    { /* ... */ }

    RCP_Communicator get_source_comm()
    {
	return getDefaultComm<OrdinalType>();
    }

    bool is_field_supported(const std::string &field_name)
    {
	bool return_val = false;

	if (field_name == "DISTRIBUTED_TEST_FIELD")
	{
	    return_val = true;
	}

	else if (field_name == "SCALAR_TEST_FIELD")
	{
	    return_val = true;
	}

	return return_val;
    }

    bool is_local_point(const PointType &point)
    {
	bool return_val = false;

	if ( point.x() > 0 && point.y() > 0 && point.z() > 0 )
	{
	    return_val = true;
	}

	return return_val;
    }

    const Teuchos::ArrayView<double>
    get_source_data(const std::string &field_name)
    {
	Teuchos::ArrayView<double> return_view;

	if ( field_name == "DISTRIBUTED_TEST_FIELD" )
	{
	    private_data.clear();
	    private_data.resize(1);
	    private_data[0] = 1.0*getDefaultComm<OrdinalType>()->getRank();
	    Teuchos::ArrayView<double> private_view(private_data);
	    return_view =  private_view;
	}

	return return_view;
    }

    double get_global_source_data(const std::string &field_name)
    {
	double return_val = 0.0;

	if ( field_name == "SCALAR_TEST_FIELD" )
	{
	    return_val = 1.0*getDefaultComm<OrdinalType>()->getRank();
	}

	return return_val;
    }
};

//---------------------------------------------------------------------------//
// transfer data target implementation - this implementation specifies double
// as the data type
template<class DataType_T, class HandleType_T, class CoordinateType_T>
class test_DataTarget
    : public DataTarget<DataType_T, HandleType_T, CoordinateType_T>
{
  public:

    typedef double                                   DataType;
    typedef int                                      HandleType;
    typedef double                                   CoordinateType;
    typedef int                                      OrdinalType;
    typedef Point<HandleType,CoordinateType>         PointType;
    typedef Teuchos::Comm<OrdinalType>               Communicator_t;
    typedef Teuchos::RCP<const Communicator_t>       RCP_Communicator;

  private:

    Teuchos::RCP<Data_Container> container;
    std::vector<PointType> points;

  public:

    test_DataTarget(Teuchos::RCP<Data_Container> _container)
	: container(_container)
    { /* ... */ }

    ~test_DataTarget()
    { /* ... */ }

    RCP_Communicator get_target_comm()
    {
	return getDefaultComm<OrdinalType>();
    }

    bool is_field_supported(const std::string &field_name)
    {
	bool return_val = false;

	if (field_name == "DISTRIBUTED_TEST_FIELD")
	{
	    return_val = true;
	}

	else if (field_name == "SCALAR_TEST_FIELD")
	{
	    return_val = true;
	}

	return return_val;
    }

    const Teuchos::ArrayView<PointType>
    get_target_points(const std::string &field_name)
    {
	Teuchos::ArrayView<PointType> return_view;

	if ( field_name == "DISTRIBUTED_TEST_FIELD" )
	{
	    PointType local_point(1, 1.0, 1.0, 1.0);
	    std::vector<PointType> local_points(1, local_point);
	    points = local_points;
	    return_view = Teuchos::ArrayView<PointType>(points);
	}

	return return_view;
    }

    Teuchos::ArrayView<DataType>
    get_target_data_space(const std::string &field_name)
    {
	Teuchos::ArrayView<DataType> return_view;

	if ( field_name == "DISTRIBUTED_TEST_FIELD" )
	{
	    return_view = container->get_distributed_data();
	}

	return return_view;
    }

    void set_global_target_data(const std::string &field_name,
				const DataType &data)
    {
	if ( field_name == "SCALAR_TEST_FIELD" )
	{
	    container->set_scalar_data(data);
	}
    }
};

} // end namespace Coupler

//---------------------------------------------------------------------------//
// TESTS
//---------------------------------------------------------------------------//

namespace Coupler {

TEUCHOS_UNIT_TEST( Transfer_DataSource, source_interface_test )
{
    typedef Point<int,double>     PointType;

    // create an instance of the source interface.
    Teuchos::RCP<DataSource<double,int,double> > source_iface =
	Teuchos::rcp(new test_DataSource<double,int,double>);

    // test the interface methods.
    TEST_ASSERT( source_iface->get_source_comm()->getRank()
		 == getDefaultComm<int>()->getRank() );
    TEST_ASSERT( source_iface->get_source_comm()->getSize()
		 == getDefaultComm<int>()->getSize() );

    TEST_ASSERT( source_iface->is_field_supported("DISTRIBUTED_TEST_FIELD") );
    TEST_ASSERT( source_iface->is_field_supported("SCALAR_TEST_FIELD") );
    TEST_ASSERT( !source_iface->is_field_supported("FOO_TEST_FIELD") );

    PointType positive_point(1, 1.0, 1.0, 1.0);
    PointType negative_point(1, -1.0, -1.0, -1.0);
    TEST_ASSERT( source_iface->is_local_point(positive_point) );
    TEST_ASSERT( !source_iface->is_local_point(negative_point) );

    std::vector<PointType> points(2);
    points[0] = positive_point;
    points[1] = negative_point;
    Teuchos::ArrayRCP<bool> point_check =
	source_iface->are_local_points(
	    Teuchos::ArrayView<PointType>( &points[0], (int) points.size() ) );
    TEST_ASSERT( point_check[0] );
    TEST_ASSERT( !point_check[1] );

    Teuchos::ArrayView<double> data_view;
    data_view = source_iface->get_source_data("FOO_TEST_FIELD");
    TEST_ASSERT( data_view.is_null() );
    data_view = source_iface->get_source_data("DISTRIBUTED_TEST_FIELD");
    TEST_ASSERT( data_view.size() == 1);
    TEST_ASSERT( data_view[0] == 1.0*getDefaultComm<int>()->getRank() );

    TEST_ASSERT( source_iface->get_global_source_data("FOO_TEST_FIELD") == 0.0 );
    TEST_ASSERT( source_iface->get_global_source_data("SCALAR_TEST_FIELD") ==
		 1.0*getDefaultComm<int>()->getRank() );
}

TEUCHOS_UNIT_TEST( Transfer_DataTarget, target_interface_test )
{
    typedef Point<int,double>     PointType;

    // create a data container instance.
    Teuchos::RCP<Data_Container> container = Teuchos::rcp(new Data_Container);

    // create an instance of the target interface.
    Teuchos::RCP<DataTarget<double,int,double> > target_iface =
	Teuchos::rcp(
	    new test_DataTarget<double,int,double>(container));

    // test the interface methods.
    TEST_ASSERT( target_iface->get_target_comm()->getRank()
		 == getDefaultComm<int>()->getRank() );
    TEST_ASSERT( target_iface->get_target_comm()->getSize()
		 == getDefaultComm<int>()->getSize() );

    TEST_ASSERT( target_iface->is_field_supported("DISTRIBUTED_TEST_FIELD") );
    TEST_ASSERT( target_iface->is_field_supported("SCALAR_TEST_FIELD") );
    TEST_ASSERT( !target_iface->is_field_supported("FOO_TEST_FIELD") );

    Teuchos::ArrayView<PointType> points_view;
    points_view = target_iface->get_target_points("FOO_TEST_FIELD");
    TEST_ASSERT( points_view.size() == 0 );
    points_view = target_iface->get_target_points("DISTRIBUTED_TEST_FIELD");
    TEST_ASSERT( points_view.size() == 1 );
    TEST_ASSERT( points_view[0].handle() == 1 );
    TEST_ASSERT( points_view[0].x() == 1.0 );
    TEST_ASSERT( points_view[0].y() == 1.0 );
    TEST_ASSERT( points_view[0].z() == 1.0 );

    Teuchos::ArrayView<double> data_view;
    data_view = target_iface->get_target_data_space("FOO_TEST_FIELD");
    TEST_ASSERT( data_view.is_null() );
    data_view = target_iface->get_target_data_space("DISTRIBUTED_TEST_FIELD");
    data_view[0] = 1.0*getDefaultComm<int>()->getRank();
    TEST_ASSERT( container->get_distributed_data().size() == 1 );
    TEST_ASSERT( container->get_distributed_data()[0] ==
		 1.0*getDefaultComm<int>()->getRank() );

    double global_scalar = 1.0*getDefaultComm<int>()->getRank();
    target_iface->set_global_target_data("FOO_TEST_FIELD", global_scalar);
    target_iface->set_global_target_data("SCALAR_TEST_FIELD", global_scalar);
    TEST_ASSERT( container->get_scalar_data() ==
		 1.0*getDefaultComm<int>()->getRank() );
}

TEUCHOS_UNIT_TEST( Transfer_DataSource, simple_coupling_test )
{
    typedef Point<int,double>     PointType;

    // create an instance of the source interface.
    Teuchos::RCP<DataSource<double,int,double> > source_iface =
	Teuchos::rcp(new test_DataSource<double,int,double>);

    // create a data container instance for checking the data under the target
    // interface.
    Teuchos::RCP<Data_Container> container = Teuchos::rcp(new Data_Container);

    // create an instance of the target interface.
    Teuchos::RCP<DataTarget<double,int,double> > target_iface =
	Teuchos::rcp( new test_DataTarget<double,int,double>(container));

    // Check that the field is supported.
    TEST_ASSERT( source_iface->is_field_supported("DISTRIBUTED_TEST_FIELD") &&
		 target_iface->is_field_supported("DISTRIBUTED_TEST_FIELD") );

    // Test a target point in the source
    TEST_ASSERT( source_iface->is_local_point(
		     target_iface->get_target_points(
			 "DISTRIBUTED_TEST_FIELD")[0] ) );

    // Transfer data from the source to the target.
    Teuchos::ArrayView<double> source_view =
	source_iface->get_source_data("DISTRIBUTED_TEST_FIELD");

    Teuchos::ArrayView<double> target_view =
	target_iface->get_target_data_space("DISTRIBUTED_TEST_FIELD");

    target_view.assign(source_view);

    TEST_ASSERT( container->get_distributed_data().size() == 1 );
    TEST_ASSERT( container->get_distributed_data()[0] ==
		 1.0*getDefaultComm<int>()->getRank() );
}

} // end namespace Coupler

//---------------------------------------------------------------------------//
//                        end of tstInterfaces.cpp
//---------------------------------------------------------------------------//
<commit_msg>Updated the interfaces test to further test the array based point query method.<commit_after>//----------------------------------*-C++-*----------------------------------//
/*!
 * \file   core/test/tstInterfaces.cpp
 * \author Stuart Slattery
 * \date   Thu Dec 01 16:50:04 2011
 * \brief  Unit tests for the data transfer pure virtual interfaces.
 */
//---------------------------------------------------------------------------//

#include <iostream>
#include <vector>
#include <cmath>
#include <sstream>

#include <Coupler_Point.hpp>
#include <Coupler_DataSource.hpp>
#include <Coupler_DataTarget.hpp>

#include "Teuchos_ArrayView.hpp"
#include "Teuchos_RCP.hpp"
#include "Teuchos_UnitTestHarness.hpp"
#include "Teuchos_DefaultComm.hpp"

//---------------------------------------------------------------------------//
// HELPER FUNCTIONS
//---------------------------------------------------------------------------//

template<class Ordinal>
Teuchos::RCP<const Teuchos::Comm<Ordinal> > getDefaultComm()
{
#ifdef HAVE_MPI
    return Teuchos::DefaultComm<Ordinal>::getComm();
#else
    return Teuchos::rcp(new Teuchos::SerialComm<Ordinal>() );
#endif
}

//---------------------------------------------------------------------------//
// DATA CLASS
//---------------------------------------------------------------------------//

class Data_Container
{
  public:

    typedef Coupler::Point<int,double>     PointType;

  private:

    double scalar_data;
    std::vector<double> distributed_data;
    std::vector<PointType> distributed_points;

  public:

    Data_Container()
      : scalar_data(0.0)
    { /* ... */ }

    ~Data_Container()
    { /* ... */ }

    void set_distributed_points(std::vector<PointType> points)
    {
	distributed_points = points;
    }

    Teuchos::ArrayView<PointType> get_distributed_points()
    {
	return distributed_points;
    }

    Teuchos::ArrayView<double> get_distributed_data()
    {
	distributed_data.resize(1);
	return Teuchos::ArrayView<double>(distributed_data);
    }

    void set_scalar_data(double data)
    {
	scalar_data = data;
    }

    double get_scalar_data()
    {
	return scalar_data;
    }
};

//---------------------------------------------------------------------------//
// INTERFACE IMPLEMENTATIONS
//---------------------------------------------------------------------------//

namespace Coupler {

// transfer data source implementation - this implementation specifies double
// as the data type
template<class DataType_T, class HandleType_T, class CoordinateType_T>
class test_DataSource
    : public DataSource<DataType_T, HandleType_T, CoordinateType_T>
{
  private:

    std::vector<double> private_data;

  public:

    typedef double                                   DataType;
    typedef int                                      HandleType;
    typedef double                                   CoordinateType;
    typedef int                                      OrdinalType;
    typedef Point<HandleType,CoordinateType>         PointType;
    typedef Teuchos::Comm<OrdinalType>               Communicator_t;
    typedef Teuchos::RCP<const Communicator_t>       RCP_Communicator;

    test_DataSource()
    { /* ... */ }

    ~test_DataSource()
    { /* ... */ }

    RCP_Communicator get_source_comm()
    {
	return getDefaultComm<OrdinalType>();
    }

    bool is_field_supported(const std::string &field_name)
    {
	bool return_val = false;

	if (field_name == "DISTRIBUTED_TEST_FIELD")
	{
	    return_val = true;
	}

	else if (field_name == "SCALAR_TEST_FIELD")
	{
	    return_val = true;
	}

	return return_val;
    }

    bool is_local_point(const PointType &point)
    {
	bool return_val = false;

	if ( point.x() > 0 && point.y() > 0 && point.z() > 0 )
	{
	    return_val = true;
	}

	return return_val;
    }

    const Teuchos::ArrayView<double>
    get_source_data(const std::string &field_name)
    {
	Teuchos::ArrayView<double> return_view;

	if ( field_name == "DISTRIBUTED_TEST_FIELD" )
	{
	    private_data.clear();
	    private_data.resize(1);
	    private_data[0] = 1.0*getDefaultComm<OrdinalType>()->getRank();
	    Teuchos::ArrayView<double> private_view(private_data);
	    return_view =  private_view;
	}

	return return_view;
    }

    double get_global_source_data(const std::string &field_name)
    {
	double return_val = 0.0;

	if ( field_name == "SCALAR_TEST_FIELD" )
	{
	    return_val = 1.0*getDefaultComm<OrdinalType>()->getRank();
	}

	return return_val;
    }
};

//---------------------------------------------------------------------------//
// transfer data target implementation - this implementation specifies double
// as the data type
template<class DataType_T, class HandleType_T, class CoordinateType_T>
class test_DataTarget
    : public DataTarget<DataType_T, HandleType_T, CoordinateType_T>
{
  public:

    typedef double                                   DataType;
    typedef int                                      HandleType;
    typedef double                                   CoordinateType;
    typedef int                                      OrdinalType;
    typedef Point<HandleType,CoordinateType>         PointType;
    typedef Teuchos::Comm<OrdinalType>               Communicator_t;
    typedef Teuchos::RCP<const Communicator_t>       RCP_Communicator;

  private:

    Teuchos::RCP<Data_Container> container;
    std::vector<PointType> points;

  public:

    test_DataTarget(Teuchos::RCP<Data_Container> _container)
	: container(_container)
    { /* ... */ }

    ~test_DataTarget()
    { /* ... */ }

    RCP_Communicator get_target_comm()
    {
	return getDefaultComm<OrdinalType>();
    }

    bool is_field_supported(const std::string &field_name)
    {
	bool return_val = false;

	if (field_name == "DISTRIBUTED_TEST_FIELD")
	{
	    return_val = true;
	}

	else if (field_name == "SCALAR_TEST_FIELD")
	{
	    return_val = true;
	}

	return return_val;
    }

    const Teuchos::ArrayView<PointType>
    get_target_points(const std::string &field_name)
    {
	Teuchos::ArrayView<PointType> return_view;

	if ( field_name == "DISTRIBUTED_TEST_FIELD" )
	{
	    PointType local_point(1, 1.0, 1.0, 1.0);
	    std::vector<PointType> local_points(1, local_point);
	    points = local_points;
	    return_view = Teuchos::ArrayView<PointType>(points);
	}

	return return_view;
    }

    Teuchos::ArrayView<DataType>
    get_target_data_space(const std::string &field_name)
    {
	Teuchos::ArrayView<DataType> return_view;

	if ( field_name == "DISTRIBUTED_TEST_FIELD" )
	{
	    return_view = container->get_distributed_data();
	}

	return return_view;
    }

    void set_global_target_data(const std::string &field_name,
				const DataType &data)
    {
	if ( field_name == "SCALAR_TEST_FIELD" )
	{
	    container->set_scalar_data(data);
	}
    }
};

} // end namespace Coupler

//---------------------------------------------------------------------------//
// TESTS
//---------------------------------------------------------------------------//

namespace Coupler {

TEUCHOS_UNIT_TEST( Transfer_DataSource, source_interface_test )
{
    typedef Point<int,double>     PointType;

    // create an instance of the source interface.
    Teuchos::RCP<DataSource<double,int,double> > source_iface =
	Teuchos::rcp(new test_DataSource<double,int,double>);

    // test the interface methods.
    TEST_ASSERT( source_iface->get_source_comm()->getRank()
		 == getDefaultComm<int>()->getRank() );
    TEST_ASSERT( source_iface->get_source_comm()->getSize()
		 == getDefaultComm<int>()->getSize() );

    TEST_ASSERT( source_iface->is_field_supported("DISTRIBUTED_TEST_FIELD") );
    TEST_ASSERT( source_iface->is_field_supported("SCALAR_TEST_FIELD") );
    TEST_ASSERT( !source_iface->is_field_supported("FOO_TEST_FIELD") );

    PointType positive_point(1, 1.0, 1.0, 1.0);
    PointType negative_point(1, -1.0, -1.0, -1.0);
    TEST_ASSERT( source_iface->is_local_point(positive_point) );
    TEST_ASSERT( !source_iface->is_local_point(negative_point) );

    std::vector<PointType> points(2);
    points[0] = positive_point;
    points[1] = negative_point;
    Teuchos::ArrayRCP<bool> point_check =
	source_iface->are_local_points(
	    Teuchos::ArrayView<PointType>( &points[0], (int) points.size() ) );
    TEST_ASSERT( point_check[0] );
    TEST_ASSERT( !point_check[1] );

    Teuchos::ArrayView<double> data_view;
    data_view = source_iface->get_source_data("FOO_TEST_FIELD");
    TEST_ASSERT( data_view.is_null() );
    data_view = source_iface->get_source_data("DISTRIBUTED_TEST_FIELD");
    TEST_ASSERT( data_view.size() == 1);
    TEST_ASSERT( data_view[0] == 1.0*getDefaultComm<int>()->getRank() );

    TEST_ASSERT( source_iface->get_global_source_data("FOO_TEST_FIELD") == 0.0 );
    TEST_ASSERT( source_iface->get_global_source_data("SCALAR_TEST_FIELD") ==
		 1.0*getDefaultComm<int>()->getRank() );
}

TEUCHOS_UNIT_TEST( Transfer_DataTarget, target_interface_test )
{
    typedef Point<int,double>     PointType;

    // create a data container instance.
    Teuchos::RCP<Data_Container> container = Teuchos::rcp(new Data_Container);

    // create an instance of the target interface.
    Teuchos::RCP<DataTarget<double,int,double> > target_iface =
	Teuchos::rcp(
	    new test_DataTarget<double,int,double>(container));

    // test the interface methods.
    TEST_ASSERT( target_iface->get_target_comm()->getRank()
		 == getDefaultComm<int>()->getRank() );
    TEST_ASSERT( target_iface->get_target_comm()->getSize()
		 == getDefaultComm<int>()->getSize() );

    TEST_ASSERT( target_iface->is_field_supported("DISTRIBUTED_TEST_FIELD") );
    TEST_ASSERT( target_iface->is_field_supported("SCALAR_TEST_FIELD") );
    TEST_ASSERT( !target_iface->is_field_supported("FOO_TEST_FIELD") );

    Teuchos::ArrayView<PointType> points_view;
    points_view = target_iface->get_target_points("FOO_TEST_FIELD");
    TEST_ASSERT( points_view.size() == 0 );
    points_view = target_iface->get_target_points("DISTRIBUTED_TEST_FIELD");
    TEST_ASSERT( points_view.size() == 1 );
    TEST_ASSERT( points_view[0].handle() == 1 );
    TEST_ASSERT( points_view[0].x() == 1.0 );
    TEST_ASSERT( points_view[0].y() == 1.0 );
    TEST_ASSERT( points_view[0].z() == 1.0 );

    Teuchos::ArrayView<double> data_view;
    data_view = target_iface->get_target_data_space("FOO_TEST_FIELD");
    TEST_ASSERT( data_view.is_null() );
    data_view = target_iface->get_target_data_space("DISTRIBUTED_TEST_FIELD");
    data_view[0] = 1.0*getDefaultComm<int>()->getRank();
    TEST_ASSERT( container->get_distributed_data().size() == 1 );
    TEST_ASSERT( container->get_distributed_data()[0] ==
		 1.0*getDefaultComm<int>()->getRank() );

    double global_scalar = 1.0*getDefaultComm<int>()->getRank();
    target_iface->set_global_target_data("FOO_TEST_FIELD", global_scalar);
    target_iface->set_global_target_data("SCALAR_TEST_FIELD", global_scalar);
    TEST_ASSERT( container->get_scalar_data() ==
		 1.0*getDefaultComm<int>()->getRank() );
}

TEUCHOS_UNIT_TEST( Transfer_DataSource, simple_coupling_test )
{
    typedef Point<int,double>     PointType;

    // create an instance of the source interface.
    Teuchos::RCP<DataSource<double,int,double> > source_iface =
	Teuchos::rcp(new test_DataSource<double,int,double>);

    // create a data container instance for checking the data under the target
    // interface.
    Teuchos::RCP<Data_Container> container = Teuchos::rcp(new Data_Container);

    // create an instance of the target interface.
    Teuchos::RCP<DataTarget<double,int,double> > target_iface =
	Teuchos::rcp( new test_DataTarget<double,int,double>(container));

    // Check that the field is supported.
    TEST_ASSERT( source_iface->is_field_supported("DISTRIBUTED_TEST_FIELD") &&
		 target_iface->is_field_supported("DISTRIBUTED_TEST_FIELD") );

    // Test a target point in the source
    TEST_ASSERT( source_iface->is_local_point(
		     target_iface->get_target_points(
			 "DISTRIBUTED_TEST_FIELD")[0] ) );
    TEST_ASSERT( source_iface->are_local_points(
		     target_iface->get_target_points(
			 "DISTRIBUTED_TEST_FIELD" ) )[0] );

    // Transfer data from the source to the target.
    Teuchos::ArrayView<double> source_view =
	source_iface->get_source_data("DISTRIBUTED_TEST_FIELD");

    Teuchos::ArrayView<double> target_view =
	target_iface->get_target_data_space("DISTRIBUTED_TEST_FIELD");

    target_view.assign(source_view);

    TEST_ASSERT( container->get_distributed_data().size() == 1 );
    TEST_ASSERT( container->get_distributed_data()[0] ==
		 1.0*getDefaultComm<int>()->getRank() );
}

} // end namespace Coupler

//---------------------------------------------------------------------------//
//                        end of tstInterfaces.cpp
//---------------------------------------------------------------------------//
<|endoftext|>


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

using terminal_tools::print_color;
using terminal_tools::print_error;
using terminal_tools::print_error;
using terminal_tools::print_warn;
using terminal_tools::print_info;
using terminal_tools::print_debug;
using terminal_tools::print_value;
using terminal_tools::print_highlight;
using terminal_tools::TT_BRIGHT;
using terminal_tools::TT_RED;
using terminal_tools::TT_GREEN;
using terminal_tools::TT_BLUE;

typedef pcl_visualization::PointCloudColorHandler<pcl::PointCloud<pcl::PointXYZ> > ColorHandler;
typedef ColorHandler::Ptr ColorHandlerPtr;
typedef ColorHandler::ConstPtr ColorHandlerConstPtr;

typedef pcl_visualization::PointCloudGeometryHandler<pcl::PointCloud<pcl::PointXYZ> > GeometryHandler;
typedef GeometryHandler::Ptr GeometryHandlerPtr;
typedef GeometryHandler::ConstPtr GeometryHandlerConstPtr;
boost::mutex mutex_;

#define NORMALS_SCALE 0.01
#define PC_SCALE 0.001

void
  printHelp (int argc, char **argv)
{
  //print_error ("Syntax is: %s <file_name 1..N>.pcd <options>\n", argv[0]);
  print_error ("Syntax is: %s <options>\n", argv[0]);
  print_info ("  where options are:\n");
  print_info ("                     -dev device_id           = device to be used\n");
  print_info ("                                                maybe \"#n\", with n being the number of the device in device list.\n");
  print_info ("                                                maybe \"bus@addr\", with bus and addr being the usb bus and address where device is connected.\n");
  print_info ("                                                maybe \"serial\", with serial being the serial number of the device.\n");
  print_info ("                     -bc r,g,b                = background color\n");
  print_info ("                     -fc r,g,b                = foreground color\n");
  print_info ("                     -ps X                    = point size ("); print_value ("1..64"); print_info (") \n");
  print_info ("                     -opaque X                = rendered point cloud opacity ("); print_value ("0..1"); print_info (")\n");

  print_info ("                     -ax "); print_value ("n"); print_info ("                    = enable on-screen display of ");
  print_color (stdout, TT_BRIGHT, TT_RED, "X"); print_color (stdout, TT_BRIGHT, TT_GREEN, "Y"); print_color (stdout, TT_BRIGHT, TT_BLUE, "Z");
  print_info (" axes and scale them to "); print_value ("n\n");
  print_info ("                     -ax_pos X,Y,Z            = if axes are enabled, set their X,Y,Z position in space (default "); print_value ("0,0,0"); print_info (")\n");

  print_info ("\n");
  print_info ("                     -cam (*)                 = use given camera settings as initial view\n");
  print_info (stderr, " (*) [Clipping Range / Focal Point / Position / ViewUp / Distance / Window Size / Window Pos] or use a <filename.cam> that contains the same information.\n");

  print_info ("\n");
  print_info ("                     -multiview 0/1           = enable/disable auto-multi viewport rendering (default "); print_value ("disabled"); print_info (")\n");
  print_info ("\n");

  print_info ("\n");
  print_info ("                     -normals 0/X             = disable/enable the display of every Xth point's surface normal as lines (default "); print_value ("disabled"); print_info (")\n");
  print_info ("                     -normals_scale X         = resize the normal unit vector size to X (default "); print_value ("0.02"); print_info (")\n");
  print_info ("\n");
  print_info ("                     -pc 0/X                  = disable/enable the display of every Xth point's principal curvatures as lines (default "); print_value ("disabled"); print_info (")\n");
  print_info ("                     -pc_scale X              = resize the principal curvatures vectors size to X (default "); print_value ("0.02"); print_info (")\n");
  print_info ("\n");

  print_info ("\n(Note: for multiple .pcd files, provide multiple -{fc,ps,opaque} parameters; they will be automatically assigned to the right file)\n");
}

// Create the PCLVisualizer object
boost::shared_ptr<pcl_visualization::PCLVisualizer> p;
ColorHandlerPtr color_handler;
GeometryHandlerPtr geometry_handler;
std::vector<double> fcolor_r, fcolor_b, fcolor_g;
bool fcolorparam = false;

struct EventHelper
{
  void cloud_cb (const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr &cloud)
  {
      //std::cout << __PRETTY_FUNCTION__ << " " << cloud->width << std::endl;
    // Add the dataset with a XYZ and a random handler
  //  geometry_handler.reset (new pcl_visualization::PointCloudGeometryHandlerXYZ<pcl::PointCloud<pcl::PointXYZRGB> > (*cloud));

    //// If color was given, ues that
    //if (fcolorparam)
    //  color_handler.reset (new pcl_visualization::PointCloudColorHandlerCustom<pcl::PointCloud<pcl::PointXYZRGB> > (cloud, fcolor_r, fcolor_g, fcolor_b));
    //else
    //  color_handler.reset (new pcl_visualization::PointCloudColorHandlerRandom<pcl::PointCloud<pcl::PointXYZRGB> > (cloud));

    // Add the cloud to the renderer

    boost::mutex::scoped_lock lock(mutex_);
    if (!cloud)
      return;
    p->removePointCloud ("PCDCloud");
    p->addPointCloud (*cloud, "PCDCloud");
  }
};

/* ---[ */
int
  main (int argc, char** argv)
{
  srand (time (0));

  if (argc > 1)
  {
    for (int i = 1; i < argc; i++)
    {
      if (std::string(argv[i]) == "-h")
      {
        printHelp (argc, argv);
        return (-1);
      }
    }
  }

  // Command line parsing
  double bcolor[3] = {0, 0, 0};
  terminal_tools::parse_3x_arguments (argc, argv, "-bc", bcolor[0], bcolor[1], bcolor[2]);

  fcolorparam = terminal_tools::parse_multiple_3x_arguments (argc, argv, "-fc", fcolor_r, fcolor_g, fcolor_b);

  int psize = 0;
  terminal_tools::parse_argument (argc, argv, "-ps", psize);

  double opaque;
  terminal_tools::parse_argument (argc, argv, "-opaque", opaque);

  p.reset (new pcl_visualization::PCLVisualizer (argc, argv, "PCD viewer"));

//  // Change the cloud rendered point size
//  if (psize > 0)
//    p->setPointCloudRenderingProperties (pcl_visualization::PCL_VISUALIZER_POINT_SIZE, psize, "KinectCloud");
//
//  // Change the cloud rendered opacity
//  if (opaque >= 0)
//    p->setPointCloudRenderingProperties (pcl_visualization::PCL_VISUALIZER_OPACITY, opaque, "KinectCloud");

  p->setBackgroundColor (bcolor[0], bcolor[1], bcolor[2]);

  //boost::signals2::connection c = interface->registerCallback (boost::bind (&bla::blatestpointcloudrgb, *this, _1));
  //boost::function<void (boost::shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >)> f = boost::bind (&bla::blatestpointcloudrgb, this, _1);
  //boost::signals2::connection c =
  //  interface->registerCallback <void(boost::shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >)> (boost::bind (&bla::blatestpointcloudrgb, *this, _1).);

  // Read axes settings
  double axes  = 0.0;
  terminal_tools::parse_argument (argc, argv, "-ax", axes);
  if (axes != 0.0 && p)
  {
    double ax_x = 0.0, ax_y = 0.0, ax_z = 0.0;
    terminal_tools::parse_3x_arguments (argc, argv, "-ax_pos", ax_x, ax_y, ax_z, false);
    // Draw XYZ axes if command-line enabled
    p->addCoordinateSystem (axes, ax_x, ax_y, ax_z);
  }

  std::string pcd_file = "";
  terminal_tools::parse_argument (argc, argv, "-file", pcd_file);

  pcl::Grabber* interface = new pcl::PCDGrabber<pcl::PointXYZRGB>(pcd_file);

  EventHelper h;
  boost::function<void(const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr&)> f = boost::bind(&EventHelper::cloud_cb, &h, _1);
  boost::signals2::connection c1 = interface->registerCallback (f);

  interface->start ();
  while (true)
  {
    usleep (10000);
    {
      boost::mutex::scoped_lock lock(mutex_);
      p->spinOnce ();
      if (p->wasStopped ())
        break;
    }
  }

  interface->stop ();
}
/* ]--- */
<commit_msg>added -dir, -fps and -repeat options<commit_after>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2011, Willow Garage, Inc.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of Willow Garage, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Radu Bogdan rusu, Suat Gedikli
 *
 */
// PCL
#include <Eigen/Geometry>
#include <pcl/common/common.h>
#include <pcl/io/pcd_io.h>
#include <pcl/io/pcd_grabber.h>
#include <cfloat>
#include <pcl/visualization/point_cloud_handlers.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/visualization/histogram_visualizer.h>
#include <pcl/terminal_tools/print.h>
#include <pcl/terminal_tools/parse.h>
#include <pcl/terminal_tools/time.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>

using terminal_tools::print_color;
using terminal_tools::print_error;
using terminal_tools::print_error;
using terminal_tools::print_warn;
using terminal_tools::print_info;
using terminal_tools::print_debug;
using terminal_tools::print_value;
using terminal_tools::print_highlight;
using terminal_tools::TT_BRIGHT;
using terminal_tools::TT_RED;
using terminal_tools::TT_GREEN;
using terminal_tools::TT_BLUE;
using namespace boost::filesystem;

typedef pcl_visualization::PointCloudColorHandler<pcl::PointCloud<pcl::PointXYZ> > ColorHandler;
typedef ColorHandler::Ptr ColorHandlerPtr;
typedef ColorHandler::ConstPtr ColorHandlerConstPtr;

typedef pcl_visualization::PointCloudGeometryHandler<pcl::PointCloud<pcl::PointXYZ> > GeometryHandler;
typedef GeometryHandler::Ptr GeometryHandlerPtr;
typedef GeometryHandler::ConstPtr GeometryHandlerConstPtr;
boost::mutex mutex_;

#define NORMALS_SCALE 0.01
#define PC_SCALE 0.001

void
printHelp (int argc, char **argv)
{
  //print_error ("Syntax is: %s <file_name 1..N>.pcd <options>\n", argv[0]);
  print_error ("Syntax is: %s <options>\n", argv[0]);
  print_info ("  where options are:\n");
  print_info ("                     -file file_name          = PCD file to be read from\n");
  print_info ("                     -dir directory_path      = directory path to PCD file(s) to be read from\n");
  print_info ("                     -fps frequency           = frames per second\n");
  print_info ("                     -repeat                  = optional parameter that tells wheter the PCD file(s) should be \"grabbed\" in a endless loop.\n");
  print_info ("                     -bc r,g,b                = background color\n");
  print_info ("                     -fc r,g,b                = foreground color\n");
  print_info ("                     -ps X                    = point size (");
  print_value ("1..64");
  print_info (") \n");
  print_info ("                     -opaque X                = rendered point cloud opacity (");
  print_value ("0..1");
  print_info (")\n");

  print_info ("                     -ax ");
  print_value ("n");
  print_info ("                    = enable on-screen display of ");
  print_color (stdout, TT_BRIGHT, TT_RED, "X");
  print_color (stdout, TT_BRIGHT, TT_GREEN, "Y");
  print_color (stdout, TT_BRIGHT, TT_BLUE, "Z");
  print_info (" axes and scale them to ");
  print_value ("n\n");
  print_info ("                     -ax_pos X,Y,Z            = if axes are enabled, set their X,Y,Z position in space (default ");
  print_value ("0,0,0");
  print_info (")\n");

  print_info ("\n");
  print_info ("                     -cam (*)                 = use given camera settings as initial view\n");
  print_info (stderr, " (*) [Clipping Range / Focal Point / Position / ViewUp / Distance / Window Size / Window Pos] or use a <filename.cam> that contains the same information.\n");

  print_info ("\n");
  print_info ("                     -multiview 0/1           = enable/disable auto-multi viewport rendering (default ");
  print_value ("disabled");
  print_info (")\n");
  print_info ("\n");

  print_info ("\n");
  print_info ("                     -normals 0/X             = disable/enable the display of every Xth point's surface normal as lines (default ");
  print_value ("disabled");
  print_info (")\n");
  print_info ("                     -normals_scale X         = resize the normal unit vector size to X (default ");
  print_value ("0.02");
  print_info (")\n");
  print_info ("\n");
  print_info ("                     -pc 0/X                  = disable/enable the display of every Xth point's principal curvatures as lines (default ");
  print_value ("disabled");
  print_info (")\n");
  print_info ("                     -pc_scale X              = resize the principal curvatures vectors size to X (default ");
  print_value ("0.02");
  print_info (")\n");
  print_info ("\n");

  print_info ("\n(Note: for multiple .pcd files, provide multiple -{fc,ps,opaque} parameters; they will be automatically assigned to the right file)\n");
}

// Create the PCLVisualizer object
boost::shared_ptr<pcl_visualization::PCLVisualizer> p;
ColorHandlerPtr color_handler;
GeometryHandlerPtr geometry_handler;
std::vector<double> fcolor_r, fcolor_b, fcolor_g;
bool fcolorparam = false;

struct EventHelper
{

  void cloud_cb (const pcl::PointCloud<pcl::PointXYZ>::ConstPtr & cloud)
  {
    //std::cout << __PRETTY_FUNCTION__ << " " << cloud->width << std::endl;
    // Add the dataset with a XYZ and a random handler
    //  geometry_handler.reset (new pcl_visualization::PointCloudGeometryHandlerXYZ<pcl::PointCloud<pcl::PointXYZRGB> > (*cloud));

    //// If color was given, ues that
    //if (fcolorparam)
    //  color_handler.reset (new pcl_visualization::PointCloudColorHandlerCustom<pcl::PointCloud<pcl::PointXYZRGB> > (cloud, fcolor_r, fcolor_g, fcolor_b));
    //else
    //  color_handler.reset (new pcl_visualization::PointCloudColorHandlerRandom<pcl::PointCloud<pcl::PointXYZRGB> > (cloud));

    // Add the cloud to the renderer

    boost::mutex::scoped_lock lock (mutex_);
    if (!cloud)
      return;
    p->removePointCloud ("PCDCloud");
    p->addPointCloud (*cloud, "PCDCloud");
  }
};

/* ---[ */
int
main (int argc, char** argv)
{
  srand (time (0));

  if (argc > 1)
  {
    for (int i = 1; i < argc; i++)
    {
      if (std::string (argv[i]) == "-h")
      {
        printHelp (argc, argv);
        return (-1);
      }
    }
  }

  // Command line parsing
  double bcolor[3] = {0, 0, 0};
  terminal_tools::parse_3x_arguments (argc, argv, "-bc", bcolor[0], bcolor[1], bcolor[2]);

  fcolorparam = terminal_tools::parse_multiple_3x_arguments (argc, argv, "-fc", fcolor_r, fcolor_g, fcolor_b);

  int psize = 0;
  terminal_tools::parse_argument (argc, argv, "-ps", psize);

  double opaque;
  terminal_tools::parse_argument (argc, argv, "-opaque", opaque);

  p.reset (new pcl_visualization::PCLVisualizer (argc, argv, "PCD viewer"));

  //  // Change the cloud rendered point size
  //  if (psize > 0)
  //    p->setPointCloudRenderingProperties (pcl_visualization::PCL_VISUALIZER_POINT_SIZE, psize, "KinectCloud");
  //
  //  // Change the cloud rendered opacity
  //  if (opaque >= 0)
  //    p->setPointCloudRenderingProperties (pcl_visualization::PCL_VISUALIZER_OPACITY, opaque, "KinectCloud");

  p->setBackgroundColor (bcolor[0], bcolor[1], bcolor[2]);

  //boost::signals2::connection c = interface->registerCallback (boost::bind (&bla::blatestpointcloudrgb, *this, _1));
  //boost::function<void (boost::shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >)> f = boost::bind (&bla::blatestpointcloudrgb, this, _1);
  //boost::signals2::connection c =
  //  interface->registerCallback <void(boost::shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >)> (boost::bind (&bla::blatestpointcloudrgb, *this, _1).);

  // Read axes settings
  double axes = 0.0;
  terminal_tools::parse_argument (argc, argv, "-ax", axes);
  if (axes != 0.0 && p)
  {
    double ax_x = 0.0, ax_y = 0.0, ax_z = 0.0;
    terminal_tools::parse_3x_arguments (argc, argv, "-ax_pos", ax_x, ax_y, ax_z, false);
    // Draw XYZ axes if command-line enabled
    p->addCoordinateSystem (axes, ax_x, ax_y, ax_z);
  }

  pcl::Grabber* interface = 0;

  float frames_per_second = 0; // 0 means only if triggered!
  terminal_tools::parse (argc, argv, "-fps", frames_per_second);
  if (frames_per_second < 0)
    frames_per_second = 0.0;

  std::cout << terminal_tools::find_argument (argc, argv, "-repeat") << " : repaet" << std::endl;
  bool repeat = (terminal_tools::find_argument (argc, argv, "-repeat") != -1);

  std::cout << "fps: " << frames_per_second << " , repeat: " << repeat << std::endl;
  std::string path = "";
  terminal_tools::parse_argument (argc, argv, "-file", path);
  std::cout << "path: " << path << std::endl;
  if (path != "" && exists (path))
  {
    interface = new pcl::PCDGrabber<pcl::PointXYZ > (path, frames_per_second, repeat);
  }
  else
  {
    std::vector<std::string> pcd_files;
    terminal_tools::parse_argument (argc, argv, "-dir", path);
    std::cout << "path: " << path << std::endl;
    if (path != "" && exists (path))
    {
      directory_iterator end_itr;
      for (directory_iterator itr (path); itr != end_itr; ++itr)
      {
        if (!is_directory (itr->status()) && boost::algorithm::to_upper_copy(extension (itr->leaf())) == ".PCD" )
        {
          pcd_files.push_back (itr->path ().string());
          std::cout << "added: " << itr->path ().string() << std::endl;
        }
      }
    }
    else
    {
      std::cout << "Neither a pcd file given using the \"-file\" option, nor given a directory containing pcd files using the \"-dir\" option." << std::endl;
    }
    interface = new pcl::PCDGrabber<pcl::PointXYZ > (pcd_files, frames_per_second, repeat);
  }

  EventHelper h;
  boost::function<void(const pcl::PointCloud<pcl::PointXYZ>::ConstPtr&) > f = boost::bind (&EventHelper::cloud_cb, &h, _1);
  boost::signals2::connection c1 = interface->registerCallback (f);

  interface->start ();
  while (true)
  {
    usleep (10000);
    {
      boost::mutex::scoped_lock lock (mutex_);
      p->spinOnce ();
      if (p->wasStopped ())
        break;
    }
  }

  interface->stop ();
}
/* ]--- */
<|endoftext|>


<commit_before>#include "MacauPrior.h"

#include <SmurffCpp/Utils/Distribution.h>
#include <Utils/Error.h>
#include <Utils/counters.h>

#include <ios>

namespace smurff {

MacauPrior::MacauPrior(TrainSession &trainSession, uint32_t mode)
    : NormalPrior(trainSession, mode, "MacauPrior")
{
    beta_precision = SideInfoConfig::BETA_PRECISION_DEFAULT_VALUE;
    tol = SideInfoConfig::TOL_DEFAULT_VALUE;

    enable_beta_precision_sampling = Config::ENABLE_BETA_PRECISION_SAMPLING_DEFAULT_VALUE;
}

MacauPrior::~MacauPrior()
{
}

void MacauPrior::init()
{
   NormalPrior::init();

   THROWERROR_ASSERT_MSG(Features->rows() == num_item(), "Number of rows in train must be equal to number of rows in features");

   if (use_FtF)
   {
      std::uint64_t dim = num_feat();
      FtF_plus_precision.resize(dim, dim);
      Features->At_mul_A(FtF_plus_precision);
      FtF_plus_precision.diagonal().array() += beta_precision;
   }

   Uhat.resize(num_item(), num_latent());
   Uhat.setZero();

   beta().resize(num_feat(), num_latent());
   beta().setZero();

   BtB = beta().transpose() * beta();
}

void MacauPrior::update_prior()
{
    /*
>> compute_uhat:                     0.5012     (12%) in        110
>> main:                             4.1396     (100%) in       1
>> rest of update_prior:             0.1684     (4%) in 110
>> sample hyper mu/Lambda:           0.3804     (9%) in 110
>> sample_beta:                      1.4927     (36%) in        110
>> sample_latents:                   3.8824     (94%) in        220
>> step:                             3.9824     (96%) in        111
>> update_prior:                     2.5436     (61%) in        110
*/
    COUNTER("update_prior");
    {
        COUNTER("rest of update_prior");

    }

    // sampling Gaussian
    {
        COUNTER("sample hyper mu/Lambda");
        //uses: U, Uhat
        // writes: Udelta
        // complexity: num_latent x num_items
        Udelta = U() - Uhat;
        // uses: Udelta
        // complexity: num_latent x num_items
        std::tie(mu(), Lambda) = CondNormalWishart(Udelta, mu0, b0, WI + beta_precision * BtB, df + num_feat());
    }

    // uses: U, F
    // writes: Ft_y
    // uses: U, F
    // writes: Ft_y
    // complexity: num_latent x num_feat x num_item
    compute_Ft_y(Ft_y);

    sample_beta();

    {
        COUNTER("compute_uhat");
        // Uhat = beta * F
        // uses: beta, F
        // output: Uhat
        // complexity: num_feat x num_latent x num_item
        Features->compute_uhat(Uhat, beta());
    }

    if (enable_beta_precision_sampling)
    {
        // uses: beta
        // writes: FtF
        COUNTER("sample_beta_precision");
        double old_beta = beta_precision;
        beta_precision = sample_beta_precision(BtB, Lambda, beta_precision_nu0, beta_precision_mu0, beta().cols());
        FtF_plus_precision.diagonal().array() += beta_precision - old_beta;
   }
}

void MacauPrior::sample_beta()
{
    COUNTER("sample_beta");
    if (use_FtF)
    {
        // uses: FtF, Ft_y,
        // writes: beta()
        // complexity: num_feat^3
        beta() = FtF_plus_precision.llt().solve(Ft_y);
    }
    else
    {
        // uses: Features, beta_precision, Ft_y,
        // writes: beta
        // complexity: num_feat x num_feat x num_iter
        blockcg_iter = Features->solve_blockcg(beta(), beta_precision, Ft_y, tol, 32, 8, throw_on_cholesky_error);
    }
    // complexity: num_feat x num_feat x num_latent
    BtB = beta().transpose() * beta();
}

const Vector MacauPrior::fullMu(int n) const
{
   return mu() + Uhat.row(n);
}

void MacauPrior::compute_Ft_y(Matrix& Ft_y)
{
    COUNTER("compute Ft_y");
   // Ft_y = (U .- mu + Normal(0, Lambda^-1)) * F + std::sqrt(beta_precision) * Normal(0, Lambda^-1)
   // Ft_y is [ num_latent x num_feat ] matrix

   //HyperU: num_latent x num_item
   HyperU = (U() + MvNormal_prec(Lambda, num_item())).rowwise() - mu();
   Ft_y = Features->A_mul_B(HyperU); // num_latent x num_feat

   //--  add beta_precision
   HyperU2 = MvNormal_prec(Lambda, num_feat()); // num_latent x num_feat
   Ft_y += std::sqrt(beta_precision) * HyperU2;
}

void MacauPrior::addSideInfo(const std::shared_ptr<ISideInfo>& side, double bp, double to, bool di, bool sa, bool th)
{
    Features = side;
    beta_precision = bp;
    tol = to;
    use_FtF = di;
    enable_beta_precision_sampling = sa;
    throw_on_cholesky_error = th;

    // Hyper-prior for beta_precision (mean 1.0, var of 1e+3):
    beta_precision_mu0 = 1.0;
    beta_precision_nu0 = 1e-3;
}

std::ostream& MacauPrior::info(std::ostream &os, std::string indent)
{
   NormalPrior::info(os, indent);
   os << indent << " SideInfo: ";
   Features->print(os);
   os << indent << " Method: ";
   if (use_FtF)
   {
      os << "Cholesky Decomposition";
      double needs_gb = (double)num_feat() / 1024. * (double)num_feat() / 1024. / 1024.;
      if (needs_gb > 1.0) os << " (needing " << needs_gb << " GB of memory)";
      os << std::endl;
   } else {
      os << "CG Solver with tolerance: " << std::scientific << tol << std::fixed << std::endl;
   }
   os << indent << " BetaPrecision: ";
   if (enable_beta_precision_sampling)
   {
       os << "sampled around ";
   }
   else
   {
       os << "fixed at ";
   }
   os << beta_precision << std::endl;
   return os;
}

std::ostream &MacauPrior::status(std::ostream &os, std::string indent) const
{
   os << indent << m_name << ": " << std::endl;
   indent += "  ";
   os << indent << "mu           = " <<  mu().transpose() << std::endl;
   os << indent << "Uhat mean    = " <<  Uhat.rowwise().mean().transpose() << std::endl;
   os << indent << "blockcg iter = " << blockcg_iter << std::endl;
   os << indent << "FtF_plus_prec= " << FtF_plus_precision.norm() << std::endl;
   os << indent << "HyperU       = " << HyperU.norm() << std::endl;
   os << indent << "HyperU2      = " << HyperU2.norm() << std::endl;
   os << indent << "Beta         = " << beta().norm() << std::endl;
   os << indent << "beta_precision  = " << beta_precision << std::endl;
   os << indent << "Ft_y         = " << Ft_y.norm() << std::endl;
   return os;
}

std::pair<double, double> MacauPrior::posterior_beta_precision(const Matrix & BtB, Matrix & Lambda_u, double nu, double mu, int N)
{
   double nux = nu + N * BtB.cols();
   double mux = mu * nux / (nu + mu * (BtB.selfadjointView<Eigen::Lower>() * Lambda_u).trace());
   double b = nux / 2;
   double c = 2 * mux / nux;
   return std::make_pair(b, c);
}

double MacauPrior::sample_beta_precision(const Matrix & BtB, Matrix & Lambda_u, double nu, double mu, int N)
{
   auto gamma_post = posterior_beta_precision(BtB, Lambda_u, nu, mu, N);
   return rgamma(gamma_post.first, gamma_post.second);
}
} // end namespace smurff
<commit_msg>FIX: row-major fixes in sample_beta_precision<commit_after>#include "MacauPrior.h"

#include <SmurffCpp/Utils/Distribution.h>
#include <Utils/Error.h>
#include <Utils/counters.h>

#include <ios>

namespace smurff {

MacauPrior::MacauPrior(TrainSession &trainSession, uint32_t mode)
    : NormalPrior(trainSession, mode, "MacauPrior")
{
    beta_precision = SideInfoConfig::BETA_PRECISION_DEFAULT_VALUE;
    tol = SideInfoConfig::TOL_DEFAULT_VALUE;

    enable_beta_precision_sampling = Config::ENABLE_BETA_PRECISION_SAMPLING_DEFAULT_VALUE;
}

MacauPrior::~MacauPrior()
{
}

void MacauPrior::init()
{
   NormalPrior::init();

   THROWERROR_ASSERT_MSG(Features->rows() == num_item(), "Number of rows in train must be equal to number of rows in features");

   if (use_FtF)
   {
      std::uint64_t dim = num_feat();
      FtF_plus_precision.resize(dim, dim);
      Features->At_mul_A(FtF_plus_precision);
      FtF_plus_precision.diagonal().array() += beta_precision;
   }

   Uhat.resize(num_item(), num_latent());
   Uhat.setZero();

   beta().resize(num_feat(), num_latent());
   beta().setZero();

   BtB = beta().transpose() * beta();
}

void MacauPrior::update_prior()
{
    /*
>> compute_uhat:                     0.5012     (12%) in        110
>> main:                             4.1396     (100%) in       1
>> rest of update_prior:             0.1684     (4%) in 110
>> sample hyper mu/Lambda:           0.3804     (9%) in 110
>> sample_beta:                      1.4927     (36%) in        110
>> sample_latents:                   3.8824     (94%) in        220
>> step:                             3.9824     (96%) in        111
>> update_prior:                     2.5436     (61%) in        110
*/
    COUNTER("update_prior");
    {
        COUNTER("rest of update_prior");

    }

    // sampling Gaussian
    {
        COUNTER("sample hyper mu/Lambda");
        //uses: U, Uhat
        // writes: Udelta
        // complexity: num_latent x num_items
        Udelta = U() - Uhat;
        // uses: Udelta
        // complexity: num_latent x num_items
        SHOW(BtB);
        SHOW(beta_precision * BtB);
        std::tie(mu(), Lambda) = CondNormalWishart(Udelta, mu0, b0, WI + beta_precision * BtB, df + num_feat());
    }

    // uses: U, F
    // writes: Ft_y
    // uses: U, F
    // writes: Ft_y
    // complexity: num_latent x num_feat x num_item
    compute_Ft_y(Ft_y);

    sample_beta();

    {
        COUNTER("compute_uhat");
        // Uhat = beta * F
        // uses: beta, F
        // output: Uhat
        // complexity: num_feat x num_latent x num_item
        Features->compute_uhat(Uhat, beta());
    }

    if (enable_beta_precision_sampling)
    {
        // uses: beta
        // writes: FtF
        COUNTER("sample_beta_precision");
        double old_beta = beta_precision;
        beta_precision = sample_beta_precision(BtB, Lambda, beta_precision_nu0, beta_precision_mu0, beta().rows());
        FtF_plus_precision.diagonal().array() += beta_precision - old_beta;
   }
}

void MacauPrior::sample_beta()
{
    COUNTER("sample_beta");
    if (use_FtF)
    {
        // uses: FtF, Ft_y,
        // writes: beta()
        // complexity: num_feat^3
        beta() = FtF_plus_precision.llt().solve(Ft_y);
    }
    else
    {
        // uses: Features, beta_precision, Ft_y,
        // writes: beta
        // complexity: num_feat x num_feat x num_iter
        blockcg_iter = Features->solve_blockcg(beta(), beta_precision, Ft_y, tol, 32, 8, throw_on_cholesky_error);
    }
    // complexity: num_feat x num_feat x num_latent
    BtB = beta().transpose() * beta();
}

const Vector MacauPrior::fullMu(int n) const
{
   return mu() + Uhat.row(n);
}

void MacauPrior::compute_Ft_y(Matrix& Ft_y)
{
    COUNTER("compute Ft_y");
   // Ft_y = (U .- mu + Normal(0, Lambda^-1)) * F + std::sqrt(beta_precision) * Normal(0, Lambda^-1)
   // Ft_y is [ num_latent x num_feat ] matrix

   //HyperU: num_latent x num_item
   HyperU = (U() + MvNormal_prec(Lambda, num_item())).rowwise() - mu();
   Ft_y = Features->A_mul_B(HyperU); // num_latent x num_feat

   //--  add beta_precision
   HyperU2 = MvNormal_prec(Lambda, num_feat()); // num_latent x num_feat
   Ft_y += std::sqrt(beta_precision) * HyperU2;
}

void MacauPrior::addSideInfo(const std::shared_ptr<ISideInfo>& side, double bp, double to, bool di, bool sa, bool th)
{
    Features = side;
    beta_precision = bp;
    tol = to;
    use_FtF = di;
    enable_beta_precision_sampling = sa;
    throw_on_cholesky_error = th;

    // Hyper-prior for beta_precision (mean 1.0, var of 1e+3):
    beta_precision_mu0 = 1.0;
    beta_precision_nu0 = 1e-3;
}

std::ostream& MacauPrior::info(std::ostream &os, std::string indent)
{
   NormalPrior::info(os, indent);
   os << indent << " SideInfo: ";
   Features->print(os);
   os << indent << " Method: ";
   if (use_FtF)
   {
      os << "Cholesky Decomposition";
      double needs_gb = (double)num_feat() / 1024. * (double)num_feat() / 1024. / 1024.;
      if (needs_gb > 1.0) os << " (needing " << needs_gb << " GB of memory)";
      os << std::endl;
   } else {
      os << "CG Solver with tolerance: " << std::scientific << tol << std::fixed << std::endl;
   }
   os << indent << " BetaPrecision: ";
   if (enable_beta_precision_sampling)
   {
       os << "sampled around ";
   }
   else
   {
       os << "fixed at ";
   }
   os << beta_precision << std::endl;
   return os;
}

std::ostream &MacauPrior::status(std::ostream &os, std::string indent) const
{
   os << indent << m_name << ": " << std::endl;
   indent += "  ";
   os << indent << "mu           = " <<  mu().transpose() << std::endl;
   os << indent << "Uhat mean    = " <<  Uhat.rowwise().mean().transpose() << std::endl;
   os << indent << "blockcg iter = " << blockcg_iter << std::endl;
   os << indent << "FtF_plus_prec= " << FtF_plus_precision.norm() << std::endl;
   os << indent << "HyperU       = " << HyperU.norm() << std::endl;
   os << indent << "HyperU2      = " << HyperU2.norm() << std::endl;
   os << indent << "Beta         = " << beta().norm() << std::endl;
   os << indent << "beta_precision  = " << beta_precision << std::endl;
   os << indent << "Ft_y         = " << Ft_y.norm() << std::endl;
   return os;
}

std::pair<double, double> MacauPrior::posterior_beta_precision(const Matrix & BtB, Matrix & Lambda_u, double nu, double mu, int N)
{
   double nux = nu + N * BtB.cols();
   double mux = mu * nux / (nu + mu * (BtB.selfadjointView<Eigen::Lower>() * Lambda_u).trace());
   double b = nux / 2;
   double c = 2 * mux / nux;
   return std::make_pair(b, c);
}

double MacauPrior::sample_beta_precision(const Matrix & BtB, Matrix & Lambda_u, double nu, double mu, int N)
{
   auto gamma_post = posterior_beta_precision(BtB, Lambda_u, nu, mu, N);
   return rgamma(gamma_post.first, gamma_post.second);
}
} // end namespace smurff
<|endoftext|>