<commit_before>#ifndef CLIENT_WS_HPP
#define	CLIENT_WS_HPP

#include "crypto.hpp"

#include <boost/asio.hpp>

#include <unordered_map>
#include <iostream>
#include <random>
#include <atomic>

namespace SimpleWeb {
    template <class socket_type>
    class SocketClient;

    template <class socket_type>
    class SocketClientBase {
    public:
        class Connection {
            friend class SocketClientBase<socket_type>;
            friend class SocketClient<socket_type>;

        public:
            std::unordered_map<std::string, std::string> header;
            std::string remote_endpoint_address;
            unsigned short remote_endpoint_port;

            Connection(socket_type* socket): socket(socket), closed(false) {}
        private:
            std::unique_ptr<socket_type> socket;

            std::atomic<bool> closed;

            void read_remote_endpoint_data() {
                try {
                    remote_endpoint_address=socket->lowest_layer().remote_endpoint().address().to_string();
                    remote_endpoint_port=socket->lowest_layer().remote_endpoint().port();
                }
                catch(const std::exception& e) {
                    std::cerr << e.what() << std::endl;
                }
            }
        };

        std::unique_ptr<Connection> connection;

        class Message : public std::istream {
            friend class SocketClientBase<socket_type>;

        public:
            unsigned char fin_rsv_opcode;
            size_t size() {
                return length;
            }
            std::string string() {
                std::stringstream ss;
                ss << rdbuf();
                return ss.str();
            }
        private:
            Message(): std::istream(&streambuf) {}
            size_t length;
            boost::asio::streambuf streambuf;
        };

        class SendStream : public std::iostream {
            friend class SocketClientBase<socket_type>;
        private:
            boost::asio::streambuf streambuf;
            std::atomic<bool> sending; //Currently not in use, but might be in a future version
        public:
            SendStream(): std::iostream(&streambuf), sending(false) {}
            size_t size() {
                return streambuf.size();
            }
        };

        std::function<void(void)> onopen;
        std::function<void(std::shared_ptr<Message>)> onmessage;
        std::function<void(const boost::system::error_code&)> onerror;
        std::function<void(int, const std::string&)> onclose;

        void start() {
            connect();

            asio_io_service.run();
        }

        void stop() {
            asio_io_service.stop();
        }

        void send(std::shared_ptr<SendStream> send_stream, const std::function<void(const boost::system::error_code&)>& callback=nullptr,
                        unsigned char fin_rsv_opcode=129) {
            //Create mask
            std::vector<unsigned char> mask;
            mask.resize(4);
            std::uniform_int_distribution<int> dist(0,255);
            std::random_device rd;
            for(int c=0;c<4;c++) {
                mask[c]=static_cast<unsigned char>(dist(rd));
            }

            std::shared_ptr<boost::asio::streambuf> buffer(new boost::asio::streambuf);
            std::ostream stream(buffer.get());

            size_t length=send_stream->size();

            stream.put(fin_rsv_opcode);
            //masked (first length byte>=128)
            if(length>=126) {
                int num_bytes;
                if(length>0xffff) {
                    num_bytes=8;
                    stream.put(static_cast<unsigned char>(127+128));
                }
                else {
                    num_bytes=2;
                    stream.put(static_cast<unsigned char>(126+128));
                }

                for(int c=num_bytes-1;c>=0;c--) {
                    stream.put((length>>(8*c))%256);
                }
            }
            else
                stream.put(static_cast<unsigned char>(length+128));

            for(int c=0;c<4;c++) {
                stream.put(mask[c]);
            }

            for(size_t c=0;c<length;c++) {
                stream.put(send_stream->get()^mask[c%4]);
            }

            //Need to copy the callback-function in case its destroyed
            boost::asio::async_write(*connection->socket, *buffer,
                    [this, buffer, callback](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                if(callback) {
                    callback(ec);
                }
            });
        }

        void send_close(int status, const std::string& reason="") {
            //Send close only once (in case close is initiated by client)
            if(connection->closed.load()) {
                return;
            }
            connection->closed.store(true);

            auto send_stream=std::make_shared<SendStream>();

            send_stream->put(status>>8);
            send_stream->put(status%256);

            *send_stream << reason;

            //fin_rsv_opcode=136: message close
            send(send_stream, [](const boost::system::error_code& /*ec*/){}, 136);
        }

    protected:
        const std::string ws_magic_string="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

        boost::asio::io_service asio_io_service;
        boost::asio::ip::tcp::endpoint asio_endpoint;
        boost::asio::ip::tcp::resolver asio_resolver;

        std::string host;
        unsigned short port;
        std::string path;

        SocketClientBase(const std::string& host_port_path, unsigned short default_port) :
                asio_resolver(asio_io_service) {
            size_t host_end=host_port_path.find(':');
            size_t host_port_end=host_port_path.find('/');
            if(host_end==std::string::npos) {
                host_end=host_port_end;
                port=default_port;
            }
            else {
                if(host_port_end==std::string::npos)
                    port=(unsigned short)stoul(host_port_path.substr(host_end+1));
                else
                    port=(unsigned short)stoul(host_port_path.substr(host_end+1, host_port_end-(host_end+1)));
            }
            if(host_port_end==std::string::npos) {
                path="/";
            }
            else {
                path=host_port_path.substr(host_port_end);
            }
            if(host_end==std::string::npos)
                host=host_port_path;
            else
                host=host_port_path.substr(0, host_end);
        }

        virtual void connect()=0;

        void handshake() {
            connection->read_remote_endpoint_data();

            std::shared_ptr<boost::asio::streambuf> write_buffer(new boost::asio::streambuf);

            std::ostream request(write_buffer.get());

            request << "GET " << path << " HTTP/1.1" << "\r\n";
            request << "Host: " << host << "\r\n";
            request << "Upgrade: websocket\r\n";
            request << "Connection: Upgrade\r\n";

            //Make random 16-byte nonce
            std::string nonce;
            nonce.resize(16);
            std::uniform_int_distribution<int> dist(0,255);
            std::random_device rd;
            for(int c=0;c<16;c++)
                nonce[c]=static_cast<unsigned char>(dist(rd));

            std::string nonce_base64=Crypto::Base64::encode(nonce);
            request << "Sec-WebSocket-Key: " << nonce_base64 << "\r\n";
            request << "Sec-WebSocket-Version: 13\r\n";
            request << "\r\n";

            //test this to base64::decode(Sec-WebSocket-Accept)
            std::shared_ptr<std::string> accept_sha1(new std::string(Crypto::SHA1(nonce_base64+ws_magic_string)));

            boost::asio::async_write(*connection->socket, *write_buffer,
                    [this, write_buffer, accept_sha1]
                    (const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                if(!ec) {
                    std::shared_ptr<Message> message(new Message());

                    boost::asio::async_read_until(*connection->socket, message->streambuf, "\r\n\r\n",
                            [this, message, accept_sha1]
                            (const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                        if(!ec) {
                            parse_handshake(*message);
                            if(Crypto::Base64::decode(connection->header["Sec-WebSocket-Accept"])==*accept_sha1) {
                                if(onopen)
                                    onopen();
                                read_message(message);
                            }
                            else
                                throw std::invalid_argument("WebSocket handshake failed");
                        }
                    });
                }
                else
                    throw std::invalid_argument("Failed sending handshake");
            });
        }

        void parse_handshake(std::istream& stream) const {
            std::string line;
            getline(stream, line);
            //Not parsing the first line

            getline(stream, line);
            size_t param_end=line.find(':');
            while(param_end!=std::string::npos) {
                size_t value_start=param_end+1;
                if(line[value_start]==' ')
                    value_start++;

                connection->header[line.substr(0, param_end)]=line.substr(value_start, line.size()-value_start-1);

                getline(stream, line);
                param_end=line.find(':');
            }
        }

        void read_message(std::shared_ptr<Message> message) {
            boost::asio::async_read(*connection->socket, message->streambuf, boost::asio::transfer_exactly(2),
                    [this, message](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                if(!ec) {
                    std::vector<unsigned char> first_bytes;
                    first_bytes.resize(2);
                    message->read((char*)&first_bytes[0], 2);

                    message->fin_rsv_opcode=first_bytes[0];

                    //Close connection if masked message from server (protocol error)
                    if(first_bytes[1]>=128) {
                        const std::string reason="message from server masked";
                        send_close(1002, reason);
                        if(onclose)
                            onclose(1002, reason);
                        return;
                    }

                    size_t length=(first_bytes[1]&127);

                    if(length==126) {
                        //2 next bytes is the size of content
                        boost::asio::async_read(*connection->socket, message->streambuf, boost::asio::transfer_exactly(2),
                                [this, message]
                                (const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                            if(!ec) {
                                std::vector<unsigned char> length_bytes;
                                length_bytes.resize(2);
                                message->read((char*)&length_bytes[0], 2);

                                size_t length=0;
                                int num_bytes=2;
                                for(int c=0;c<num_bytes;c++)
                                    length+=length_bytes[c]<<(8*(num_bytes-1-c));

                                message->length=length;
                                read_message_content(message);
                            }
                            else {
                                if(onerror)
                                    onerror(ec);
                            }
                        });
                    }
                    else if(length==127) {
                        //8 next bytes is the size of content
                        boost::asio::async_read(*connection->socket, message->streambuf, boost::asio::transfer_exactly(8),
                                [this, message]
                                (const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                            if(!ec) {
                                std::vector<unsigned char> length_bytes;
                                length_bytes.resize(8);
                                message->read((char*)&length_bytes[0], 8);

                                size_t length=0;
                                int num_bytes=8;
                                for(int c=0;c<num_bytes;c++)
                                    length+=length_bytes[c]<<(8*(num_bytes-1-c));

                                message->length=length;
                                read_message_content(message);
                            }
                            else {
                                if(onerror)
                                    onerror(ec);
                            }
                        });
                    }
                    else {
                        message->length=length;
                        read_message_content(message);
                    }
                }
                else {
                    if(onerror)
                        onerror(ec);
                }
            });
        }

        void read_message_content(std::shared_ptr<Message> message) {
            boost::asio::async_read(*connection->socket, message->streambuf, boost::asio::transfer_exactly(message->length),
                    [this, message]
                    (const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                if(!ec) {
                    //If connection close
                    if((message->fin_rsv_opcode&0x0f)==8) {
                        int status=0;
                        if(message->length>=2) {
                            unsigned char byte1=message->get();
                            unsigned char byte2=message->get();
                            status=(byte1<<8)+byte2;
                        }

                        auto reason=message->string();

                        send_close(status, reason);
                        if(onclose)
                            onclose(status, reason);
                        return;
                    }
                    //If ping
                    else if((message->fin_rsv_opcode&0x0f)==9) {
                        //send pong
                        auto empty_send_stream=std::make_shared<SendStream>();
                        send(empty_send_stream, nullptr, message->fin_rsv_opcode+1);
                    }
                    else if(onmessage) {
                        onmessage(message);
                    }

                    //Next message
                    std::shared_ptr<Message> next_message(new Message());
                    read_message(next_message);
                }
                else {
                    if(onerror)
                        onerror(ec);
                }
            });
        }
    };

    template<class socket_type>
    class SocketClient : public SocketClientBase<socket_type> {};

    typedef boost::asio::ip::tcp::socket WS;

    template<>
    class SocketClient<WS> : public SocketClientBase<WS> {
    public:
        SocketClient(const std::string& server_port_path) : SocketClientBase<WS>::SocketClientBase(server_port_path, 80) {};

    private:
        void connect() {
            boost::asio::ip::tcp::resolver::query query(host, std::to_string(port));

            asio_resolver.async_resolve(query, [this]
                    (const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator it){
                if(!ec) {
                    connection=std::unique_ptr<Connection>(new Connection(new WS(asio_io_service)));

                    boost::asio::async_connect(*connection->socket, it, [this]
                            (const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator it){
                        if(!ec) {
                            handshake();
                        }
                        else
                            throw std::invalid_argument(ec.message());
                    });
                }
                else
                    throw std::invalid_argument(ec.message());
            });
        }
    };
}

#endif	/* CLIENT_WS_HPP */
<commit_msg>Minor changes to #15.<commit_after>#ifndef CLIENT_WS_HPP
#define	CLIENT_WS_HPP

#include "crypto.hpp"

#include <boost/asio.hpp>

#include <unordered_map>
#include <iostream>
#include <random>
#include <atomic>

namespace SimpleWeb {
    template <class socket_type>
    class SocketClient;

    template <class socket_type>
    class SocketClientBase {
    public:
        class Connection {
            friend class SocketClientBase<socket_type>;
            friend class SocketClient<socket_type>;

        public:
            std::unordered_map<std::string, std::string> header;
            std::string remote_endpoint_address;
            unsigned short remote_endpoint_port;

            Connection(socket_type* socket): socket(socket), closed(false) {}
        private:
            std::unique_ptr<socket_type> socket;

            std::atomic<bool> closed;

            void read_remote_endpoint_data() {
                try {
                    remote_endpoint_address=socket->lowest_layer().remote_endpoint().address().to_string();
                    remote_endpoint_port=socket->lowest_layer().remote_endpoint().port();
                }
                catch(const std::exception& e) {
                    std::cerr << e.what() << std::endl;
                }
            }
        };

        std::unique_ptr<Connection> connection;

        class Message : public std::istream {
            friend class SocketClientBase<socket_type>;

        public:
            unsigned char fin_rsv_opcode;
            size_t size() {
                return length;
            }
            std::string string() {
                std::stringstream ss;
                ss << rdbuf();
                return ss.str();
            }
        private:
            Message(): std::istream(&streambuf) {}
            size_t length;
            boost::asio::streambuf streambuf;
        };

        class SendStream : public std::iostream {
            friend class SocketClientBase<socket_type>;
        private:
            boost::asio::streambuf streambuf;
            std::atomic<bool> sending; //Currently not in use, but might be in a future version
        public:
            SendStream(): std::iostream(&streambuf), sending(false) {}
            size_t size() {
                return streambuf.size();
            }
        };

        std::function<void(void)> onopen;
        std::function<void(std::shared_ptr<Message>)> onmessage;
        std::function<void(const boost::system::error_code&)> onerror;
        std::function<void(int, const std::string&)> onclose;

        void start() {
            connect();

            asio_io_service.run();
        }

        void stop() {
            asio_io_service.stop();
        }

        void send(std::shared_ptr<SendStream> send_stream, const std::function<void(const boost::system::error_code&)>& callback=nullptr,
                        unsigned char fin_rsv_opcode=129) {
            //Create mask
            std::vector<unsigned char> mask;
            mask.resize(4);
            std::uniform_int_distribution<unsigned short> dist(0,255);
            std::random_device rd;
            for(int c=0;c<4;c++) {
                mask[c]=static_cast<unsigned char>(dist(rd));
            }

            std::shared_ptr<boost::asio::streambuf> buffer(new boost::asio::streambuf);
            std::ostream stream(buffer.get());

            size_t length=send_stream->size();

            stream.put(fin_rsv_opcode);
            //masked (first length byte>=128)
            if(length>=126) {
                int num_bytes;
                if(length>0xffff) {
                    num_bytes=8;
                    stream.put(static_cast<unsigned char>(127+128));
                }
                else {
                    num_bytes=2;
                    stream.put(static_cast<unsigned char>(126+128));
                }

                for(int c=num_bytes-1;c>=0;c--) {
                    stream.put((length>>(8*c))%256);
                }
            }
            else
                stream.put(static_cast<unsigned char>(length+128));

            for(int c=0;c<4;c++) {
                stream.put(mask[c]);
            }

            for(size_t c=0;c<length;c++) {
                stream.put(send_stream->get()^mask[c%4]);
            }

            //Need to copy the callback-function in case its destroyed
            boost::asio::async_write(*connection->socket, *buffer,
                    [this, buffer, callback](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                if(callback) {
                    callback(ec);
                }
            });
        }

        void send_close(int status, const std::string& reason="") {
            //Send close only once (in case close is initiated by client)
            if(connection->closed.load()) {
                return;
            }
            connection->closed.store(true);

            auto send_stream=std::make_shared<SendStream>();

            send_stream->put(status>>8);
            send_stream->put(status%256);

            *send_stream << reason;

            //fin_rsv_opcode=136: message close
            send(send_stream, [](const boost::system::error_code& /*ec*/){}, 136);
        }

    protected:
        const std::string ws_magic_string="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

        boost::asio::io_service asio_io_service;
        boost::asio::ip::tcp::endpoint asio_endpoint;
        boost::asio::ip::tcp::resolver asio_resolver;

        std::string host;
        unsigned short port;
        std::string path;

        SocketClientBase(const std::string& host_port_path, unsigned short default_port) :
                asio_resolver(asio_io_service) {
            size_t host_end=host_port_path.find(':');
            size_t host_port_end=host_port_path.find('/');
            if(host_end==std::string::npos) {
                host_end=host_port_end;
                port=default_port;
            }
            else {
                if(host_port_end==std::string::npos)
                    port=(unsigned short)stoul(host_port_path.substr(host_end+1));
                else
                    port=(unsigned short)stoul(host_port_path.substr(host_end+1, host_port_end-(host_end+1)));
            }
            if(host_port_end==std::string::npos) {
                path="/";
            }
            else {
                path=host_port_path.substr(host_port_end);
            }
            if(host_end==std::string::npos)
                host=host_port_path;
            else
                host=host_port_path.substr(0, host_end);
        }

        virtual void connect()=0;

        void handshake() {
            connection->read_remote_endpoint_data();

            std::shared_ptr<boost::asio::streambuf> write_buffer(new boost::asio::streambuf);

            std::ostream request(write_buffer.get());

            request << "GET " << path << " HTTP/1.1" << "\r\n";
            request << "Host: " << host << "\r\n";
            request << "Upgrade: websocket\r\n";
            request << "Connection: Upgrade\r\n";

            //Make random 16-byte nonce
            std::string nonce;
            nonce.resize(16);
            std::uniform_int_distribution<unsigned short> dist(0,255);
            std::random_device rd;
            for(int c=0;c<16;c++)
                nonce[c]=static_cast<unsigned char>(dist(rd));

            std::string nonce_base64=Crypto::Base64::encode(nonce);
            request << "Sec-WebSocket-Key: " << nonce_base64 << "\r\n";
            request << "Sec-WebSocket-Version: 13\r\n";
            request << "\r\n";

            //test this to base64::decode(Sec-WebSocket-Accept)
            std::shared_ptr<std::string> accept_sha1(new std::string(Crypto::SHA1(nonce_base64+ws_magic_string)));

            boost::asio::async_write(*connection->socket, *write_buffer,
                    [this, write_buffer, accept_sha1]
                    (const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                if(!ec) {
                    std::shared_ptr<Message> message(new Message());

                    boost::asio::async_read_until(*connection->socket, message->streambuf, "\r\n\r\n",
                            [this, message, accept_sha1]
                            (const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                        if(!ec) {
                            parse_handshake(*message);
                            if(Crypto::Base64::decode(connection->header["Sec-WebSocket-Accept"])==*accept_sha1) {
                                if(onopen)
                                    onopen();
                                read_message(message);
                            }
                            else
                                throw std::invalid_argument("WebSocket handshake failed");
                        }
                    });
                }
                else
                    throw std::invalid_argument("Failed sending handshake");
            });
        }

        void parse_handshake(std::istream& stream) const {
            std::string line;
            getline(stream, line);
            //Not parsing the first line

            getline(stream, line);
            size_t param_end=line.find(':');
            while(param_end!=std::string::npos) {
                size_t value_start=param_end+1;
                if(line[value_start]==' ')
                    value_start++;

                connection->header[line.substr(0, param_end)]=line.substr(value_start, line.size()-value_start-1);

                getline(stream, line);
                param_end=line.find(':');
            }
        }

        void read_message(std::shared_ptr<Message> message) {
            boost::asio::async_read(*connection->socket, message->streambuf, boost::asio::transfer_exactly(2),
                    [this, message](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                if(!ec) {
                    std::vector<unsigned char> first_bytes;
                    first_bytes.resize(2);
                    message->read((char*)&first_bytes[0], 2);

                    message->fin_rsv_opcode=first_bytes[0];

                    //Close connection if masked message from server (protocol error)
                    if(first_bytes[1]>=128) {
                        const std::string reason="message from server masked";
                        send_close(1002, reason);
                        if(onclose)
                            onclose(1002, reason);
                        return;
                    }

                    size_t length=(first_bytes[1]&127);

                    if(length==126) {
                        //2 next bytes is the size of content
                        boost::asio::async_read(*connection->socket, message->streambuf, boost::asio::transfer_exactly(2),
                                [this, message]
                                (const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                            if(!ec) {
                                std::vector<unsigned char> length_bytes;
                                length_bytes.resize(2);
                                message->read((char*)&length_bytes[0], 2);

                                size_t length=0;
                                int num_bytes=2;
                                for(int c=0;c<num_bytes;c++)
                                    length+=length_bytes[c]<<(8*(num_bytes-1-c));

                                message->length=length;
                                read_message_content(message);
                            }
                            else {
                                if(onerror)
                                    onerror(ec);
                            }
                        });
                    }
                    else if(length==127) {
                        //8 next bytes is the size of content
                        boost::asio::async_read(*connection->socket, message->streambuf, boost::asio::transfer_exactly(8),
                                [this, message]
                                (const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                            if(!ec) {
                                std::vector<unsigned char> length_bytes;
                                length_bytes.resize(8);
                                message->read((char*)&length_bytes[0], 8);

                                size_t length=0;
                                int num_bytes=8;
                                for(int c=0;c<num_bytes;c++)
                                    length+=length_bytes[c]<<(8*(num_bytes-1-c));

                                message->length=length;
                                read_message_content(message);
                            }
                            else {
                                if(onerror)
                                    onerror(ec);
                            }
                        });
                    }
                    else {
                        message->length=length;
                        read_message_content(message);
                    }
                }
                else {
                    if(onerror)
                        onerror(ec);
                }
            });
        }

        void read_message_content(std::shared_ptr<Message> message) {
            boost::asio::async_read(*connection->socket, message->streambuf, boost::asio::transfer_exactly(message->length),
                    [this, message]
                    (const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                if(!ec) {
                    //If connection close
                    if((message->fin_rsv_opcode&0x0f)==8) {
                        int status=0;
                        if(message->length>=2) {
                            unsigned char byte1=message->get();
                            unsigned char byte2=message->get();
                            status=(byte1<<8)+byte2;
                        }

                        auto reason=message->string();

                        send_close(status, reason);
                        if(onclose)
                            onclose(status, reason);
                        return;
                    }
                    //If ping
                    else if((message->fin_rsv_opcode&0x0f)==9) {
                        //send pong
                        auto empty_send_stream=std::make_shared<SendStream>();
                        send(empty_send_stream, nullptr, message->fin_rsv_opcode+1);
                    }
                    else if(onmessage) {
                        onmessage(message);
                    }

                    //Next message
                    std::shared_ptr<Message> next_message(new Message());
                    read_message(next_message);
                }
                else {
                    if(onerror)
                        onerror(ec);
                }
            });
        }
    };

    template<class socket_type>
    class SocketClient : public SocketClientBase<socket_type> {};

    typedef boost::asio::ip::tcp::socket WS;

    template<>
    class SocketClient<WS> : public SocketClientBase<WS> {
    public:
        SocketClient(const std::string& server_port_path) : SocketClientBase<WS>::SocketClientBase(server_port_path, 80) {};

    private:
        void connect() {
            boost::asio::ip::tcp::resolver::query query(host, std::to_string(port));

            asio_resolver.async_resolve(query, [this]
                    (const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator it){
                if(!ec) {
                    connection=std::unique_ptr<Connection>(new Connection(new WS(asio_io_service)));

                    boost::asio::async_connect(*connection->socket, it, [this]
                            (const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator it){
                        if(!ec) {
                            handshake();
                        }
                        else
                            throw std::invalid_argument(ec.message());
                    });
                }
                else
                    throw std::invalid_argument(ec.message());
            });
        }
    };
}

#endif	/* CLIENT_WS_HPP */
<|endoftext|>


<commit_before>#include <TSystem.h>
#include <TFile.h>
#include <TStyle.h>
#include <TTree.h>
#include <TClonesArray.h>
#include <TObjArray.h>
#include <TH2F.h>
#include <TCanvas.h>

#include <AliHMPIDHit.h>
#include <AliHMPIDCluster.h>
#include <AliHMPIDDigit.h>
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TObjArray *CreateContainer(const char *classname,TTree *pTree)
{
  TObjArray *pOA=new TObjArray(7); pOA->SetOwner();
  for(Int_t iCh=AliHMPIDParam::kMinCh;iCh<=AliHMPIDParam::kMaxCh;iCh++){
    TClonesArray *pCA=new TClonesArray(classname);
    pOA->AddAt(pCA,iCh);
    pTree->SetBranchAddress(Form("HMPID%i",iCh),&pCA);
  }
  return pOA;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TH1F *hHitQdc; TH2F *hHitMap[7];
void Hits(Int_t mode,TTree *pTree=0x0)
{
  switch(mode){
    case 1:
      hHitQdc=new TH1F("HitQdc","Hit Qdc all chamber;QDC",500,0,4000);
      for(Int_t iCh=0;iCh<7;iCh++) hHitMap[iCh]=new TH2F(Form("HitMap%i",iCh),Form("Ch%i;x_{Hit};y_{Hit}",iCh),160,0,160,160,0,160);
      break;
    case 2:
      if(pTree==0) return;
      TClonesArray *pHits=new TClonesArray("AliHMPIDHit");  pTree->SetBranchAddress("HMPID",&pHits);
      for(Int_t iEnt=0;iEnt<pTree->GetEntriesFast();iEnt++){//entries loop
        pTree->GetEntry(iEnt);
        for(Int_t iHit=0;iHit<pHits->GetEntriesFast();iHit++){//hits loop
          AliHMPIDHit *pHit = (AliHMPIDHit*)pHits->UncheckedAt(iHit);
          hHitMap[pHit->Ch()]->Fill(pHit->LorsX(),pHit->LorsY());
          hHitQdc->Fill(pHit->Q());
        }//hits loop
      }//entries loop
      delete pHits;
      break;
    case 3:
      TCanvas *c1=new TCanvas("HitCan","Hits",1280,800); c1->Divide(3,3);

      for(Int_t iCh=0;iCh<7;iCh++){
        if(iCh==6) c1->cd(1); if(iCh==5) c1->cd(2);
        if(iCh==4) c1->cd(4); if(iCh==3) c1->cd(5); if(iCh==2) c1->cd(6);
                              if(iCh==1) c1->cd(8); if(iCh==0) c1->cd(9);
        gStyle->SetPalette(1);
        hHitMap[iCh]->Draw("colz");
      }
      c1->cd(3); gPad->SetLogy(); hHitQdc->SetFillColor(5);hHitQdc->Draw();
      break;
  }
}//Hits()
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TH1F *hCluEvt,*hCluChi2,*hCluFlg,*hCluSize;
void Clus(Int_t mode, TTree *pTree=0x0)
{
  switch(mode){
    case 1:
      hCluEvt=new TH1F("CluPerEvt","# clusters per event",21,-0.5,20.5);
      hCluChi2  =new TH1F("CluChi2"  ,"Chi2 "               ,1000,0,100);
      hCluFlg   =new TH1F("CluFlg"   ,"Cluster flag"        ,14,-1.5,12.5);                       hCluFlg->SetFillColor(5);
      hCluSize  =new TH1F("CluSize"  ,"Raw cluster size    ",100,0,100);
      break;
    case 2:
      if(pTree==0) return;
      TObjArray *pLst=CreateContainer("AliHMPIDCluster",pTree); pTree->GetEntry(0);
      for(Int_t iCh=AliHMPIDParam::kMinCh;iCh<=AliHMPIDParam::kMaxCh;iCh++){//chambers loop
        TClonesArray *pClus=(TClonesArray *)pLst->UncheckedAt(iCh);
        hCluEvt->Fill(pClus->GetEntriesFast());
        for(Int_t iClu=0;iClu<pClus->GetEntriesFast();iClu++){//clusters loop
          AliHMPIDCluster *pClu=(AliHMPIDCluster*)pClus->UncheckedAt(iClu);
          hCluFlg->Fill(pClu->Status());  hCluChi2->Fill(pClu->Chi2());  hCluSize->Fill(pClu->Size());
        }
      }
      delete pLst;
      break;
    case 3:
      TCanvas *c1=new TCanvas("CluComCan","Clusters in common",1280,800); c1->Divide(3,3);
      c1->cd(1); hCluEvt->SetFillColor(5);    hCluEvt->Draw();
      c1->cd(2); hCluChi2->SetFillColor(5);     hCluChi2->Draw();
      c1->cd(3); hCluFlg->SetFillColor(5);      hCluFlg->Draw();
      c1->cd(4); hCluSize->SetFillColor(5);     hCluSize->Draw();
      break;
  }//switch
}//Clus()
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TH1F *hDigPcEvt,*hDigQ, *hDigChEvt;
void Digs(Int_t mode, TTree *pTree=0x0)
{
  switch(mode){
    case 1:
      hDigPcEvt=new TH1F("hDigPcEvt","PC occupancy per event",156,-1,77);
      hDigChEvt=new TH1F("hDigChEvt","Chamber occupancy per event",32,-1,7);
      hDigQ  =new TH1F("Q        ","Q                     ",3000,0,3000);
      break;
    case 2:
      if(pTree==0) return;
      TObjArray *pLst=CreateContainer("AliHMPIDDigit",pTree); pTree->GetEntry(0);
      for(Int_t iCh=AliHMPIDParam::kMinCh;iCh<=AliHMPIDParam::kMaxCh;iCh++){//chambers loop
        TClonesArray *pDigs=(TClonesArray *)pLst->UncheckedAt(iCh);
        hDigChEvt->Fill(iCh,pDigs->GetEntriesFast()/(48.*80.*6.));
        for(Int_t iDig=0;iDig<pDigs->GetEntriesFast();iDig++){//digits loop
          AliHMPIDDigit *pDig=(AliHMPIDDigit*)pDigs->UncheckedAt(iDig);
          hDigPcEvt->Fill(10.*iCh+pDig->Pc(),1./(48.*80.));
          hDigQ->Fill(pDig->Q());
        }
      }
      delete pLst;
      break;
    case 3:
      TCanvas *c1=new TCanvas("DigQa","Digit Check",1280,800); c1->Divide(2,2);
      c1->cd(1); hDigPcEvt->Draw();       c1->cd(2); hDigQ->Draw(); c1->cd(3); hDigChEvt->Draw();
      break;
  }//switch
}//Dig()

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void Hqa()
{

  TFile *fh=0; if(gSystem->IsFileInIncludePath("HMPID.Hits.root"))      fh=TFile::Open("HMPID.Hits.root"     ,"read");if(fh->IsZombie()) fh=0;
  TFile *fd=0; if(gSystem->IsFileInIncludePath("HMPID.Digits.root"))    fd=TFile::Open("HMPID.Digits.root"   ,"read");if(fd->IsZombie()) fd=0;
  TFile *fc=0; if(gSystem->IsFileInIncludePath("HMPID.RecPoints.root")) fc=TFile::Open("HMPID.RecPoints.root","read");if(fc->IsZombie()) fc=0;
  if(fh==0 && fd==0 && fc==0){Printf("Nothing to do!"); return;}
  if(fh) Hits(1); if(fc) Clus(1);  if(fd) Digs(1);//book
  Int_t iEvt=0;
  while(1){
    TTree *th=0; if(fh) th=(TTree*)fh->Get(Form("Event%i/TreeH",iEvt));
    TTree *td=0; if(fd) td=(TTree*)fd->Get(Form("Event%i/TreeD",iEvt));
    TTree *tc=0; if(fc) tc=(TTree*)fc->Get(Form("Event%i/TreeR",iEvt));
    Hits(2,th);   Clus(2,tc); Digs(2,td);//fill
    if(th==0 && td==0 && tc==0) break;
    iEvt++;
    Printf("Event %i processed",iEvt);
  }
  if(fh) Hits(3);
  if(fc) Clus(3);
  if(fd) Digs(3);//plot
}

<commit_msg>More checks<commit_after>#if !defined(__CINT__) || defined(__MAKECINT__)
#include <TSystem.h>
#include <TFile.h>
#include <TStyle.h>
#include <TTree.h>
#include <TClonesArray.h>
#include <TObjArray.h>
#include <TF1.h>
#include <TH1F.h>
#include <TH2F.h>
#include <TProfile.h>
#include <TCanvas.h>

#include <AliHMPIDParam.h>
#include <AliHMPIDHit.h>
#include <AliHMPIDCluster.h>
#include <AliHMPIDDigit.h>
#endif

Int_t nEntries = 0;

TObjArray *CreateContainer(const char *classname,TTree *pTree);
void Hits(Int_t mode,TTree *pTree=0x0);
void Digs(Int_t mode, TTree *pTree=0x0);
void Clus(Int_t mode, TTree *pTree=0x0);
void Summary();

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TObjArray *CreateContainer(const char *classname,TTree *pTree)
{
  TObjArray *pOA=new TObjArray(AliHMPIDParam::kMaxCh+1);
  for(Int_t iCh=AliHMPIDParam::kMinCh;iCh<=AliHMPIDParam::kMaxCh;iCh++){
    TClonesArray *pCA=new TClonesArray(classname);
    pOA->AddAt(pCA,iCh);
  }

  pOA->SetOwner(kTRUE);

  for(Int_t iCh=AliHMPIDParam::kMinCh;iCh<=AliHMPIDParam::kMaxCh;iCh++){
    pTree->SetBranchAddress(Form("HMPID%i",iCh),&(*pOA)[iCh]);
  }
  return pOA;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TH1F *hHitQdc,*hHitQdcCh[AliHMPIDParam::kMaxCh+1]; TH2F *hHitMap[AliHMPIDParam::kMaxCh+1];
Double_t fHitMean[AliHMPIDParam::kMaxCh+1],fHitErr[AliHMPIDParam::kMaxCh+1];
void Hits(Int_t mode,TTree *pTree)
{
  switch(mode){
    case 1:
      hHitQdc=new TH1F("HitQdc","Hit Qdc all chamber;QDC",4000,0,4000);
  for(Int_t iCh=AliHMPIDParam::kMinCh;iCh<=AliHMPIDParam::kMaxCh;iCh++){
        hHitMap[iCh]=new TH2F(Form("HitMap%i",iCh),Form("Ch%i;x_{Hit};y_{Hit}",iCh),160,0,160,160,0,160);
        hHitQdcCh[iCh]=new TH1F(Form("HMPID%i",iCh),Form("Charge for HMPID%i",iCh),4000,0,4000);
      }
      break;
    case 2:
      if(pTree==0) return;
      TClonesArray *pHits=new TClonesArray("AliHMPIDHit");  pTree->SetBranchAddress("HMPID",&pHits);
      for(Int_t iEnt=0;iEnt<pTree->GetEntriesFast();iEnt++){//entries loop
        pTree->GetEntry(iEnt);
        for(Int_t iHit=0;iHit<pHits->GetEntriesFast();iHit++){//hits loop
          AliHMPIDHit *pHit = (AliHMPIDHit*)pHits->UncheckedAt(iHit);
          hHitMap[pHit->Ch()]->Fill(pHit->LorsX(),pHit->LorsY());
          hHitQdc->Fill(pHit->Q());
          hHitQdcCh[pHit->Ch()]->Fill(pHit->Q());
        }//hits loop
      }//entries loop
      delete pHits;
      break;
    case 3:
      TCanvas *c1=new TCanvas("HitCan","Hits",1280,800); c1->Divide(3,3);

      for(Int_t iCh=AliHMPIDParam::kMinCh;iCh<=AliHMPIDParam::kMaxCh;iCh++){
        if(iCh==6) c1->cd(1); if(iCh==5) c1->cd(2);
        if(iCh==4) c1->cd(4); if(iCh==3) c1->cd(5); if(iCh==2) c1->cd(6);
                              if(iCh==1) c1->cd(8); if(iCh==0) c1->cd(9);
        gStyle->SetPalette(1);
        hHitMap[iCh]->Draw("colz");
      }
      for(Int_t iCh=AliHMPIDParam::kMinCh;iCh<=AliHMPIDParam::kMaxCh;iCh++){
        fHitMean[iCh] = 0;
        fHitErr[iCh]  = 0;
        if((Int_t)hHitQdcCh[iCh]->GetEntries()<nEntries) continue;
        c1->cd(3);hHitQdcCh[iCh]->Fit("expo","Q");
        TF1 *funcfit = (TF1*)hHitQdcCh[iCh]->FindObject("expo");
        fHitMean[iCh] = funcfit->GetParameter(1);
        fHitErr[iCh]  = funcfit->GetParError(1);
      }
      TPad *pad = (TPad*)c1->cd(3); hHitQdc->SetFillColor(5); pad->SetLogy(); hHitQdc->Fit("expo","Q");
      break;
  }
}//Hits()
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TH1F     *hDigQ;
TProfile *hDigHighQ,*hDigChEvt;
void Digs(Int_t mode, TTree *pTree)
{
  switch(mode){
    case 1:
      hDigHighQ=new TProfile("hDigHighQ","Highest charge in chamber  ",AliHMPIDParam::kMaxCh+1,AliHMPIDParam::kMinCh,AliHMPIDParam::kMaxCh+1);
      hDigChEvt=new TProfile("hDigChEvt","Chamber occupancy per event",AliHMPIDParam::kMaxCh+1,AliHMPIDParam::kMinCh,AliHMPIDParam::kMaxCh+1);
      hDigQ    =new TH1F("hDigQ        ","Charge of digits (ADC)     ",3000,0,3000);
      break;
    case 2:
      if(pTree==0) return;
      TObjArray *pLst=CreateContainer("AliHMPIDDigit",pTree); pTree->GetEntry(0);
      for(Int_t iCh=AliHMPIDParam::kMinCh;iCh<=AliHMPIDParam::kMaxCh;iCh++){//chambers loop
        TClonesArray *pDigs=(TClonesArray *)pLst->UncheckedAt(iCh);
        hDigChEvt->Fill(iCh,pDigs->GetEntriesFast()/(48.*80.*6.)*100.);
        Double_t highQ = 0;
        for(Int_t iDig=0;iDig<pDigs->GetEntriesFast();iDig++){//digits loop
          AliHMPIDDigit *pDig=(AliHMPIDDigit*)pDigs->UncheckedAt(iDig);
          hDigQ->Fill(pDig->Q());
          if(pDig->Q()>highQ) highQ = pDig->Q();
        }
        hDigHighQ->Fill(iCh,highQ);
      }
      delete pLst;
      break;
    case 3:
      TCanvas *c1=new TCanvas("DigQa","Digit Check",1280,800); c1->Divide(2,2);
      gStyle->SetOptFit(1);
      TPad *pad = (TPad*)c1->cd(1); pad->SetLogy(); hDigQ->Fit("expo","QN");
      c1->cd(2); hDigHighQ->Draw();
      c1->cd(3); hDigChEvt->Draw();
      break;
  }//switch
}//Dig()
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TH1F *hCluEvt,*hCluChi2,*hCluFlg,*hCluSize,*hCluQ;
void Clus(Int_t mode, TTree *pTree)
{
  switch(mode){
    case 1:
      hCluEvt=new TH1F("CluPerEvt","Cluster multiplicity"   ,100,0,100);
      hCluChi2  =new TH1F("CluChi2"  ,"Chi2 "               ,1000,0,100);
      hCluFlg   =new TH1F("CluFlg"   ,"Cluster flag"        ,14,-1.5,12.5);                       hCluFlg->SetFillColor(5);
      hCluSize  =new TH1F("CluSize"  ,"Cluster size        ",100,0,100);
      hCluQ     =new TH1F("CluQ"     ,"Cluster charge (ADC)",1000,0,5000);
      break;
    case 2:
      if(pTree==0) return;

      TObjArray *pLst=CreateContainer("AliHMPIDCluster",pTree); pTree->GetEntry(0);
      for(Int_t iCh=AliHMPIDParam::kMinCh;iCh<=AliHMPIDParam::kMaxCh;iCh++){//chambers loop
        TClonesArray *pClus=(TClonesArray *)pLst->UncheckedAt(iCh);
        hCluEvt->Fill(pClus->GetEntriesFast());
        for(Int_t iClu=0;iClu<pClus->GetEntriesFast();iClu++){//clusters loop
          AliHMPIDCluster *pClu=(AliHMPIDCluster*)pClus->UncheckedAt(iClu);
          hCluFlg->Fill(pClu->Status());
          hCluChi2->Fill(pClu->Chi2());
          hCluSize->Fill(pClu->Size());
          hCluQ->Fill(pClu->Q());
        }
      }
      delete pLst;
      break;
    case 3:
      TCanvas *c1=new TCanvas("CluComCan","Clusters in common",1280,800); c1->Divide(3,3);
      c1->cd(1); hCluEvt->SetFillColor(5);      hCluEvt->Draw();
      c1->cd(2); hCluChi2->SetFillColor(5);     hCluChi2->Draw();
      c1->cd(3); hCluFlg->SetFillColor(5);      hCluFlg->Draw();
      c1->cd(4); hCluSize->SetFillColor(5);     hCluSize->Draw();
      TPad *pad = (TPad*)c1->cd(5); hCluQ->SetFillColor(5); pad->SetLogy(); hCluQ->Draw();
      break;
  }//switch
}//Clus()
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void Hqa()
{
  TFile *fh=0; if(gSystem->IsFileInIncludePath("HMPID.Hits.root"))      fh=TFile::Open("HMPID.Hits.root"     ,"read");if(fh->IsZombie()) fh=0;
  TFile *fd=0; if(gSystem->IsFileInIncludePath("HMPID.Digits.root"))    fd=TFile::Open("HMPID.Digits.root"   ,"read");if(fd->IsZombie()) fd=0;
  TFile *fc=0; if(gSystem->IsFileInIncludePath("HMPID.RecPoints.root")) fc=TFile::Open("HMPID.RecPoints.root","read");if(fc->IsZombie()) fc=0;
  if(fh==0 && fd==0 && fc==0){Printf("Nothing to do!"); return;}
  if(fh) Hits(1); if(fc) Clus(1);  if(fd) Digs(1);//book
  Int_t iEvt=0;
  while(1){
    TTree *th=0; if(fh) th=(TTree*)fh->Get(Form("Event%i/TreeH",iEvt));
    TTree *td=0; if(fd) td=(TTree*)fd->Get(Form("Event%i/TreeD",iEvt));
    TTree *tc=0; if(fc) tc=(TTree*)fc->Get(Form("Event%i/TreeR",iEvt));

    Hits(2,th); Digs(2,td); Clus(2,tc); //fill
    if(th==0 && td==0 && tc==0) break;
    iEvt++;
    if(!(iEvt%50)) Printf("Event %i processed",iEvt);
  }

  nEntries = iEvt;

  if(fd) Clus(3);//plot everything
  if(fc) Digs(3);
  if(fh) Hits(3);
  Summary();
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void Summary()
{
  //info for hits...
  for(Int_t iCh=AliHMPIDParam::kMinCh;iCh<=AliHMPIDParam::kMaxCh;iCh++){
    Printf(" #################### Summary for HMPID %i#################### ",iCh);
    //info for hits...
    Printf("-----Summary of Hits----- ");
    if(fHitMean[iCh]==0) {
      Printf("gain %5.2f +/- %5.2f",fHitMean[iCh],fHitErr[iCh]);
    } else {
      Double_t gain = 1./TMath::Abs(fHitMean[iCh]);
      Double_t errgain = gain*gain*fHitErr[iCh];
      Printf("gain %5.2f +/- %5.2f",gain,errgain);
    }
    //info for digits...
    Printf("-----Summary of Digits-----");
    Printf(" Chamber %d with occupancy (%) %5.2f +/- %5.2f",iCh,hDigChEvt->GetBinContent(iCh+1),hDigChEvt->GetBinError(iCh+1));
    Printf(" Chamber %d with higest Q (ADC) %7.0f +/- %7.0f",iCh,hDigHighQ->GetBinContent(iCh+1),hDigHighQ->GetBinError(iCh+1));
  }
}
<|endoftext|>


<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2012 Francois Beaune, Jupiter Jazz Limited
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "latlongmapenvironmentedf.h"

// appleseed.renderer headers.
#include "renderer/global/globallogger.h"
#include "renderer/global/globaltypes.h"
#include "renderer/kernel/lighting/imageimportancesampler.h"
#include "renderer/kernel/texturing/texturecache.h"
#include "renderer/kernel/texturing/texturestore.h"
#include "renderer/modeling/environmentedf/environmentedf.h"
#include "renderer/modeling/input/inputarray.h"
#include "renderer/modeling/input/inputevaluator.h"
#include "renderer/modeling/input/source.h"
#include "renderer/modeling/input/texturesource.h"
#include "renderer/modeling/project/project.h"
#include "renderer/modeling/scene/scene.h"
#include "renderer/modeling/texture/texture.h"
#include "renderer/utility/paramarray.h"

// appleseed.foundation headers.
#include "foundation/image/canvasproperties.h"
#include "foundation/image/color.h"
#include "foundation/image/colorspace.h"
#include "foundation/math/sampling.h"
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/platform/types.h"
#include "foundation/utility/containers/dictionary.h"

// Standard headers.
#include <cassert>
#include <cmath>
#include <cstddef>
#include <memory>

using namespace foundation;
using namespace std;

namespace renderer
{

namespace
{
    //
    // Latitude-longitude environment map EDF.
    //
    // Reference:
    //
    //   http://www.cs.virginia.edu/~gfx/courses/2007/ImageSynthesis/assignments/envsample.pdf
    //
    // Light probes:
    //
    //   http://www.debevec.org/probes/
    //   http://gl.ict.usc.edu/Data/HighResProbes/
    //   http://www.cs.kuleuven.be/~graphics/index.php/environment-maps
    //

    class ImageSampler
    {
      public:
        ImageSampler(
            TextureCache&   texture_cache,
            Source*         source,
            const size_t    width,
            const size_t    height,
            const double    u_shift,
            const double    v_shift)
          : m_texture_cache(texture_cache)
          , m_source(source)
          , m_rcp_width(1.0 / width)
          , m_rcp_height(1.0 / height)
          , m_u_shift(u_shift)
          , m_v_shift(v_shift)
          , m_out_of_range_luminance_error_count(0)
        {
        }

        double operator()(const size_t x, const size_t y)
        {
            if (m_source == 0)
                return 0.0;

            const Vector2d uv(
                x * m_rcp_width + m_u_shift,
                1.0 - y * m_rcp_height + m_v_shift);

            Color3f linear_rgb;
            Alpha alpha;

            m_source->evaluate(
                m_texture_cache,
                uv,
                linear_rgb,
                alpha);

            const double MaxLuminance = 1.0e4;

            double lum = static_cast<double>(luminance(linear_rgb));

            if (lum < 0.0)
            {
                lum = 0.0;
                ++m_out_of_range_luminance_error_count;
            }

            if (lum > MaxLuminance)
            {
                lum = MaxLuminance;
                ++m_out_of_range_luminance_error_count;
            }

            return lum;
        }

        size_t get_out_of_range_luminance_error_count() const
        {
            return m_out_of_range_luminance_error_count;
        }

      private:
        TextureCache&   m_texture_cache;
        Source*         m_source;
        const double    m_rcp_width;
        const double    m_rcp_height;
        const double    m_u_shift;
        const double    m_v_shift;
        size_t          m_out_of_range_luminance_error_count;
    };

    const char* Model = "latlong_map_environment_edf";

    class LatLongMapEnvironmentEDF
      : public EnvironmentEDF
    {
      public:
        LatLongMapEnvironmentEDF(
            const char*         name,
            const ParamArray&   params)
          : EnvironmentEDF(name, params)
          , m_importance_map_width(0)
          , m_importance_map_height(0)
          , m_probability_scale(0.0)
        {
            m_inputs.declare("exitance", InputFormatSpectrum);

            m_u_shift = m_params.get_optional<double>("horizontal_shift", 0.0) / 360.0;
            m_v_shift = m_params.get_optional<double>("vertical_shift", 0.0) / 360.0;
        }

        virtual void release()
        {
            delete this;
        }

        virtual const char* get_model() const
        {
            return Model;
        }

        virtual void on_frame_begin(const Project& project)
        {
            EnvironmentEDF::on_frame_begin(project);

            if (m_importance_sampler.get() == 0)
                build_importance_map(*project.get_scene());
        }

        virtual void sample(
            InputEvaluator&     input_evaluator,
            const Vector2d&     s,
            Vector3d&           outgoing,
            Spectrum&           value,
            double&             probability) const
        {
            // Sample the importance map.
            size_t x, y;
            double prob_xy;
            m_importance_sampler->sample(s, x, y, prob_xy);

            // Compute the coordinates in [0,1]^2 of the sample.
            const double u = (2.0 * x + 1.0) / (2.0 * m_importance_map_width);
            const double v = (2.0 * y + 1.0) / (2.0 * m_importance_map_height);

            double theta, phi;
            unit_square_to_angles(u, v, theta, phi);

            // Compute the world space emission direction.
            outgoing = Vector3d::unit_vector(theta, phi);

            lookup_environment_map(input_evaluator, u, v, value);

            probability = prob_xy * m_probability_scale / sin(theta);
        }

        virtual void evaluate(
            InputEvaluator&     input_evaluator,
            const Vector3d&     outgoing,
            Spectrum&           value) const
        {
            assert(is_normalized(outgoing));

            double theta, phi;
            unit_vector_to_angles(outgoing, theta, phi);

            double u, v;
            angles_to_unit_square(theta, phi, u, v);

            lookup_environment_map(input_evaluator, u, v, value);
        }

        virtual void evaluate(
            InputEvaluator&     input_evaluator,
            const Vector3d&     outgoing,
            Spectrum&           value,
            double&             probability) const
        {
            assert(is_normalized(outgoing));

            double theta, phi;
            unit_vector_to_angles(outgoing, theta, phi);

            double u, v;
            angles_to_unit_square(theta, phi, u, v);

            lookup_environment_map(input_evaluator, u, v, value);

            probability = compute_pdf(u, v, theta);
        }

        virtual double evaluate_pdf(
            InputEvaluator&     input_evaluator,
            const Vector3d&     outgoing) const
        {
            assert(is_normalized(outgoing));

            double theta, phi;
            unit_vector_to_angles(outgoing, theta, phi);

            double u, v;
            angles_to_unit_square(theta, phi, u, v);

            return compute_pdf(u, v, theta);
        }

      private:
        struct InputValues
        {
            Spectrum    m_exitance;
            Alpha       m_exitance_alpha;       // unused
        };

        typedef ImageImportanceSampler<double> ImageImportanceSamplerType;

        double                                  m_u_shift;
        double                                  m_v_shift;

        size_t                                  m_importance_map_width;
        size_t                                  m_importance_map_height;
        double                                  m_probability_scale;
        auto_ptr<ImageImportanceSamplerType>    m_importance_sampler;

        // Compute the spherical coordinates of a given direction.
        static void unit_vector_to_angles(
            const Vector3d&     v,              // unit length
            double&             theta,          // in [0, Pi]
            double&             phi)            // in [-Pi, Pi]
        {
            assert(is_normalized(v));

            theta = acos(v[1]);
            phi = atan2(v[2], v[0]);
        }

        // Convert a given direction from spherical coordinates to [0,1]^2.
        static void angles_to_unit_square(
            const double        theta,          // in [0, Pi]
            const double        phi,            // in [-Pi, Pi]
            double&             u,              // in [0, 1]
            double&             v)              // in [0, 1]
        {
            assert(theta >= 0.0);
            assert(theta <= Pi);
            assert(phi >= -Pi);
            assert(phi <= Pi);

            u = (0.5 / Pi) * (phi + Pi);
            v = (1.0 / Pi) * theta;
        }

        // Convert a given direction from [0,1]^2 to spherical coordinates.
        static void unit_square_to_angles(
            const double        u,          // in [0, 1]
            const double        v,          // in [0, 1]
            double&             theta,      // in [0, Pi]
            double&             phi)        // in [-Pi, Pi]
        {
            assert(u >= 0.0);
            assert(u <= 1.0);
            assert(v >= 0.0);
            assert(v <= 1.0);

            theta = Pi * v;
            phi = Pi * (2.0 * u - 1.0);
        }

        void build_importance_map(const Scene& scene)
        {
            const TextureSource* exitance_source =
                dynamic_cast<const TextureSource*>(m_inputs.source("exitance"));

            if (exitance_source)
            {
                const CanvasProperties& texture_props = exitance_source->get_texture(scene).properties();
                m_importance_map_width = texture_props.m_canvas_width;
                m_importance_map_height = texture_props.m_canvas_height;
            }
            else
            {
                m_importance_map_width = 1;
                m_importance_map_height = 1;
            }

            const size_t texel_count = m_importance_map_width * m_importance_map_height;
            m_probability_scale = texel_count / (2.0 * Pi * Pi);

            TextureStore texture_store(scene);
            TextureCache texture_cache(texture_store);
            ImageSampler sampler(
                texture_cache,
                m_inputs.source("exitance"),
                m_importance_map_width,
                m_importance_map_height,
                m_u_shift,
                m_v_shift);

            RENDERER_LOG_INFO(
                "building " FMT_SIZE_T "x" FMT_SIZE_T " importance map "
                "for environment edf \"%s\"...",
                m_importance_map_width,
                m_importance_map_height,
                get_name());

            m_importance_sampler.reset(
                new ImageImportanceSamplerType(
                    m_importance_map_width,
                    m_importance_map_height,
                    sampler));

            const size_t oor_error_count = sampler.get_out_of_range_luminance_error_count();

            if (oor_error_count > 0)
            {
                RENDERER_LOG_WARNING(
                    "while building importance map for environment edf \"%s\": "
                    "found %s pixel%s with out-of-range luminance; rendering artifacts are to be expected.",
                    get_name(),
                    pretty_uint(oor_error_count).c_str(),
                    oor_error_count > 1 ? "s" : "");
            }

            RENDERER_LOG_INFO(
                "built importance map for environment edf \"%s\".",
                get_name());
        }

        void lookup_environment_map(
            InputEvaluator&     input_evaluator,
            const double        u,
            const double        v,
            Spectrum&           value) const
        {
            const Vector2d uv(u + m_u_shift, 1.0 - v + m_v_shift);

            const InputValues* values =
                input_evaluator.evaluate<InputValues>(m_inputs, uv);

            value = values->m_exitance;
        }

        double compute_pdf(
            const double        u,
            const double        v,
            const double        theta) const
        {
            // Compute the probability density of this sample in the importance map.
            const size_t x = truncate<size_t>(m_importance_map_width * u);
            const size_t y = truncate<size_t>(m_importance_map_height * v);
            const double prob_xy = m_importance_sampler->get_pdf(x, y);

            // Compute the probability density of the emission direction.
            return prob_xy * m_probability_scale / sin(theta);
        }
    };
}

//
// LatLongMapEnvironmentEDFFactory class implementation.
//

const char* LatLongMapEnvironmentEDFFactory::get_model() const
{
    return Model;
}

const char* LatLongMapEnvironmentEDFFactory::get_human_readable_model() const
{
    return "Latitude-Longitude Map Environment EDF";
}

DictionaryArray LatLongMapEnvironmentEDFFactory::get_widget_definitions() const
{
    DictionaryArray definitions;

    definitions.push_back(
        Dictionary()
            .insert("name", "exitance")
            .insert("label", "Exitance")
            .insert("widget", "entity_picker")
            .insert("entity_types",
                Dictionary()
                    .insert("texture_instance", "Textures"))
            .insert("use", "required")
            .insert("default", ""));

    definitions.push_back(
        Dictionary()
            .insert("name", "horizontal_shift")
            .insert("label", "Horizontal Shift")
            .insert("widget", "text_box")
            .insert("default", "0.0")
            .insert("use", "optional"));

    definitions.push_back(
        Dictionary()
            .insert("name", "vertical_shift")
            .insert("label", "Vertical Shift")
            .insert("widget", "text_box")
            .insert("default", "0.0")
            .insert("use", "optional"));

    return definitions;
}

auto_release_ptr<EnvironmentEDF> LatLongMapEnvironmentEDFFactory::create(
    const char*         name,
    const ParamArray&   params) const
{
    return
        auto_release_ptr<EnvironmentEDF>(
            new LatLongMapEnvironmentEDF(name, params));
}

}   // namespace renderer
<commit_msg>code tweaks.<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2012 Francois Beaune, Jupiter Jazz Limited
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "latlongmapenvironmentedf.h"

// appleseed.renderer headers.
#include "renderer/global/globallogger.h"
#include "renderer/global/globaltypes.h"
#include "renderer/kernel/lighting/imageimportancesampler.h"
#include "renderer/kernel/texturing/texturecache.h"
#include "renderer/kernel/texturing/texturestore.h"
#include "renderer/modeling/environmentedf/environmentedf.h"
#include "renderer/modeling/input/inputarray.h"
#include "renderer/modeling/input/inputevaluator.h"
#include "renderer/modeling/input/source.h"
#include "renderer/modeling/input/texturesource.h"
#include "renderer/modeling/project/project.h"
#include "renderer/modeling/scene/scene.h"
#include "renderer/modeling/texture/texture.h"
#include "renderer/utility/paramarray.h"

// appleseed.foundation headers.
#include "foundation/image/canvasproperties.h"
#include "foundation/image/color.h"
#include "foundation/image/colorspace.h"
#include "foundation/math/sampling.h"
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/platform/types.h"
#include "foundation/utility/containers/dictionary.h"

// Standard headers.
#include <cassert>
#include <cmath>
#include <cstddef>
#include <memory>

using namespace foundation;
using namespace std;

namespace renderer
{

namespace
{
    //
    // Latitude-longitude environment map EDF.
    //
    // Reference:
    //
    //   http://www.cs.virginia.edu/~gfx/courses/2007/ImageSynthesis/assignments/envsample.pdf
    //
    // Light probes:
    //
    //   http://www.debevec.org/probes/
    //   http://gl.ict.usc.edu/Data/HighResProbes/
    //   http://www.cs.kuleuven.be/~graphics/index.php/environment-maps
    //

    class ImageSampler
    {
      public:
        ImageSampler(
            TextureCache&   texture_cache,
            const Source*   source,
            const size_t    width,
            const size_t    height,
            const double    u_shift,
            const double    v_shift)
          : m_texture_cache(texture_cache)
          , m_source(source)
          , m_rcp_width(1.0 / width)
          , m_rcp_height(1.0 / height)
          , m_u_shift(u_shift)
          , m_v_shift(v_shift)
          , m_out_of_range_luminance_error_count(0)
        {
        }

        double operator()(const size_t x, const size_t y)
        {
            if (m_source == 0)
                return 0.0;

            const Vector2d uv(
                x * m_rcp_width + m_u_shift,
                1.0 - y * m_rcp_height + m_v_shift);

            Color3f linear_rgb;
            Alpha alpha;

            m_source->evaluate(
                m_texture_cache,
                uv,
                linear_rgb,
                alpha);

            const double MaxLuminance = 1.0e4;

            double lum = static_cast<double>(luminance(linear_rgb));

            if (lum < 0.0)
            {
                lum = 0.0;
                ++m_out_of_range_luminance_error_count;
            }

            if (lum > MaxLuminance)
            {
                lum = MaxLuminance;
                ++m_out_of_range_luminance_error_count;
            }

            return lum;
        }

        size_t get_out_of_range_luminance_error_count() const
        {
            return m_out_of_range_luminance_error_count;
        }

      private:
        TextureCache&   m_texture_cache;
        const Source*   m_source;
        const double    m_rcp_width;
        const double    m_rcp_height;
        const double    m_u_shift;
        const double    m_v_shift;
        size_t          m_out_of_range_luminance_error_count;
    };

    const char* Model = "latlong_map_environment_edf";

    class LatLongMapEnvironmentEDF
      : public EnvironmentEDF
    {
      public:
        LatLongMapEnvironmentEDF(
            const char*         name,
            const ParamArray&   params)
          : EnvironmentEDF(name, params)
          , m_importance_map_width(0)
          , m_importance_map_height(0)
          , m_probability_scale(0.0)
        {
            m_inputs.declare("exitance", InputFormatSpectrum);

            m_u_shift = m_params.get_optional<double>("horizontal_shift", 0.0) / 360.0;
            m_v_shift = m_params.get_optional<double>("vertical_shift", 0.0) / 360.0;
        }

        virtual void release()
        {
            delete this;
        }

        virtual const char* get_model() const
        {
            return Model;
        }

        virtual void on_frame_begin(const Project& project)
        {
            EnvironmentEDF::on_frame_begin(project);

            if (m_importance_sampler.get() == 0)
                build_importance_map(*project.get_scene());
        }

        virtual void sample(
            InputEvaluator&     input_evaluator,
            const Vector2d&     s,
            Vector3d&           outgoing,
            Spectrum&           value,
            double&             probability) const
        {
            // Sample the importance map.
            size_t x, y;
            double prob_xy;
            m_importance_sampler->sample(s, x, y, prob_xy);

            // Compute the coordinates in [0,1]^2 of the sample.
            const double u = (2.0 * x + 1.0) / (2.0 * m_importance_map_width);
            const double v = (2.0 * y + 1.0) / (2.0 * m_importance_map_height);

            double theta, phi;
            unit_square_to_angles(u, v, theta, phi);

            // Compute the world space emission direction.
            outgoing = Vector3d::unit_vector(theta, phi);

            lookup_environment_map(input_evaluator, u, v, value);

            probability = prob_xy * m_probability_scale / sin(theta);
        }

        virtual void evaluate(
            InputEvaluator&     input_evaluator,
            const Vector3d&     outgoing,
            Spectrum&           value) const
        {
            assert(is_normalized(outgoing));

            double theta, phi;
            unit_vector_to_angles(outgoing, theta, phi);

            double u, v;
            angles_to_unit_square(theta, phi, u, v);

            lookup_environment_map(input_evaluator, u, v, value);
        }

        virtual void evaluate(
            InputEvaluator&     input_evaluator,
            const Vector3d&     outgoing,
            Spectrum&           value,
            double&             probability) const
        {
            assert(is_normalized(outgoing));

            double theta, phi;
            unit_vector_to_angles(outgoing, theta, phi);

            double u, v;
            angles_to_unit_square(theta, phi, u, v);

            lookup_environment_map(input_evaluator, u, v, value);

            probability = compute_pdf(u, v, theta);
        }

        virtual double evaluate_pdf(
            InputEvaluator&     input_evaluator,
            const Vector3d&     outgoing) const
        {
            assert(is_normalized(outgoing));

            double theta, phi;
            unit_vector_to_angles(outgoing, theta, phi);

            double u, v;
            angles_to_unit_square(theta, phi, u, v);

            return compute_pdf(u, v, theta);
        }

      private:
        struct InputValues
        {
            Spectrum    m_exitance;
            Alpha       m_exitance_alpha;       // unused
        };

        typedef ImageImportanceSampler<double> ImageImportanceSamplerType;

        double                                  m_u_shift;
        double                                  m_v_shift;

        size_t                                  m_importance_map_width;
        size_t                                  m_importance_map_height;
        double                                  m_probability_scale;
        auto_ptr<ImageImportanceSamplerType>    m_importance_sampler;

        // Compute the spherical coordinates of a given direction.
        static void unit_vector_to_angles(
            const Vector3d&     v,              // unit length
            double&             theta,          // in [0, Pi]
            double&             phi)            // in [-Pi, Pi]
        {
            assert(is_normalized(v));

            theta = acos(v[1]);
            phi = atan2(v[2], v[0]);
        }

        // Convert a given direction from spherical coordinates to [0,1]^2.
        static void angles_to_unit_square(
            const double        theta,          // in [0, Pi]
            const double        phi,            // in [-Pi, Pi]
            double&             u,              // in [0, 1]
            double&             v)              // in [0, 1]
        {
            assert(theta >= 0.0);
            assert(theta <= Pi);
            assert(phi >= -Pi);
            assert(phi <= Pi);

            u = (0.5 / Pi) * (phi + Pi);
            v = (1.0 / Pi) * theta;
        }

        // Convert a given direction from [0,1]^2 to spherical coordinates.
        static void unit_square_to_angles(
            const double        u,          // in [0, 1]
            const double        v,          // in [0, 1]
            double&             theta,      // in [0, Pi]
            double&             phi)        // in [-Pi, Pi]
        {
            assert(u >= 0.0);
            assert(u <= 1.0);
            assert(v >= 0.0);
            assert(v <= 1.0);

            theta = Pi * v;
            phi = Pi * (2.0 * u - 1.0);
        }

        void build_importance_map(const Scene& scene)
        {
            const Source* exitance_source = m_inputs.source("exitance");
            assert(exitance_source);

            if (dynamic_cast<const TextureSource*>(exitance_source))
            {
                const CanvasProperties& texture_props =
                    static_cast<const TextureSource*>(exitance_source)->get_texture(scene).properties();
                m_importance_map_width = texture_props.m_canvas_width;
                m_importance_map_height = texture_props.m_canvas_height;
            }
            else
            {
                m_importance_map_width = 1;
                m_importance_map_height = 1;
            }

            const size_t texel_count = m_importance_map_width * m_importance_map_height;
            m_probability_scale = texel_count / (2.0 * Pi * Pi);

            TextureStore texture_store(scene);
            TextureCache texture_cache(texture_store);
            ImageSampler sampler(
                texture_cache,
                exitance_source,
                m_importance_map_width,
                m_importance_map_height,
                m_u_shift,
                m_v_shift);

            RENDERER_LOG_INFO(
                "building " FMT_SIZE_T "x" FMT_SIZE_T " importance map "
                "for environment edf \"%s\"...",
                m_importance_map_width,
                m_importance_map_height,
                get_name());

            m_importance_sampler.reset(
                new ImageImportanceSamplerType(
                    m_importance_map_width,
                    m_importance_map_height,
                    sampler));

            const size_t oor_error_count = sampler.get_out_of_range_luminance_error_count();

            if (oor_error_count > 0)
            {
                RENDERER_LOG_WARNING(
                    "while building importance map for environment edf \"%s\": "
                    "found %s pixel%s with out-of-range luminance; rendering artifacts are to be expected.",
                    get_name(),
                    pretty_uint(oor_error_count).c_str(),
                    oor_error_count > 1 ? "s" : "");
            }

            RENDERER_LOG_INFO(
                "built importance map for environment edf \"%s\".",
                get_name());
        }

        void lookup_environment_map(
            InputEvaluator&     input_evaluator,
            const double        u,
            const double        v,
            Spectrum&           value) const
        {
            const Vector2d uv(u + m_u_shift, 1.0 - v + m_v_shift);

            const InputValues* values =
                input_evaluator.evaluate<InputValues>(m_inputs, uv);

            value = values->m_exitance;
        }

        double compute_pdf(
            const double        u,
            const double        v,
            const double        theta) const
        {
            // Compute the probability density of this sample in the importance map.
            const size_t x = truncate<size_t>(m_importance_map_width * u);
            const size_t y = truncate<size_t>(m_importance_map_height * v);
            const double prob_xy = m_importance_sampler->get_pdf(x, y);

            // Compute the probability density of the emission direction.
            return prob_xy * m_probability_scale / sin(theta);
        }
    };
}

//
// LatLongMapEnvironmentEDFFactory class implementation.
//

const char* LatLongMapEnvironmentEDFFactory::get_model() const
{
    return Model;
}

const char* LatLongMapEnvironmentEDFFactory::get_human_readable_model() const
{
    return "Latitude-Longitude Map Environment EDF";
}

DictionaryArray LatLongMapEnvironmentEDFFactory::get_widget_definitions() const
{
    DictionaryArray definitions;

    definitions.push_back(
        Dictionary()
            .insert("name", "exitance")
            .insert("label", "Exitance")
            .insert("widget", "entity_picker")
            .insert("entity_types",
                Dictionary()
                    .insert("texture_instance", "Textures"))
            .insert("use", "required")
            .insert("default", ""));

    definitions.push_back(
        Dictionary()
            .insert("name", "horizontal_shift")
            .insert("label", "Horizontal Shift")
            .insert("widget", "text_box")
            .insert("default", "0.0")
            .insert("use", "optional"));

    definitions.push_back(
        Dictionary()
            .insert("name", "vertical_shift")
            .insert("label", "Vertical Shift")
            .insert("widget", "text_box")
            .insert("default", "0.0")
            .insert("use", "optional"));

    return definitions;
}

auto_release_ptr<EnvironmentEDF> LatLongMapEnvironmentEDFFactory::create(
    const char*         name,
    const ParamArray&   params) const
{
    return
        auto_release_ptr<EnvironmentEDF>(
            new LatLongMapEnvironmentEDF(name, params));
}

}   // namespace renderer
<|endoftext|>


<commit_before>#include <clog/clog.h>
#include <nomagic.h>
#include <functional>
#include <stdexcept>
#include <string>
#include <type_traits>

#include "macros.h"

namespace {

using nomagic::loc;
using nomagic::Test;

using clogcmn::Content;

class Spy {
public:
	static Content* last() {
		return p;
	}

	static void out(const Content& content) {
		if (q == 0)
			q = new Content(content.message);
		p = q;
		*p = content;
	}

	static void reset() {
		p = 0;
	}

private:
	static Content* p;
	static Content* q;
};

Content* Spy::p(0);
Content* Spy::q(0);

namespace nspy {

void t1(const char* ms)
{
	Test t(ms);
	t.a(Spy::last() == 0, L);
}

void test_out(Test& t, const char* m)
{
	Content c(m);
	Spy::out(c);
	Content* r(Spy::last());
	t.a(r != 0, L);
	t.a(r->message == m, L);
}

void t2(const char* ms)
{
	Test t(ms);
	test_out(t, "a message 1");
	Spy::reset();
	test_out(t, "a message 2");
}

void t3(const char* ms)
{
	Test t(ms);
	Content c("a message");
	Spy::out(c);
	Spy::reset();
	Content* r(Spy::last());
	t.a(r == 0, L);
}

} // nspy

namespace nlog {

class F {
public:
	F() {
		clog::outfn = Spy::out;
	}
};

namespace nf {

bool called;

class A {
public:
	A() : v(0) {
	}

	A(const A& op)
		:	v(op.v) {
		copied = true;
	}

	int v;
	static bool copied;
};
bool A::copied;

A a;
A arg;

void reset()
{
	called = false;
	a = A();
	a.v = 1;
	arg = A();
	A::copied = false;
}

struct E {
};

} // nf

void f1()
{
	nf::called = true;
}

namespace d {

using std::enable_if;
using std::is_void;

template<class T>
struct enable_void : enable_if<is_void<T>::value> {
};

template<class T>
struct disable_void : enable_if<!is_void<T>::value> {
};

} // d

class Test_common {
public:
	Test_common(const char* ms)
		:	t(ms) {
	}

	void operator()() {
		F f;
		m = "message";
		nf::reset();
		prepare_call();
		call_and_assert();
		verify();
	}
protected:
	virtual void call_and_assert() = 0;
	virtual void prepare_call() {
	}
	virtual void verify() = 0;

	Test t;
	const char* m;
};

template<class F>
class Test_0 : public Test_common {
public:
	Test_0(F f, const char* ms)
		:	Test_common(ms), f(f) {
	}
protected:
	void call_and_assert() {
		call<decltype(clog::out(m, f))>();
	}
	void verify() {
		t.a(Spy::last()->message == m, L);
		t.a(nf::called, L);
	}

	F f;
private:
	template<class T>
	void call(typename d::enable_void<T>::type* = 0) {
		clog::out(m, f);
	}
	template<class T>
	void call(typename d::disable_void<T>::type* = 0) {
		t.a(clog::out(m, f) == 1, L);
	}
};

template<class F>
inline Test_0<F> test_0(F f, const char* ms)
{
	return Test_0<F>(f, ms);
}

void t1(const char* ms)
{
	(test_0(f1, ms))();
}

void f2(int a)
{
	nf::called = true;
	nf::arg.v = a;
}

template<class F>
class Test_1 : public Test_common {
public:
	Test_1(F f, const char* ms) : Test_common(ms), f(f) {
	}

protected:
	void verify() {
		t.a(Spy::last()->message == m, L);
		t.a(nf::called, L);
		t.a(nf::arg.v == nf::a.v, L);
		t.a(nf::A::copied == false, L);
	}

	F f;
};

template<class F>
class Test_1v : public Test_1<F> {
public:
	Test_1v(F f, const char* ms) : Test_1<F>(f, ms) {
	}

protected:
	using Test_common::m;
	using Test_1<F>::f;

	void call_and_assert() {
		call<decltype(clog::out(m, f, nf::a.v))>();
	}
	void prepare_call() {
		nf::a.v = 2;
	}
private:
	template<class T>
	void call(typename d::disable_void<T>::type* = 0) {
		Test_common::t.a(clog::out(m, f, nf::a.v) == 1, L);
	}

	template<class T>
	void call(typename d::enable_void<T>::type* = 0) {
		clog::out(m, f, nf::a.v);
	}
};

template<class F>
inline Test_1v<F> test_1v(F f, const char* ms)
{
	return Test_1v<F>(f, ms);
}

void t2(const char* ms)
{
	(test_1v(f2, ms))();
}

void f3(nf::A& a)
{
	nf::called = true;
	nf::arg = a;
}

void f3c(const nf::A& a)
{
	nf::called = true;
	nf::arg = a;
}

template<class F>
class Test_1r : public Test_1<F> {
public:
	Test_1r(F f, const char* ms) : Test_1<F>(f, ms) {
	}

protected:
	using Test_common::m;
	using Test_1<F>::f;

	void call_and_assert() {
		call<decltype(clog::out(m, f, nf::a))>();
	}
private:
	template<class T>
	void call(typename d::disable_void<T>::type* = 0) {
		Test_common::t.a(clog::out(m, f, nf::a) == 1, L);
	}

	template<class T>
	void call(typename d::enable_void<T>::type* = 0) {
		clog::out(m, f, nf::a);
	}
};

template<class F>
inline Test_1r<F> test_1r(F f, const char* ms)
{
	return Test_1r<F>(f, ms);
}

void t3(const char* ms)
{
	(test_1r(f3, ms))();
}

int f1_1r(nf::A& a)
{
	nf::called = true;
	nf::arg = a;
	return 1;
}

int f1_1rc(const nf::A& a)
{
	nf::called = true;
	nf::arg = a;
	return 1;
}

void t8(const char* ms)
{
	(test_1r(f1_1r, ms))();
}

void t9(const char* ms)
{
	(test_1r(f1_1rc, ms))();
}

void t4(const char* ms)
{
	(test_1r(f3c, ms))();
}

int f1_0()
{
	nf::called = true;
	return 1;
}

void t5(const char* ms)
{
	(test_0(f1_0, ms))();
}

int f1_1(int v)
{
	nf::called = true;
	nf::arg.v = v;
	return 1;
}

void t6(const char* ms)
{
	(test_1v(f1_1, ms))();
}

void f0e()
{
	throw nf::E();
}

void t7(const char* ms)
{
	Test t(ms);
	F f;
	const char* m("message");
	nf::reset();
	try {
		clog::out(m, f0e);
		throw std::logic_error("test");
	}
	catch (const nf::E&) {
	}
	t.a(Spy::last()->message == m, L);
	t.a(Spy::last()->exception, L);
}

} // nlog

using nomagic::run;

void spy_tests()
{
	using namespace nspy;

	run(	"Spy returns null when it is asked to "
		"provide the last log output but there is not.", t1);

	run("Spy returns the last log object", t2);

	run("Spy resets the last log", t3);
}

void log_tests()
{
	using namespace nlog;

	run("return(void), arg(0)", t1);
	run("return(void), arg(1)", t2);
	run("return(void), arg(1, ref)", t3);
	run("return(void), arg(1, cref)", t4);
	run("return(int), arg(0)", t5);
	run("return(int), arg(1)", t6);
	run("return(int), arg(1, ref)", t8);
	run("return(int), arg(1, cref)", t9);
	run("return(void), arg(0), exception", t7);
}

} // unnamed

#include "test.h"

namespace test {

void run_clog_tests()
{
	spy_tests();
	log_tests();
}

} // test

<commit_msg>This commit makes code beatifull slightly.<commit_after>#include <clog/clog.h>
#include <nomagic.h>
#include <functional>
#include <stdexcept>
#include <string>
#include <type_traits>

#include "macros.h"

namespace {

using nomagic::loc;
using nomagic::Test;

using clogcmn::Content;

class Spy {
public:
	static Content* last() {
		return p;
	}

	static void out(const Content& content) {
		if (q == 0)
			q = new Content(content.message);
		p = q;
		*p = content;
	}

	static void reset() {
		p = 0;
	}

private:
	static Content* p;
	static Content* q;
};

Content* Spy::p(0);
Content* Spy::q(0);

namespace nspy {

void t1(const char* ms)
{
	Test t(ms);
	t.a(Spy::last() == 0, L);
}

void test_out(Test& t, const char* m)
{
	Content c(m);
	Spy::out(c);
	Content* r(Spy::last());
	t.a(r != 0, L);
	t.a(r->message == m, L);
}

void t2(const char* ms)
{
	Test t(ms);
	test_out(t, "a message 1");
	Spy::reset();
	test_out(t, "a message 2");
}

void t3(const char* ms)
{
	Test t(ms);
	Content c("a message");
	Spy::out(c);
	Spy::reset();
	Content* r(Spy::last());
	t.a(r == 0, L);
}

} // nspy

namespace nlog {

class F {
public:
	F() {
		clog::outfn = Spy::out;
	}
};

namespace nf {

bool called;

class A {
public:
	A() : v(0) {
	}

	A(const A& op)
		:	v(op.v) {
		copied = true;
	}

	int v;
	static bool copied;
};
bool A::copied;

A a;
A arg;

void reset()
{
	called = false;
	a = A();
	a.v = 1;
	arg = A();
	A::copied = false;
}

struct E {
};

} // nf

void f1()
{
	nf::called = true;
}

namespace d {

using std::enable_if;
using std::is_void;

template<class T>
struct enable_void : enable_if<is_void<T>::value> {
};

template<class T>
struct disable_void : enable_if<!is_void<T>::value> {
};

} // d

class Test_common {
public:
	Test_common(const char* ms)
		:	t(ms) {
	}

	void operator()() {
		F f;
		m = "message";
		nf::reset();
		prepare_call();
		call_and_assert();
		verify();
	}
protected:
	virtual void call_and_assert() = 0;
	virtual void prepare_call() {
	}
	virtual void verify() = 0;

	Test t;
	const char* m;
};

template<class F>
class Test_0 : public Test_common {
public:
	Test_0(F f, const char* ms)
		:	Test_common(ms), f(f) {
	}
protected:
	void call_and_assert() {
		call<decltype(clog::out(m, f))>();
	}
	void verify() {
		t.a(Spy::last()->message == m, L);
		t.a(nf::called, L);
	}

	F f;
private:
	template<class T>
	void call(typename d::enable_void<T>::type* = 0) {
		clog::out(m, f);
	}
	template<class T>
	void call(typename d::disable_void<T>::type* = 0) {
		t.a(clog::out(m, f) == 1, L);
	}
};

template<class F>
inline Test_0<F> test_0(F f, const char* ms)
{
	return Test_0<F>(f, ms);
}

void t1(const char* ms)
{
	(test_0(f1, ms))();
}

void f2(int a)
{
	nf::called = true;
	nf::arg.v = a;
}

template<class F>
class Test_1 : public Test_common {
public:
	Test_1(F f, const char* ms) : Test_common(ms), f(f) {
	}

protected:
	void verify() {
		t.a(Spy::last()->message == m, L);
		t.a(nf::called, L);
		t.a(nf::arg.v == nf::a.v, L);
		t.a(nf::A::copied == false, L);
	}

	F f;
};

template<class F>
class Test_1v : public Test_1<F> {
public:
	Test_1v(F f, const char* ms) : Test_1<F>(f, ms) {
	}

protected:
	using Test_1<F>::m;
	using Test_1<F>::t;
	using Test_1<F>::f;

	void call_and_assert() {
		call<decltype(clog::out(m, f, nf::a.v))>();
	}
	void prepare_call() {
		nf::a.v = 2;
	}
private:
	template<class T>
	void call(typename d::disable_void<T>::type* = 0) {
		t.a(clog::out(m, f, nf::a.v) == 1, L);
	}

	template<class T>
	void call(typename d::enable_void<T>::type* = 0) {
		clog::out(m, f, nf::a.v);
	}
};

template<class F>
inline Test_1v<F> test_1v(F f, const char* ms)
{
	return Test_1v<F>(f, ms);
}

void t2(const char* ms)
{
	(test_1v(f2, ms))();
}

void f3(nf::A& a)
{
	nf::called = true;
	nf::arg = a;
}

void f3c(const nf::A& a)
{
	nf::called = true;
	nf::arg = a;
}

template<class F>
class Test_1r : public Test_1<F> {
public:
	Test_1r(F f, const char* ms) : Test_1<F>(f, ms) {
	}

protected:
	using Test_1<F>::m;
	using Test_1<F>::t;
	using Test_1<F>::f;

	void call_and_assert() {
		call<decltype(clog::out(m, f, nf::a))>();
	}
private:
	template<class T>
	void call(typename d::disable_void<T>::type* = 0) {
		t.a(clog::out(m, f, nf::a) == 1, L);
	}

	template<class T>
	void call(typename d::enable_void<T>::type* = 0) {
		clog::out(m, f, nf::a);
	}
};

template<class F>
inline Test_1r<F> test_1r(F f, const char* ms)
{
	return Test_1r<F>(f, ms);
}

void t3(const char* ms)
{
	(test_1r(f3, ms))();
}

int f1_1r(nf::A& a)
{
	nf::called = true;
	nf::arg = a;
	return 1;
}

int f1_1rc(const nf::A& a)
{
	nf::called = true;
	nf::arg = a;
	return 1;
}

void t8(const char* ms)
{
	(test_1r(f1_1r, ms))();
}

void t9(const char* ms)
{
	(test_1r(f1_1rc, ms))();
}

void t4(const char* ms)
{
	(test_1r(f3c, ms))();
}

int f1_0()
{
	nf::called = true;
	return 1;
}

void t5(const char* ms)
{
	(test_0(f1_0, ms))();
}

int f1_1(int v)
{
	nf::called = true;
	nf::arg.v = v;
	return 1;
}

void t6(const char* ms)
{
	(test_1v(f1_1, ms))();
}

void f0e()
{
	throw nf::E();
}

void t7(const char* ms)
{
	Test t(ms);
	F f;
	const char* m("message");
	nf::reset();
	try {
		clog::out(m, f0e);
		throw std::logic_error("test");
	}
	catch (const nf::E&) {
	}
	t.a(Spy::last()->message == m, L);
	t.a(Spy::last()->exception, L);
}

} // nlog

using nomagic::run;

void spy_tests()
{
	using namespace nspy;

	run(	"Spy returns null when it is asked to "
		"provide the last log output but there is not.", t1);

	run("Spy returns the last log object", t2);

	run("Spy resets the last log", t3);
}

void log_tests()
{
	using namespace nlog;

	run("return(void), arg(0)", t1);
	run("return(void), arg(1)", t2);
	run("return(void), arg(1, ref)", t3);
	run("return(void), arg(1, cref)", t4);
	run("return(int), arg(0)", t5);
	run("return(int), arg(1)", t6);
	run("return(int), arg(1, ref)", t8);
	run("return(int), arg(1, cref)", t9);
	run("return(void), arg(0), exception", t7);
}

} // unnamed

#include "test.h"

namespace test {

void run_clog_tests()
{
	spy_tests();
	log_tests();
}

} // test

<|endoftext|>