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

#include <inviwo/core/datastructures/volume/marchingtetrahedron.h>
namespace inviwo {

Mesh *MarchingTetrahedron::apply(const VolumeRepresentation *in, const double &iso,
                                 const vec4 &color) {
    detail::MarchingTetrahedronDispatcher disp;
    return in->getDataFormat()->dispatch(disp, in, iso, color);
}

void detail::evaluateTetra(Tree &vertexTree, IndexBufferRAM *indexBuffer,
                           std::vector<vec3> &positions, std::vector<vec3> &normals,
                           const glm::vec3 &p0, const double &v0, const glm::vec3 &p1,
                           const double &v1, const glm::vec3 &p2, const double &v2,
                           const glm::vec3 &p3, const double &v3) {
    int index = 0;
    if (v0 >= 0) index += 1;
    if (v1 >= 0) index += 2;
    if (v2 >= 0) index += 4;
    if (v3 >= 0) index += 8;
    glm::vec3 a, b, c, d;
    if (index == 0 || index == 15) return;
    if (index == 1 || index == 14) {
        a = interpolate(p0, v0, p2, v2);
        b = interpolate(p0, v0, p1, v1);
        c = interpolate(p0, v0, p3, v3);
        if (index == 1) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
        }
    } else if (index == 2 || index == 13) {
        a = interpolate(p1, v1, p0, v0);
        b = interpolate(p1, v1, p2, v2);
        c = interpolate(p1, v1, p3, v3);
        if (index == 2) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
        }

    } else if (index == 4 || index == 11) {
        a = interpolate(p2, v2, p0, v0);
        b = interpolate(p2, v2, p1, v1);
        c = interpolate(p2, v2, p3, v3);
        if (index == 4) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
        }
    } else if (index == 7 || index == 8) {
        a = interpolate(p3, v3, p0, v0);
        b = interpolate(p3, v3, p2, v2);
        c = interpolate(p3, v3, p1, v1);
        if (index == 7) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
        }
    } else if (index == 3 || index == 12) {
        a = interpolate(p0, v0, p2, v2);
        b = interpolate(p1, v1, p3, v3);
        c = interpolate(p0, v0, p3, v3);
        d = interpolate(p1, v1, p2, v2);

        if (index == 3) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
            addTriangle(vertexTree, indexBuffer, positions, normals, a, d, b);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, d);
        }

    } else if (index == 5 || index == 10) {
        a = interpolate(p2, v2, p3, v3);
        b = interpolate(p0, v0, p1, v1);
        c = interpolate(p0, v0, p3, v3);
        d = interpolate(p1, v1, p2, v2);

        if (index == 5) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
            addTriangle(vertexTree, indexBuffer, positions, normals, a, d, b);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, d);
        }

    } else if (index == 6 || index == 9) {
        a = interpolate(p1, v1, p3, v3);
        b = interpolate(p0, v0, p2, v2);
        c = interpolate(p0, v0, p1, v1);
        d = interpolate(p2, v2, p3, v3);

        if (index == 6) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, d);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
            addTriangle(vertexTree, indexBuffer, positions, normals, a, d, b);
        }
    }
}

size_t detail::addVertex(Tree &vertexTree, std::vector<vec3> &positions,
                         std::vector<vec3> &normals, const vec3 pos) {
    Tree::Node *nearest = vertexTree.findNearest(vec3(pos));
    vec3 p;
    if (nearest) {
        p.x = nearest->getPosition()[0];
        p.y = nearest->getPosition()[1];
        p.z = nearest->getPosition()[2];
    }
    if (!nearest || (glm::distance(p, pos) > glm::epsilon<double>() * 5)) {
        nearest = vertexTree.insert(pos, positions.size());
        positions.push_back(pos);
        normals.push_back(vec3(0, 0, 0));
    }
    return nearest->get();
}

void detail::addTriangle(Tree &vertexTree, IndexBufferRAM *indexBuffer,
                         std::vector<vec3> &positions, std::vector<vec3> &normals,
                         const glm::vec3 &a, const glm::vec3 &b, const glm::vec3 &c) {
    size_t i0 = addVertex(vertexTree, positions, normals, a);
    size_t i1 = addVertex(vertexTree, positions, normals, b);
    size_t i2 = addVertex(vertexTree, positions, normals, c);

    if (i0 == i1 || i0 == i2 || i1 == i2) {
        // triangle is so small so that the vertices are merged.
        return;
    }

    indexBuffer->add(static_cast<uint32_t>(i0));
    indexBuffer->add(static_cast<uint32_t>(i1));
    indexBuffer->add(static_cast<uint32_t>(i2));

    vec3 e0 = b - a;
    vec3 e1 = c - a;
    vec3 n = glm::normalize(glm::cross(e0, e1));

    normals[i0] += n;
    normals[i1] += n;
    normals[i2] += n;
}

glm::vec3 detail::interpolate(const glm::vec3 &p0, const double &v0, const glm::vec3 &p1,
                              const double &v1) {
    double t = 0;

    if (v0 != v1) t = v0 / (v0 - v1);

    float tF = static_cast<float>(t);
    return tF * p1 + (1.f - tF) * p0;
}

}  // namespace
<commit_msg>Jenkins Fix<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2014-2015 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <inviwo/core/datastructures/volume/marchingtetrahedron.h>
namespace inviwo {

Mesh *MarchingTetrahedron::apply(const VolumeRepresentation *in, const double &iso,
                                 const vec4 &color) {
    detail::MarchingTetrahedronDispatcher disp;
    return in->getDataFormat()->dispatch(disp, in, iso, color);
}

void detail::evaluateTetra(detail::Tree &vertexTree, IndexBufferRAM *indexBuffer,
                           std::vector<vec3> &positions, std::vector<vec3> &normals,
                           const glm::vec3 &p0, const double &v0, const glm::vec3 &p1,
                           const double &v1, const glm::vec3 &p2, const double &v2,
                           const glm::vec3 &p3, const double &v3) {
    int index = 0;
    if (v0 >= 0) index += 1;
    if (v1 >= 0) index += 2;
    if (v2 >= 0) index += 4;
    if (v3 >= 0) index += 8;
    glm::vec3 a, b, c, d;
    if (index == 0 || index == 15) return;
    if (index == 1 || index == 14) {
        a = interpolate(p0, v0, p2, v2);
        b = interpolate(p0, v0, p1, v1);
        c = interpolate(p0, v0, p3, v3);
        if (index == 1) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
        }
    } else if (index == 2 || index == 13) {
        a = interpolate(p1, v1, p0, v0);
        b = interpolate(p1, v1, p2, v2);
        c = interpolate(p1, v1, p3, v3);
        if (index == 2) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
        }

    } else if (index == 4 || index == 11) {
        a = interpolate(p2, v2, p0, v0);
        b = interpolate(p2, v2, p1, v1);
        c = interpolate(p2, v2, p3, v3);
        if (index == 4) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
        }
    } else if (index == 7 || index == 8) {
        a = interpolate(p3, v3, p0, v0);
        b = interpolate(p3, v3, p2, v2);
        c = interpolate(p3, v3, p1, v1);
        if (index == 7) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
        }
    } else if (index == 3 || index == 12) {
        a = interpolate(p0, v0, p2, v2);
        b = interpolate(p1, v1, p3, v3);
        c = interpolate(p0, v0, p3, v3);
        d = interpolate(p1, v1, p2, v2);

        if (index == 3) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
            addTriangle(vertexTree, indexBuffer, positions, normals, a, d, b);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, d);
        }

    } else if (index == 5 || index == 10) {
        a = interpolate(p2, v2, p3, v3);
        b = interpolate(p0, v0, p1, v1);
        c = interpolate(p0, v0, p3, v3);
        d = interpolate(p1, v1, p2, v2);

        if (index == 5) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
            addTriangle(vertexTree, indexBuffer, positions, normals, a, d, b);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, d);
        }

    } else if (index == 6 || index == 9) {
        a = interpolate(p1, v1, p3, v3);
        b = interpolate(p0, v0, p2, v2);
        c = interpolate(p0, v0, p1, v1);
        d = interpolate(p2, v2, p3, v3);

        if (index == 6) {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, c, b);
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, d);
        } else {
            addTriangle(vertexTree, indexBuffer, positions, normals, a, b, c);
            addTriangle(vertexTree, indexBuffer, positions, normals, a, d, b);
        }
    }
}

size_t detail::addVertex(detail::Tree &vertexTree, std::vector<vec3> &positions,
                         std::vector<vec3> &normals, const vec3 pos) {
    detail::Tree::Node *nearest = vertexTree.findNearest(vec3(pos));
    vec3 p;
    if (nearest) {
        p.x = nearest->getPosition()[0];
        p.y = nearest->getPosition()[1];
        p.z = nearest->getPosition()[2];
    }
    if (!nearest || (glm::distance(p, pos) > glm::epsilon<double>() * 5)) {
        nearest = vertexTree.insert(pos, positions.size());
        positions.push_back(pos);
        normals.push_back(vec3(0, 0, 0));
    }
    return nearest->get();
}

void detail::addTriangle(detail::Tree &vertexTree, IndexBufferRAM *indexBuffer,
                         std::vector<vec3> &positions, std::vector<vec3> &normals,
                         const glm::vec3 &a, const glm::vec3 &b, const glm::vec3 &c) {
    size_t i0 = addVertex(vertexTree, positions, normals, a);
    size_t i1 = addVertex(vertexTree, positions, normals, b);
    size_t i2 = addVertex(vertexTree, positions, normals, c);

    if (i0 == i1 || i0 == i2 || i1 == i2) {
        // triangle is so small so that the vertices are merged.
        return;
    }

    indexBuffer->add(static_cast<uint32_t>(i0));
    indexBuffer->add(static_cast<uint32_t>(i1));
    indexBuffer->add(static_cast<uint32_t>(i2));

    vec3 e0 = b - a;
    vec3 e1 = c - a;
    vec3 n = glm::normalize(glm::cross(e0, e1));

    normals[i0] += n;
    normals[i1] += n;
    normals[i2] += n;
}

glm::vec3 detail::interpolate(const glm::vec3 &p0, const double &v0, const glm::vec3 &p1,
                              const double &v1) {
    double t = 0;

    if (v0 != v1) t = v0 / (v0 - v1);

    float tF = static_cast<float>(t);
    return tF * p1 + (1.f - tF) * p0;
}

}  // namespace
<|endoftext|>


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

#include "Stock.hxx"
#include "translation/Handler.hxx"
#include "translation/Request.hxx"
#include "Client.hxx"
#include "stock/Item.hxx"
#include "stock/GetHandler.hxx"
#include "lease.hxx"
#include "pool/pool.hxx"
#include "pool/LeakDetector.hxx"
#include "system/Error.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "event/SocketEvent.hxx"
#include "io/Logger.hxx"
#include "stopwatch.hxx"

#include <stdexcept>

#include <string.h>
#include <errno.h>

class TranslationStock::Connection final : public StockItem {
	UniqueSocketDescriptor s;

	SocketEvent event;

public:
	explicit Connection(CreateStockItem c) noexcept
		:StockItem(c),
		 event(c.stock.GetEventLoop(), BIND_THIS_METHOD(EventCallback)) {}

private:
	bool CreateAndConnect(SocketAddress a) noexcept {
		assert(!s.IsDefined());

		return s.CreateNonBlock(AF_LOCAL, SOCK_STREAM, 0) &&
			s.Connect(a);
	}

public:
	void CreateAndConnectAndFinish(SocketAddress a) noexcept {
		if (CreateAndConnect(a)) {
			event.Open(s);
			InvokeCreateSuccess();
		} else {
			auto error = std::make_exception_ptr(MakeErrno("Failed to connect to translation server"));

			if (s.IsDefined())
				s.Close();

			InvokeCreateError(error);
		}
	}

	SocketDescriptor GetSocket() noexcept {
		return s;
	}

private:
	void EventCallback(unsigned) noexcept {
		char buffer;
		ssize_t nbytes = recv(s.Get(), &buffer, sizeof(buffer), MSG_DONTWAIT);
		if (nbytes < 0)
			LogConcat(2, "translation",
				  "error on idle translation server connection: ",
				  strerror(errno));
		else if (nbytes > 0)
			LogConcat(2, "translation",
				  "unexpected data in idle translation server connection");

		InvokeIdleDisconnect();
	}

public:
	/* virtual methods from class StockItem */
	bool Borrow() noexcept override {
		event.Cancel();
		return true;
	}

	bool Release() noexcept override {
		event.ScheduleRead();
		return true;
	}
};

class TranslationStock::Request final
	: Cancellable, StockGetHandler, Lease, PoolLeakDetector
{
	struct pool &pool;

	StopwatchPtr stopwatch;

	TranslationStock &stock;
	Connection *item;

	const TranslateRequest &request;

	TranslateHandler &handler;

	CancellablePointer &caller_cancel_ptr;
	CancellablePointer cancel_ptr;

public:
	Request(TranslationStock &_stock, struct pool &_pool,
		const TranslateRequest &_request,
		const StopwatchPtr &parent_stopwatch,
		TranslateHandler &_handler,
		CancellablePointer &_cancel_ptr) noexcept
		:PoolLeakDetector(_pool),
		 pool(_pool),
		 stopwatch(parent_stopwatch, "translate",
			   _request.GetDiagnosticName()),
		 stock(_stock),
		 request(_request),
		 handler(_handler),
		 caller_cancel_ptr(_cancel_ptr)
	{
		_cancel_ptr = *this;
	}

	void Start() noexcept {
		stock.Get(*this, cancel_ptr);
	}

private:
	void Destroy() noexcept {
		this->~Request();
	}

	/* virtual methods from class Cancellable */
	void Cancel() noexcept override {
		/* this cancels only the TranslationStock::Get() call initiated
		   from Start() */

		cancel_ptr.Cancel();
		Destroy();
	}

	/* virtual methods from class StockGetHandler */
	void OnStockItemReady(StockItem &item) noexcept override;
	void OnStockItemError(std::exception_ptr ep) noexcept override;

	/* virtual methods from class Lease */
	void ReleaseLease(bool reuse) noexcept override {
		stock.Put(*item, !reuse);
		Destroy();
	}
};

/*
 * stock callback
 *
 */

void
TranslationStock::Request::OnStockItemReady(StockItem &_item) noexcept
{
	item = &(Connection &)_item;

	/* cancellation will not be handled by this class from here on;
	   instead, we pass the caller's CancellablePointer to
	   translate() */
	translate(pool, stock.GetEventLoop(), std::move(stopwatch),
		  item->GetSocket(),
		  *this,
		  request, handler,
		  caller_cancel_ptr);

	/* ReleaseLease() will invoke Destroy() */
}

void
TranslationStock::Request::OnStockItemError(std::exception_ptr ep) noexcept
{
	auto &_handler = handler;
	Destroy();
	_handler.OnTranslateError(ep);
}

void
TranslationStock::Create(CreateStockItem c, StockRequest,
			 CancellablePointer &)
{
	auto *connection = new Connection(c);
	connection->CreateAndConnectAndFinish(address);
}

void
TranslationStock::SendRequest(struct pool &pool,
			      const TranslateRequest &request,
			      const StopwatchPtr &parent_stopwatch,
			      TranslateHandler &handler,
			      CancellablePointer &cancel_ptr) noexcept
{
	auto r = NewFromPool<Request>(pool, *this, pool, request,
				      parent_stopwatch,
				      handler, cancel_ptr);
	r->Start();
}
<commit_msg>translation/Stock: move code to CreateConnectStreamSocket()<commit_after>/*
 * Copyright 2007-2020 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Stock.hxx"
#include "translation/Handler.hxx"
#include "translation/Request.hxx"
#include "Client.hxx"
#include "stock/Item.hxx"
#include "stock/GetHandler.hxx"
#include "lease.hxx"
#include "pool/pool.hxx"
#include "pool/LeakDetector.hxx"
#include "system/Error.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "net/ToString.hxx"
#include "event/SocketEvent.hxx"
#include "io/Logger.hxx"
#include "stopwatch.hxx"

#include <stdexcept>

#include <string.h>
#include <errno.h>

static UniqueSocketDescriptor
CreateConnectStreamSocket(const SocketAddress address)
{
	UniqueSocketDescriptor fd;
	if (!fd.CreateNonBlock(address.GetFamily(), SOCK_STREAM, 0))
		throw MakeErrno("Failed to create socket");

	if (!fd.Connect(address)) {
		const int e = errno;
		char buffer[256];
		ToString(buffer, sizeof(buffer), address);
		throw FormatErrno(e, "Failed to connect to %s", buffer);
	}

	return fd;
}

class TranslationStock::Connection final : public StockItem {
	UniqueSocketDescriptor s;

	SocketEvent event;

public:
	explicit Connection(CreateStockItem c) noexcept
		:StockItem(c),
		 event(c.stock.GetEventLoop(), BIND_THIS_METHOD(EventCallback)) {}

	void CreateAndConnectAndFinish(SocketAddress a) noexcept {
		try {
			s = CreateConnectStreamSocket(a);
		} catch (...) {
			InvokeCreateError(std::current_exception());
			return;
		}

		event.Open(s);
		InvokeCreateSuccess();
	}

	SocketDescriptor GetSocket() noexcept {
		return s;
	}

private:
	void EventCallback(unsigned) noexcept {
		char buffer;
		ssize_t nbytes = recv(s.Get(), &buffer, sizeof(buffer), MSG_DONTWAIT);
		if (nbytes < 0)
			LogConcat(2, "translation",
				  "error on idle translation server connection: ",
				  strerror(errno));
		else if (nbytes > 0)
			LogConcat(2, "translation",
				  "unexpected data in idle translation server connection");

		InvokeIdleDisconnect();
	}

public:
	/* virtual methods from class StockItem */
	bool Borrow() noexcept override {
		event.Cancel();
		return true;
	}

	bool Release() noexcept override {
		event.ScheduleRead();
		return true;
	}
};

class TranslationStock::Request final
	: Cancellable, StockGetHandler, Lease, PoolLeakDetector
{
	struct pool &pool;

	StopwatchPtr stopwatch;

	TranslationStock &stock;
	Connection *item;

	const TranslateRequest &request;

	TranslateHandler &handler;

	CancellablePointer &caller_cancel_ptr;
	CancellablePointer cancel_ptr;

public:
	Request(TranslationStock &_stock, struct pool &_pool,
		const TranslateRequest &_request,
		const StopwatchPtr &parent_stopwatch,
		TranslateHandler &_handler,
		CancellablePointer &_cancel_ptr) noexcept
		:PoolLeakDetector(_pool),
		 pool(_pool),
		 stopwatch(parent_stopwatch, "translate",
			   _request.GetDiagnosticName()),
		 stock(_stock),
		 request(_request),
		 handler(_handler),
		 caller_cancel_ptr(_cancel_ptr)
	{
		_cancel_ptr = *this;
	}

	void Start() noexcept {
		stock.Get(*this, cancel_ptr);
	}

private:
	void Destroy() noexcept {
		this->~Request();
	}

	/* virtual methods from class Cancellable */
	void Cancel() noexcept override {
		/* this cancels only the TranslationStock::Get() call initiated
		   from Start() */

		cancel_ptr.Cancel();
		Destroy();
	}

	/* virtual methods from class StockGetHandler */
	void OnStockItemReady(StockItem &item) noexcept override;
	void OnStockItemError(std::exception_ptr ep) noexcept override;

	/* virtual methods from class Lease */
	void ReleaseLease(bool reuse) noexcept override {
		stock.Put(*item, !reuse);
		Destroy();
	}
};

/*
 * stock callback
 *
 */

void
TranslationStock::Request::OnStockItemReady(StockItem &_item) noexcept
{
	item = &(Connection &)_item;

	/* cancellation will not be handled by this class from here on;
	   instead, we pass the caller's CancellablePointer to
	   translate() */
	translate(pool, stock.GetEventLoop(), std::move(stopwatch),
		  item->GetSocket(),
		  *this,
		  request, handler,
		  caller_cancel_ptr);

	/* ReleaseLease() will invoke Destroy() */
}

void
TranslationStock::Request::OnStockItemError(std::exception_ptr ep) noexcept
{
	auto &_handler = handler;
	Destroy();
	_handler.OnTranslateError(ep);
}

void
TranslationStock::Create(CreateStockItem c, StockRequest,
			 CancellablePointer &)
{
	auto *connection = new Connection(c);
	connection->CreateAndConnectAndFinish(address);
}

void
TranslationStock::SendRequest(struct pool &pool,
			      const TranslateRequest &request,
			      const StopwatchPtr &parent_stopwatch,
			      TranslateHandler &handler,
			      CancellablePointer &cancel_ptr) noexcept
{
	auto r = NewFromPool<Request>(pool, *this, pool, request,
				      parent_stopwatch,
				      handler, cancel_ptr);
	r->Start();
}
<|endoftext|>


<commit_before>/*
// ts_mruby.hpp - ts_mruby internal functions
//
*/

#ifndef TS_MRUBY_H
#define TS_MRUBY_H

#include <vector>
#include <map>
#include <atscppapi/InterceptPlugin.h>
#include <atscppapi/Transaction.h>
#include <atscppapi/TransformationPlugin.h>

#define MODULE_NAME "ts_mruby"
#define MODULE_VERSION "0.1"

#define MODULE_AUTHOR "Ryo Okubo"
#define MODULE_EMAIL ""

const int FILTER_RESERVED_BUFFER_SIZE = 1024;

typedef std::vector<std::pair<std::string, std::string>> HeaderVec;

class RputsPlugin : public atscppapi::InterceptPlugin {
private:
  int _statusCode;
  HeaderVec _headers;
  std::string _message;

public:
  RputsPlugin(atscppapi::Transaction &transaction, int code = 200)
      : atscppapi::InterceptPlugin(
            transaction, atscppapi::InterceptPlugin::TRANSACTION_INTERCEPT),
        _statusCode(code), _message("") {}

  void consume(const std::string &data,
               atscppapi::InterceptPlugin::RequestDataType type){};

  void setStatusCode(int code);

  void appendMessage(const std::string &msg);

  void appendHeader(const std::pair<std::string, std::string> &entry);

  void appendHeaders(const HeaderVec &h);

  void handleInputComplete();
};

class HeaderRewritePlugin : public atscppapi::TransactionPlugin {
private:
  HeaderVec _headers;

public:
  HeaderRewritePlugin(atscppapi::Transaction &transaction)
    : atscppapi::TransactionPlugin(transaction) {
      atscppapi::TransactionPlugin::registerHook(HOOK_SEND_RESPONSE_HEADERS);
  }

  void addRewriteRule(const std::pair<std::string, std::string> &entry);
  void handleSendResponseHeaders(atscppapi::Transaction &transaction);
};

class FilterPlugin : public atscppapi::TransformationPlugin {
private:
  std::string _bodyBuffer;

public:
  FilterPlugin(atscppapi::Transaction &transaction)
      : atscppapi::TransformationPlugin(transaction, RESPONSE_TRANSFORMATION) {
    registerHook(HOOK_SEND_RESPONSE_HEADERS);
    _bodyBuffer.reserve(FILTER_RESERVED_BUFFER_SIZE);
  }

  void appendBody(const std::string &data);

  void consume(const std::string &data) { /* drop */
  }
  void handleInputComplete();
};

struct TSMrubyContext {
  atscppapi::Transaction *transaction;

  RputsPlugin *rputs;
  HeaderRewritePlugin *header_rewrite;
  FilterPlugin *filter;
};

#endif // TS_MRUBY_CORE_H
<commit_msg>refactoring: replace typedef with type alias<commit_after>/*
// ts_mruby.hpp - ts_mruby internal functions
//
*/

#ifndef TS_MRUBY_H
#define TS_MRUBY_H

#include <vector>
#include <map>
#include <atscppapi/InterceptPlugin.h>
#include <atscppapi/Transaction.h>
#include <atscppapi/TransformationPlugin.h>

#define MODULE_NAME "ts_mruby"
#define MODULE_VERSION "0.1"

#define MODULE_AUTHOR "Ryo Okubo"
#define MODULE_EMAIL ""

const int FILTER_RESERVED_BUFFER_SIZE = 1024;

using HeaderVec = std::vector<std::pair<std::string, std::string>>;

class RputsPlugin : public atscppapi::InterceptPlugin {
private:
  int _statusCode;
  HeaderVec _headers;
  std::string _message;

public:
  RputsPlugin(atscppapi::Transaction &transaction, int code = 200)
      : atscppapi::InterceptPlugin(
            transaction, atscppapi::InterceptPlugin::TRANSACTION_INTERCEPT),
        _statusCode(code), _message("") {}

  void consume(const std::string &data,
               atscppapi::InterceptPlugin::RequestDataType type){};

  void setStatusCode(int code);

  void appendMessage(const std::string &msg);

  void appendHeader(const std::pair<std::string, std::string> &entry);

  void appendHeaders(const HeaderVec &h);

  void handleInputComplete();
};

class HeaderRewritePlugin : public atscppapi::TransactionPlugin {
private:
  HeaderVec _headers;

public:
  HeaderRewritePlugin(atscppapi::Transaction &transaction)
    : atscppapi::TransactionPlugin(transaction) {
      atscppapi::TransactionPlugin::registerHook(HOOK_SEND_RESPONSE_HEADERS);
  }

  void addRewriteRule(const std::pair<std::string, std::string> &entry);
  void handleSendResponseHeaders(atscppapi::Transaction &transaction);
};

class FilterPlugin : public atscppapi::TransformationPlugin {
private:
  std::string _bodyBuffer;

public:
  FilterPlugin(atscppapi::Transaction &transaction)
      : atscppapi::TransformationPlugin(transaction, RESPONSE_TRANSFORMATION) {
    registerHook(HOOK_SEND_RESPONSE_HEADERS);
    _bodyBuffer.reserve(FILTER_RESERVED_BUFFER_SIZE);
  }

  void appendBody(const std::string &data);

  void consume(const std::string &data) { /* drop */
  }
  void handleInputComplete();
};

struct TSMrubyContext {
  atscppapi::Transaction *transaction;

  RputsPlugin *rputs;
  HeaderRewritePlugin *header_rewrite;
  FilterPlugin *filter;
};

#endif // TS_MRUBY_CORE_H
<|endoftext|>


<commit_before>// Copyright (c) 2020 by Robert Bosch GmbH, Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "iceoryx_binding_c/internal/cpp2c_enum_translation.hpp"
#include "iceoryx_binding_c/enums.h"

using namespace iox;
using namespace iox::popo;

namespace cpp2c
{
iox_SubscribeState SubscribeState(const iox::SubscribeState value)
{
    switch (value)
    {
    case SubscribeState::NOT_SUBSCRIBED:
        return iox_SubscribeState::SubscribeState_NOT_SUBSCRIBED;
    case SubscribeState::SUBSCRIBE_REQUESTED:
        return iox_SubscribeState::SubscribeState_SUBSCRIBE_REQUESTED;
    case SubscribeState::SUBSCRIBED:
        return iox_SubscribeState::SubscribeState_SUBSCRIBED;
    case SubscribeState::UNSUBSCRIBE_REQUESTED:
        return iox_SubscribeState::SubscribeState_UNSUBSCRIBE_REQUESTED;
    case SubscribeState::WAIT_FOR_OFFER:
        return iox_SubscribeState::SubscribeState_WAIT_FOR_OFFER;
    default:
        return iox_SubscribeState::SubscribeState_UNDEFINED_ERROR;
    }
}

iox_ChunkReceiveResult ChunkReceiveResult(const iox::popo::ChunkReceiveError value)
{
    switch (value)
    {
    case ChunkReceiveError::TOO_MANY_CHUNKS_HELD_IN_PARALLEL:
        return ChunkReceiveResult_TOO_MANY_CHUNKS_HELD_IN_PARALLEL;
    default:
        return ChunkReceiveResult_UNDEFINED_ERROR;
    }
}

iox_AllocationResult AllocationResult(const iox::popo::AllocationError value)
{
    switch (value)
    {
    case AllocationError::RUNNING_OUT_OF_CHUNKS:
        return AllocationResult_RUNNING_OUT_OF_CHUNKS;
    case AllocationError::TOO_MANY_CHUNKS_ALLOCATED_IN_PARALLEL:
        return AllocationResult_TOO_MANY_CHUNKS_ALLOCATED_IN_PARALLEL;
    default:
        return AllocationResult_UNDEFINED_ERROR;
    }
}

iox_WaitSetResult WaitSetResult(const iox::popo::WaitSetError value)
{
    switch (value)
    {
    case WaitSetError::WAIT_SET_FULL:
        return WaitSetResult_WAIT_SET_FULL;
    case WaitSetError::EVENT_ALREADY_ATTACHED:
        return WaitSetResult_EVENT_ALREADY_ATTACHED;
    default:
        return WaitSetResult_UNDEFINED_ERROR;
    }
}

} // namespace cpp2c
<commit_msg>iox-#495 add namespace to SubscribeState in C-Binding<commit_after>// Copyright (c) 2020 by Robert Bosch GmbH, Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "iceoryx_binding_c/internal/cpp2c_enum_translation.hpp"
#include "iceoryx_binding_c/enums.h"

using namespace iox;
using namespace iox::popo;

namespace cpp2c
{
iox_SubscribeState SubscribeState(const iox::SubscribeState value)
{
    switch (value)
    {
    case iox::SubscribeState::NOT_SUBSCRIBED:
        return iox_SubscribeState::SubscribeState_NOT_SUBSCRIBED;
    case iox::SubscribeState::SUBSCRIBE_REQUESTED:
        return iox_SubscribeState::SubscribeState_SUBSCRIBE_REQUESTED;
    case iox::SubscribeState::SUBSCRIBED:
        return iox_SubscribeState::SubscribeState_SUBSCRIBED;
    case iox::SubscribeState::UNSUBSCRIBE_REQUESTED:
        return iox_SubscribeState::SubscribeState_UNSUBSCRIBE_REQUESTED;
    case iox::SubscribeState::WAIT_FOR_OFFER:
        return iox_SubscribeState::SubscribeState_WAIT_FOR_OFFER;
    default:
        return iox_SubscribeState::SubscribeState_UNDEFINED_ERROR;
    }
}

iox_ChunkReceiveResult ChunkReceiveResult(const iox::popo::ChunkReceiveError value)
{
    switch (value)
    {
    case ChunkReceiveError::TOO_MANY_CHUNKS_HELD_IN_PARALLEL:
        return ChunkReceiveResult_TOO_MANY_CHUNKS_HELD_IN_PARALLEL;
    default:
        return ChunkReceiveResult_UNDEFINED_ERROR;
    }
}

iox_AllocationResult AllocationResult(const iox::popo::AllocationError value)
{
    switch (value)
    {
    case AllocationError::RUNNING_OUT_OF_CHUNKS:
        return AllocationResult_RUNNING_OUT_OF_CHUNKS;
    case AllocationError::TOO_MANY_CHUNKS_ALLOCATED_IN_PARALLEL:
        return AllocationResult_TOO_MANY_CHUNKS_ALLOCATED_IN_PARALLEL;
    default:
        return AllocationResult_UNDEFINED_ERROR;
    }
}

iox_WaitSetResult WaitSetResult(const iox::popo::WaitSetError value)
{
    switch (value)
    {
    case WaitSetError::WAIT_SET_FULL:
        return WaitSetResult_WAIT_SET_FULL;
    case WaitSetError::EVENT_ALREADY_ATTACHED:
        return WaitSetResult_EVENT_ALREADY_ATTACHED;
    default:
        return WaitSetResult_UNDEFINED_ERROR;
    }
}

} // namespace cpp2c
<|endoftext|>


<commit_before>#include "BracketedSentenceWidget.hh"

#include <QDebug>
#include <QPainter>
#include <QTextEdit>
#include <QSettings>

BracketedSentenceWidget::BracketedSentenceWidget(QWidget *parent)
:
    QTextEdit(parent),
    d_stylesheet(":/stylesheets/bracketed-sentence.xsl"),
    d_transformer(d_stylesheet)
{
    QFontMetrics m(font());
    int ruleHeight = m.lineSpacing();
    setFixedHeight(2 * ruleHeight + (2 * document()->documentMargin()));

}

void BracketedSentenceWidget::setParse(QString const &parse)
{
    d_parse = parse;
    updateText();
}

void BracketedSentenceWidget::updateText()
{
    if (!d_parse.isEmpty())
    {
        QString sentence = transformXML(d_parse);
        setText(sentence.trimmed());
    }
}

QString BracketedSentenceWidget::transformXML(QString const &xml) const
{
    QHash<QString, QString> params;
    return d_transformer.transform(xml, params);
}
<commit_msg>Made BracketedSentenceWidget read-only. Because it is.<commit_after>#include "BracketedSentenceWidget.hh"

#include <QDebug>
#include <QPainter>
#include <QTextEdit>
#include <QSettings>

BracketedSentenceWidget::BracketedSentenceWidget(QWidget *parent)
:
    QTextEdit(parent),
    d_stylesheet(":/stylesheets/bracketed-sentence.xsl"),
    d_transformer(d_stylesheet)
{
    QFontMetrics m(font());
    int ruleHeight = m.lineSpacing();
    setFixedHeight(2 * ruleHeight + (2 * document()->documentMargin()));
    setReadOnly(true);
}

void BracketedSentenceWidget::setParse(QString const &parse)
{
    d_parse = parse;
    updateText();
}

void BracketedSentenceWidget::updateText()
{
    if (!d_parse.isEmpty())
    {
        QString sentence = transformXML(d_parse);
        setText(sentence.trimmed());
    }
}

QString BracketedSentenceWidget::transformXML(QString const &xml) const
{
    QHash<QString, QString> params;
    return d_transformer.transform(xml, params);
}
<|endoftext|>


<commit_before>/************************************************************************************
**
* @copyright (c) 2013-2100, ChengDu Duyer Technology Co., LTD. All Right Reserved.
*
*************************************************************************************/
/**
* @file		g_fileinputstream.cpp
* @version
* @brief
* @author   ylh
* @date     2014-07-11
* @note
*
*  1. 2014-07-11 ylh Created this file
*
*/

#include <g_fileinputstream.h>

using namespace std;
using namespace GCommon;

FileInputStream::FileInputStream(shared_ptr<GFile> file) throw(std::logic_error)
	: m_file(file)
{
	if (m_file == NULL)
	{
		throw invalid_argument("");
	}
}

FileInputStream::FileInputStream(const string filepath) throw(std::bad_alloc, std::ios_base::failure)
	: m_file((shared_ptr<GFile>(new GFile(filepath.c_str()))))
{
	if (m_file->open(G_OPEN_READ) == G_NO)
	{
		throw ios_base::failure("");
	}
}

FileInputStream::~FileInputStream()
{
}

GInt32 FileInputStream::available() throw(std::ios_base::failure)
{
	// TODO: GFile
	return 0;
}

void FileInputStream::close() throw(std::ios_base::failure)
{
	// m_file->close();
	// do nothing
}

GInt32 FileInputStream::read() throw(std::ios_base::failure, std::logic_error)
{
	GInt8 pBuffer[1] = {0};
	return  this->read(pBuffer, 1);
}

GInt32 FileInputStream::read(GInt8 * pBuffer, GInt32 iBufferLen, GInt32 iOffset, GInt32 iLen) throw(std::ios_base::failure, std::logic_error)
{
	if (openCheck() == G_NO)
	{
		return -4;
	}

	if (!pBuffer)
	{
		throw invalid_argument("");
	}
	else if (iOffset < 0 || iLen < 0 ||iBufferLen < iOffset + iLen)
	{
		throw out_of_range("");
	}
	else if (iLen == 0)
	{
		return 0;
	}

	return m_file->read(pBuffer + iOffset, iLen);
}

GInt64 FileInputStream::skip(GInt64 lNum) throw(std::ios_base::failure)
{
	if (openCheck() == G_NO)
	{
		return -4;
	}

	if (lNum == 0)
	{
		return 0;
	}
	if (lNum  < 0)
	{
		throw out_of_range("");
	}

	// TODO: GFile
	if (m_file->seek(lNum, G_SEEK_CUR))
	{
		throw ios_base::failure("");
	}
	return 0;
}

shared_ptr<GFile> FileInputStream::GetFile()
{
	return m_file;
}

GResult FileInputStream::openCheck()
{
	// TODO: GFile
	return G_YES;
}
<commit_msg>Update g_fileinputstream.cpp<commit_after>/************************************************************************************
**
* @copyright (c) 2013-2100, ChengDu Duyer Technology Co., LTD. All Right Reserved.
*
*************************************************************************************/
/**
* @file		g_fileinputstream.cpp
* @version
* @brief
* @author   ylh
* @date     2014-07-11
* @note
*
*  1. 2014-07-11 ylh Created this file
*
*/

#include <g_fileinputstream.h>
#include <g_private_define.h>

using namespace std;
using namespace GCommon;

FileInputStream::FileInputStream(shared_ptr<GFile> file)
	: m_file(file)
{
	if (m_file == NULL)
	{
		throw invalid_argument(EXCEPTION_DESCRIPTION("invalid_argument"));
	}
}

FileInputStream::FileInputStream(const string filepath)
	: m_file((shared_ptr<GFile>(new GFile(filepath.c_str()))))
{
	if (m_file->open(G_OPEN_READ) == G_NO)
	{
		throw ios_base::failure(EXCEPTION_DESCRIPTION("ios_base::failure"));
	}
}

FileInputStream::~FileInputStream()
{
}

GInt32 FileInputStream::available() throw(std::ios_base::failure)
{
	// TODO: GFile
	return 0;
}

void FileInputStream::close() throw(std::ios_base::failure)
{
	// m_file->close();
	// do nothing
}

GInt32 FileInputStream::read() throw(std::ios_base::failure, std::logic_error)
{
	GInt8 pBuffer[1] = {0};
	return  this->read(pBuffer, 1);
}

GInt32 FileInputStream::read(GInt8 * pBuffer, GInt32 iBufferLen, GInt32 iOffset, GInt32 iLen) throw(std::ios_base::failure, std::logic_error)
{
	if (openCheck() == G_NO)
	{
		return -4;
	}

	if (!pBuffer)
	{
		throw invalid_argument(EXCEPTION_DESCRIPTION("invalid_argument"));
	}
	else if (iOffset < 0 || iLen < 0 ||iBufferLen < iOffset + iLen)
	{
		throw out_of_range(EXCEPTION_DESCRIPTION("out_of_range"));
	}
	else if (iLen == 0)
	{
		return 0;
	}

	return m_file->read(pBuffer + iOffset, iLen);
}

GInt64 FileInputStream::skip(GInt64 lNum) throw(std::ios_base::failure)
{
	if (openCheck() == G_NO)
	{
		return -4;
	}

	if (lNum == 0)
	{
		return 0;
	}
	if (lNum  < 0)
	{
		throw out_of_range("");
	}

	// TODO: GFile
	if (m_file->seek(lNum, G_SEEK_CUR))
	{
		throw ios_base::failure(EXCEPTION_DESCRIPTION("ios_base::failure"));
	}
	return 0;
}

shared_ptr<GFile> FileInputStream::GetFile()
{
	return m_file;
}

GResult FileInputStream::openCheck()
{
	// TODO: GFile
	return G_YES;
}
<|endoftext|>


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

/**
 * @file path_data.cc
 **/

#include "modules/planning/common/path/path_data.h"

#include <algorithm>
#include <limits>

#include "modules/common/log.h"
#include "modules/common/util/string_util.h"
#include "modules/common/util/util.h"
#include "modules/planning/common/planning_gflags.h"
#include "modules/planning/common/planning_util.h"
#include "modules/planning/math/frame_conversion/cartesian_frenet_conversion.h"

namespace apollo {
namespace planning {

using apollo::common::SLPoint;
using apollo::common::math::Vec2d;

bool PathData::SetDiscretizedPath(const DiscretizedPath &path) {
  if (reference_line_ == nullptr) {
    AERROR << "Should NOT set discretized path when reference line is nullptr. "
              "Please set reference line first.";
    return false;
  }
  discretized_path_ = path;
  if (!XYToSL(discretized_path_, &frenet_path_)) {
    AERROR << "Fail to transfer discretized path to frenet path.";
    return false;
  }
  DCHECK_EQ(discretized_path_.NumOfPoints(), frenet_path_.points().size());
  path_data_history_.push_back(std::make_pair(discretized_path_, frenet_path_));
  return true;
}

bool PathData::SetFrenetPath(const FrenetFramePath &frenet_path) {
  if (reference_line_ == nullptr) {
    AERROR << "Should NOT set frenet path when reference line is nullptr. "
              "Please set reference line first.";
    return false;
  }
  frenet_path_ = frenet_path;
  if (!SLToXY(frenet_path_, &discretized_path_)) {
    AERROR << "Fail to transfer frenet path to discretized path.";
    return false;
  }
  DCHECK_EQ(discretized_path_.NumOfPoints(), frenet_path_.points().size());
  path_data_history_.push_back(std::make_pair(discretized_path_, frenet_path_));
  return true;
}

const DiscretizedPath &PathData::discretized_path() const {
  return discretized_path_;
}

const FrenetFramePath &PathData::frenet_frame_path() const {
  return frenet_path_;
}

void PathData::SetReferenceLine(const ReferenceLine *reference_line) {
  Clear();
  reference_line_ = reference_line;
}

bool PathData::GetPathPointWithPathS(
    const double s, common::PathPoint *const path_point) const {
  *path_point = discretized_path_.EvaluateUsingLinearApproximation(s);
  return true;
}

bool PathData::GetPathPointWithRefS(const double ref_s,
                                    common::PathPoint *const path_point) const {
  DCHECK_NOTNULL(reference_line_);
  DCHECK_NOTNULL(path_point);
  DCHECK_EQ(discretized_path_.path_points().size(),
            frenet_path_.points().size());
  if (ref_s < 0) {
    AERROR << "ref_s[" << ref_s << "] should be > 0";
    return false;
  }
  if (ref_s > frenet_path_.points().back().s()) {
    AERROR << "ref_s is larger than the length of frenet_path_ length ["
           << frenet_path_.points().back().s() << "].";
    return false;
  }

  uint32_t index = 0;
  const double kDistanceEpsilon = 1e-3;
  for (uint32_t i = 0; i + 1 < frenet_path_.points().size(); ++i) {
    if (fabs(ref_s - frenet_path_.points().at(i).s()) < kDistanceEpsilon) {
      path_point->CopyFrom(discretized_path_.path_points().at(i));
      return true;
    }
    if (frenet_path_.points().at(i).s() < ref_s &&
        ref_s <= frenet_path_.points().at(i + 1).s()) {
      index = i;
      break;
    }
  }
  double r = (ref_s - frenet_path_.points().at(index).s()) /
             (frenet_path_.points().at(index + 1).s() -
              frenet_path_.points().at(index).s());

  const double discretized_path_s =
      discretized_path_.path_points().at(index).s() +
      r * (discretized_path_.path_points().at(index + 1).s() -
           discretized_path_.path_points().at(index).s());
  path_point->CopyFrom(
      discretized_path_.EvaluateUsingLinearApproximation(discretized_path_s));

  return true;
}

void PathData::Clear() {
  discretized_path_.Clear();
  frenet_path_.Clear();
  reference_line_ = nullptr;
}

std::string PathData::DebugString() const {
  const auto &path_points = discretized_path_.path_points();
  const auto limit =
      std::min(path_points.size(),
               static_cast<size_t>(FLAGS_trajectory_point_num_for_debug));

  return apollo::common::util::StrCat(
      "[\n", apollo::common::util::PrintDebugStringIter(
                 path_points.begin(), path_points.begin() + limit, ",\n"),
      "]\n");
}

bool PathData::SLToXY(const FrenetFramePath &frenet_path,
                      DiscretizedPath *const discretized_path) {
  DCHECK_NOTNULL(discretized_path);
  std::vector<common::PathPoint> path_points;
  for (const common::FrenetFramePoint &frenet_point : frenet_path.points()) {
    common::SLPoint sl_point;
    common::math::Vec2d cartesian_point;
    sl_point.set_s(frenet_point.s());
    sl_point.set_l(frenet_point.l());
    if (!reference_line_->SLToXY(sl_point, &cartesian_point)) {
      AERROR << "Fail to convert sl point to xy point";
      return false;
    }
    ReferencePoint ref_point =
        reference_line_->GetReferencePoint(frenet_point.s());
    double theta = CartesianFrenetConverter::CalculateTheta(
        ref_point.heading(), ref_point.kappa(), frenet_point.l(),
        frenet_point.dl());
    double kappa = CartesianFrenetConverter::CalculateKappa(
        ref_point.kappa(), ref_point.dkappa(), frenet_point.l(),
        frenet_point.dl(), frenet_point.ddl());

    common::PathPoint path_point = common::util::MakePathPoint(
        cartesian_point.x(), cartesian_point.y(), 0.0, theta, kappa, 0.0, 0.0);

    if (path_points.empty()) {
      path_point.set_s(0.0);
    } else {
      common::math::Vec2d last(path_points.back().x(), path_points.back().y());
      common::math::Vec2d current(path_point.x(), path_point.y());
      double distance = (last - current).Length();
      path_point.set_s(path_points.back().s() + distance);
    }
    path_points.push_back(std::move(path_point));
  }
  *discretized_path = DiscretizedPath(std::move(path_points));

  return true;
}

bool PathData::XYToSL(const DiscretizedPath &discretized_path,
                      FrenetFramePath *const frenet_path) {
  DCHECK_NOTNULL(frenet_path);
  std::vector<common::FrenetFramePoint> frenet_frame_points;

  for (const auto &path_point : discretized_path.path_points()) {
    SLPoint sl_point;
    if (!reference_line_->XYToSL({path_point.x(), path_point.y()}, &sl_point)) {
      AERROR << "Fail to transfer cartesian point to frenet point.";
      return false;
    }
    common::FrenetFramePoint frenet_point;
    // NOTICE: does not set dl and ddl here. Add if needed.
    frenet_point.set_s(sl_point.s());
    frenet_point.set_l(sl_point.l());
    frenet_frame_points.push_back(std::move(frenet_point));
  }
  *frenet_path = FrenetFramePath(std::move(frenet_frame_points));
  return true;
}

}  // namespace planning
}  // namespace apollo
<commit_msg>planning: fix frenet s larger than reference line s warning<commit_after>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

/**
 * @file path_data.cc
 **/

#include "modules/planning/common/path/path_data.h"

#include <algorithm>
#include <limits>

#include "modules/common/log.h"
#include "modules/common/util/string_util.h"
#include "modules/common/util/util.h"
#include "modules/planning/common/planning_gflags.h"
#include "modules/planning/common/planning_util.h"
#include "modules/planning/math/frame_conversion/cartesian_frenet_conversion.h"

namespace apollo {
namespace planning {

using apollo::common::SLPoint;
using apollo::common::math::Vec2d;

bool PathData::SetDiscretizedPath(const DiscretizedPath &path) {
  if (reference_line_ == nullptr) {
    AERROR << "Should NOT set discretized path when reference line is nullptr. "
              "Please set reference line first.";
    return false;
  }
  discretized_path_ = path;
  if (!XYToSL(discretized_path_, &frenet_path_)) {
    AERROR << "Fail to transfer discretized path to frenet path.";
    return false;
  }
  DCHECK_EQ(discretized_path_.NumOfPoints(), frenet_path_.points().size());
  path_data_history_.push_back(std::make_pair(discretized_path_, frenet_path_));
  return true;
}

bool PathData::SetFrenetPath(const FrenetFramePath &frenet_path) {
  if (reference_line_ == nullptr) {
    AERROR << "Should NOT set frenet path when reference line is nullptr. "
              "Please set reference line first.";
    return false;
  }
  frenet_path_ = frenet_path;
  if (!SLToXY(frenet_path_, &discretized_path_)) {
    AERROR << "Fail to transfer frenet path to discretized path.";
    return false;
  }
  DCHECK_EQ(discretized_path_.NumOfPoints(), frenet_path_.points().size());
  path_data_history_.push_back(std::make_pair(discretized_path_, frenet_path_));
  return true;
}

const DiscretizedPath &PathData::discretized_path() const {
  return discretized_path_;
}

const FrenetFramePath &PathData::frenet_frame_path() const {
  return frenet_path_;
}

void PathData::SetReferenceLine(const ReferenceLine *reference_line) {
  Clear();
  reference_line_ = reference_line;
}

bool PathData::GetPathPointWithPathS(
    const double s, common::PathPoint *const path_point) const {
  *path_point = discretized_path_.EvaluateUsingLinearApproximation(s);
  return true;
}

bool PathData::GetPathPointWithRefS(const double ref_s,
                                    common::PathPoint *const path_point) const {
  DCHECK_NOTNULL(reference_line_);
  DCHECK_NOTNULL(path_point);
  DCHECK_EQ(discretized_path_.path_points().size(),
            frenet_path_.points().size());
  if (ref_s < 0) {
    AERROR << "ref_s[" << ref_s << "] should be > 0";
    return false;
  }
  if (ref_s > frenet_path_.points().back().s()) {
    AERROR << "ref_s is larger than the length of frenet_path_ length ["
           << frenet_path_.points().back().s() << "].";
    return false;
  }

  uint32_t index = 0;
  const double kDistanceEpsilon = 1e-3;
  for (uint32_t i = 0; i + 1 < frenet_path_.points().size(); ++i) {
    if (fabs(ref_s - frenet_path_.points().at(i).s()) < kDistanceEpsilon) {
      path_point->CopyFrom(discretized_path_.path_points().at(i));
      return true;
    }
    if (frenet_path_.points().at(i).s() < ref_s &&
        ref_s <= frenet_path_.points().at(i + 1).s()) {
      index = i;
      break;
    }
  }
  double r = (ref_s - frenet_path_.points().at(index).s()) /
             (frenet_path_.points().at(index + 1).s() -
              frenet_path_.points().at(index).s());

  const double discretized_path_s =
      discretized_path_.path_points().at(index).s() +
      r * (discretized_path_.path_points().at(index + 1).s() -
           discretized_path_.path_points().at(index).s());
  path_point->CopyFrom(
      discretized_path_.EvaluateUsingLinearApproximation(discretized_path_s));

  return true;
}

void PathData::Clear() {
  discretized_path_.Clear();
  frenet_path_.Clear();
  reference_line_ = nullptr;
}

std::string PathData::DebugString() const {
  const auto &path_points = discretized_path_.path_points();
  const auto limit =
      std::min(path_points.size(),
               static_cast<size_t>(FLAGS_trajectory_point_num_for_debug));

  return apollo::common::util::StrCat(
      "[\n", apollo::common::util::PrintDebugStringIter(
                 path_points.begin(), path_points.begin() + limit, ",\n"),
      "]\n");
}

bool PathData::SLToXY(const FrenetFramePath &frenet_path,
                      DiscretizedPath *const discretized_path) {
  DCHECK_NOTNULL(discretized_path);
  std::vector<common::PathPoint> path_points;
  for (const common::FrenetFramePoint &frenet_point : frenet_path.points()) {
    common::SLPoint sl_point;
    common::math::Vec2d cartesian_point;
    sl_point.set_s(frenet_point.s());
    sl_point.set_l(frenet_point.l());
    if (!reference_line_->SLToXY(sl_point, &cartesian_point)) {
      AERROR << "Fail to convert sl point to xy point";
      return false;
    }
    ReferencePoint ref_point =
        reference_line_->GetReferencePoint(frenet_point.s());
    double theta = CartesianFrenetConverter::CalculateTheta(
        ref_point.heading(), ref_point.kappa(), frenet_point.l(),
        frenet_point.dl());
    double kappa = CartesianFrenetConverter::CalculateKappa(
        ref_point.kappa(), ref_point.dkappa(), frenet_point.l(),
        frenet_point.dl(), frenet_point.ddl());

    common::PathPoint path_point = common::util::MakePathPoint(
        cartesian_point.x(), cartesian_point.y(), 0.0, theta, kappa, 0.0, 0.0);

    if (path_points.empty()) {
      path_point.set_s(0.0);
    } else {
      common::math::Vec2d last(path_points.back().x(), path_points.back().y());
      common::math::Vec2d current(path_point.x(), path_point.y());
      double distance = (last - current).Length();
      path_point.set_s(path_points.back().s() + distance);
    }
    path_points.push_back(std::move(path_point));
  }
  *discretized_path = DiscretizedPath(std::move(path_points));

  return true;
}

bool PathData::XYToSL(const DiscretizedPath &discretized_path,
                      FrenetFramePath *const frenet_path) {
  CHECK_NOTNULL(frenet_path);
  CHECK_NOTNULL(reference_line_);
  std::vector<common::FrenetFramePoint> frenet_frame_points;
  const double max_len = reference_line_->Length();
  for (const auto &path_point : discretized_path.path_points()) {
    SLPoint sl_point;
    if (!reference_line_->XYToSL({path_point.x(), path_point.y()}, &sl_point)) {
      AERROR << "Fail to transfer cartesian point to frenet point.";
      return false;
    }
    common::FrenetFramePoint frenet_point;
    // NOTICE: does not set dl and ddl here. Add if needed.
    frenet_point.set_s(std::max(0.0, std::min(sl_point.s(), max_len)));
    frenet_point.set_l(sl_point.l());
    frenet_frame_points.push_back(std::move(frenet_point));
  }
  *frenet_path = FrenetFramePath(std::move(frenet_frame_points));
  return true;
}

}  // namespace planning
}  // namespace apollo
<|endoftext|>


<commit_before>#include "graphics/sprite/sprite_painter.h"
#include "graphics/sprite/sprite.h"
#include "graphics/painter.h"
#include <gsl/gsl>

#include "graphics/material/material.h"
#include "graphics/text/text_renderer.h"
#include "halley/utils/algorithm.h"

using namespace Halley;

void MaterialRecycler::startFrame()
{
	for (auto& [k, v]: entries) {
		++v.age;
	}
	std_ex::erase_if_value(entries, [] (const Entry& e)
	{
		return e.age >= 3;
	});
}

std::shared_ptr<Material> MaterialRecycler::cloneMaterial(const Material& material)
{
	const auto iter = entries.find(material.getHash());
	if (iter == entries.end()) {
		auto m = material.clone();
		entries[material.getHash()] = Entry{ m, 0 };
		return m;
	} else {
		iter->second.age = 0;
		return iter->second.material;
	}
}

Sprite MaterialRecycler::clone(const Sprite& sprite)
{
	return sprite.clone().setMaterial(cloneMaterial(sprite.getMaterial()));
}

TextRenderer MaterialRecycler::clone(const TextRenderer& text)
{
	// TODO: clone materials
	return text.clone();
}

SpritePainterEntry::SpritePainterEntry(gsl::span<const Sprite> sprites, int mask, int layer, float tieBreaker, size_t insertOrder, std::optional<Rect4f> clip)
	: ptr(sprites.empty() ? nullptr : &sprites[0])
	, count(uint32_t(sprites.size()))
	, type(SpritePainterEntryType::SpriteRef)
	, layer(layer)
	, mask(mask)
	, tieBreaker(tieBreaker)
	, insertOrder(insertOrder)
	, clip(clip)
{}

SpritePainterEntry::SpritePainterEntry(gsl::span<const TextRenderer> texts, int mask, int layer, float tieBreaker, size_t insertOrder, std::optional<Rect4f> clip)
	: ptr(texts.empty() ? nullptr : &texts[0])
	, count(uint32_t(texts.size()))
	, type(SpritePainterEntryType::TextRef)
	, layer(layer)
	, mask(mask)
	, tieBreaker(tieBreaker)
	, insertOrder(insertOrder)
	, clip(clip)
{
}

SpritePainterEntry::SpritePainterEntry(SpritePainterEntryType type, size_t spriteIdx, size_t count, int mask, int layer, float tieBreaker, size_t insertOrder, std::optional<Rect4f> clip)
	: count(uint32_t(count))
	, index(static_cast<int>(spriteIdx))
	, type(type)
	, layer(layer)
	, mask(mask)
	, tieBreaker(tieBreaker)
	, insertOrder(insertOrder)
	, clip(clip)
{}

bool SpritePainterEntry::operator<(const SpritePainterEntry& o) const
{
	if (layer != o.layer) {
		return layer < o.layer;
	} else if (tieBreaker != o.tieBreaker) {
		return tieBreaker < o.tieBreaker;
	} else {
		return insertOrder < o.insertOrder;
	}
}

SpritePainterEntryType SpritePainterEntry::getType() const
{
	return type;
}

gsl::span<const Sprite> SpritePainterEntry::getSprites() const
{
	Expects(ptr != nullptr);
	Expects(type == SpritePainterEntryType::SpriteRef);
	return gsl::span<const Sprite>(static_cast<const Sprite*>(ptr), count);
}

gsl::span<const TextRenderer> SpritePainterEntry::getTexts() const
{
	Expects(ptr != nullptr);
	Expects(type == SpritePainterEntryType::TextRef);
	return gsl::span<const TextRenderer>(static_cast<const TextRenderer*>(ptr), count);
}

uint32_t SpritePainterEntry::getIndex() const
{
	Expects(ptr == nullptr);
	return index;
}

uint32_t SpritePainterEntry::getCount() const
{
	return count;
}

int SpritePainterEntry::getMask() const
{
	return mask;
}

const std::optional<Rect4f>& SpritePainterEntry::getClip() const
{
	return clip;
}

void SpritePainter::start(bool forceCopy)
{
	this->forceCopy = forceCopy;
	sprites.clear();
	cachedSprites.clear();
	cachedText.clear();
	materialRecycler.startFrame();
}

void SpritePainter::add(const Sprite& sprite, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	if (forceCopy) {
		addCopy(sprite, mask, layer, tieBreaker, clip);
	} else {
		Expects(mask >= 0);
		sprites.push_back(SpritePainterEntry(gsl::span<const Sprite>(&sprite, 1), mask, layer, tieBreaker, sprites.size(), std::move(clip)));
		dirty = true;
	}
}

void SpritePainter::addCopy(const Sprite& sprite, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	Expects(mask >= 0);
	sprites.push_back(SpritePainterEntry(SpritePainterEntryType::SpriteCached, cachedSprites.size(), 1, mask, layer, tieBreaker, sprites.size(), std::move(clip)));
	cachedSprites.push_back(materialRecycler.clone(sprite));
	dirty = true;
}

void SpritePainter::add(gsl::span<const Sprite> sprites, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	if (!sprites.empty()) {
		if (forceCopy) {
			addCopy(sprites, mask, layer, tieBreaker, clip);
		} else {
			Expects(mask >= 0);
			this->sprites.push_back(SpritePainterEntry(sprites, mask, layer, tieBreaker, this->sprites.size(), std::move(clip)));
			dirty = true;
		}
	}
}

void SpritePainter::addCopy(gsl::span<const Sprite> sprites, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	Expects(mask >= 0);
	if (!sprites.empty()) {
		this->sprites.push_back(SpritePainterEntry(SpritePainterEntryType::SpriteCached, cachedSprites.size(), sprites.size(), mask, layer, tieBreaker, this->sprites.size(), std::move(clip)));
		cachedSprites.reserve(cachedSprites.size() + sprites.size());
		for (auto& s: sprites) {
			cachedSprites.push_back(materialRecycler.clone(s));
		}
		dirty = true;
	}
}

void SpritePainter::add(const TextRenderer& text, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	if (forceCopy) {
		addCopy(text, mask, layer, tieBreaker, clip);
	} else {
		Expects(mask >= 0);
		sprites.push_back(SpritePainterEntry(gsl::span<const TextRenderer>(&text, 1), mask, layer, tieBreaker, sprites.size(), std::move(clip)));
		dirty = true;
	}
}

void SpritePainter::addCopy(const TextRenderer& text, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	Expects(mask >= 0);
	sprites.push_back(SpritePainterEntry(SpritePainterEntryType::TextCached, cachedText.size(), 1, mask, layer, tieBreaker, sprites.size(), std::move(clip)));
	cachedText.push_back(materialRecycler.clone(text));
	dirty = true;
}

void SpritePainter::add(SpritePainterEntry::Callback callback, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	Expects(mask >= 0);
	sprites.push_back(SpritePainterEntry(SpritePainterEntryType::Callback, callbacks.size(), 1, mask, layer, tieBreaker, sprites.size(), std::move(clip)));
	callbacks.push_back(std::move(callback));
	dirty = true;
}

void SpritePainter::draw(int mask, Painter& painter)
{
	if (dirty) {
		// TODO: implement hierarchical bucketing.
		// - one bucket per layer
		// - for each layer, one bucket per vertical band of the screen (32px or so)
		// - sort each leaf bucket
		std::sort(sprites.begin(), sprites.end()); // lol
		dirty = false;
	}

	// View
	const auto& cam = painter.getCurrentCamera();
	Rect4f view = cam.getClippingRectangle();

	// Draw!
	for (auto& s : sprites) {
		if ((s.getMask() & mask) != 0) {
			const auto type = s.getType();

			if (type == SpritePainterEntryType::SpriteRef) {
				draw(s.getSprites(), painter, view, s.getClip());
			} else if (type == SpritePainterEntryType::SpriteCached) {
				draw(gsl::span<const Sprite>(cachedSprites.data() + s.getIndex(), s.getCount()), painter, view, s.getClip());
			} else if (type == SpritePainterEntryType::TextRef) {
				draw(s.getTexts(), painter, view, s.getClip());
			} else if (type == SpritePainterEntryType::TextCached) {
				draw(gsl::span<const TextRenderer>(cachedText.data() + s.getIndex(), s.getCount()), painter, view, s.getClip());
			} else if (type == SpritePainterEntryType::Callback) {
				draw(callbacks.at(s.getIndex()), painter, s.getClip());
			}
		}
	}
	painter.flush();
}

void SpritePainter::draw(gsl::span<const Sprite> sprites, Painter& painter, Rect4f view, const std::optional<Rect4f>& clip) const
{
	for (const auto& sprite: sprites) {
		if (sprite.isInView(view)) {
			sprite.draw(painter, clip);
		}
	}
}

void SpritePainter::draw(gsl::span<const TextRenderer> texts, Painter& painter, Rect4f view, const std::optional<Rect4f>& clip) const
{
	for (const auto& text: texts) {
		text.draw(painter, clip);
	}
}

void SpritePainter::draw(const SpritePainterEntry::Callback& callback, Painter& painter, const std::optional<Rect4f>& clip) const
{
	if (clip) {
		painter.setRelativeClip(clip.value());
	}
	callback(painter);
	if (clip) {
		painter.setClip();
	}
}
<commit_msg>Fix crash on material recycler<commit_after>#include "graphics/sprite/sprite_painter.h"
#include "graphics/sprite/sprite.h"
#include "graphics/painter.h"
#include <gsl/gsl>

#include "graphics/material/material.h"
#include "graphics/text/text_renderer.h"
#include "halley/utils/algorithm.h"

using namespace Halley;

void MaterialRecycler::startFrame()
{
	for (auto& [k, v]: entries) {
		++v.age;
	}
	std_ex::erase_if_value(entries, [] (const Entry& e)
	{
		return e.age >= 3;
	});
}

std::shared_ptr<Material> MaterialRecycler::cloneMaterial(const Material& material)
{
	const auto iter = entries.find(material.getHash());
	if (iter == entries.end()) {
		auto m = material.clone();
		entries[material.getHash()] = Entry{ m, 0 };
		return m;
	} else {
		iter->second.age = 0;
		return iter->second.material;
	}
}

Sprite MaterialRecycler::clone(const Sprite& sprite)
{
	auto s2 = sprite.clone();
	if (sprite.hasMaterial()) {
		s2.setMaterial(cloneMaterial(sprite.getMaterial()));
	}
	return s2;
}

TextRenderer MaterialRecycler::clone(const TextRenderer& text)
{
	// TODO: clone materials
	return text.clone();
}

SpritePainterEntry::SpritePainterEntry(gsl::span<const Sprite> sprites, int mask, int layer, float tieBreaker, size_t insertOrder, std::optional<Rect4f> clip)
	: ptr(sprites.empty() ? nullptr : &sprites[0])
	, count(uint32_t(sprites.size()))
	, type(SpritePainterEntryType::SpriteRef)
	, layer(layer)
	, mask(mask)
	, tieBreaker(tieBreaker)
	, insertOrder(insertOrder)
	, clip(clip)
{}

SpritePainterEntry::SpritePainterEntry(gsl::span<const TextRenderer> texts, int mask, int layer, float tieBreaker, size_t insertOrder, std::optional<Rect4f> clip)
	: ptr(texts.empty() ? nullptr : &texts[0])
	, count(uint32_t(texts.size()))
	, type(SpritePainterEntryType::TextRef)
	, layer(layer)
	, mask(mask)
	, tieBreaker(tieBreaker)
	, insertOrder(insertOrder)
	, clip(clip)
{
}

SpritePainterEntry::SpritePainterEntry(SpritePainterEntryType type, size_t spriteIdx, size_t count, int mask, int layer, float tieBreaker, size_t insertOrder, std::optional<Rect4f> clip)
	: count(uint32_t(count))
	, index(static_cast<int>(spriteIdx))
	, type(type)
	, layer(layer)
	, mask(mask)
	, tieBreaker(tieBreaker)
	, insertOrder(insertOrder)
	, clip(clip)
{}

bool SpritePainterEntry::operator<(const SpritePainterEntry& o) const
{
	if (layer != o.layer) {
		return layer < o.layer;
	} else if (tieBreaker != o.tieBreaker) {
		return tieBreaker < o.tieBreaker;
	} else {
		return insertOrder < o.insertOrder;
	}
}

SpritePainterEntryType SpritePainterEntry::getType() const
{
	return type;
}

gsl::span<const Sprite> SpritePainterEntry::getSprites() const
{
	Expects(ptr != nullptr);
	Expects(type == SpritePainterEntryType::SpriteRef);
	return gsl::span<const Sprite>(static_cast<const Sprite*>(ptr), count);
}

gsl::span<const TextRenderer> SpritePainterEntry::getTexts() const
{
	Expects(ptr != nullptr);
	Expects(type == SpritePainterEntryType::TextRef);
	return gsl::span<const TextRenderer>(static_cast<const TextRenderer*>(ptr), count);
}

uint32_t SpritePainterEntry::getIndex() const
{
	Expects(ptr == nullptr);
	return index;
}

uint32_t SpritePainterEntry::getCount() const
{
	return count;
}

int SpritePainterEntry::getMask() const
{
	return mask;
}

const std::optional<Rect4f>& SpritePainterEntry::getClip() const
{
	return clip;
}

void SpritePainter::start(bool forceCopy)
{
	this->forceCopy = forceCopy;
	sprites.clear();
	cachedSprites.clear();
	cachedText.clear();
	materialRecycler.startFrame();
}

void SpritePainter::add(const Sprite& sprite, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	if (forceCopy) {
		addCopy(sprite, mask, layer, tieBreaker, clip);
	} else {
		Expects(mask >= 0);
		sprites.push_back(SpritePainterEntry(gsl::span<const Sprite>(&sprite, 1), mask, layer, tieBreaker, sprites.size(), std::move(clip)));
		dirty = true;
	}
}

void SpritePainter::addCopy(const Sprite& sprite, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	Expects(mask >= 0);
	sprites.push_back(SpritePainterEntry(SpritePainterEntryType::SpriteCached, cachedSprites.size(), 1, mask, layer, tieBreaker, sprites.size(), std::move(clip)));
	cachedSprites.push_back(materialRecycler.clone(sprite));
	dirty = true;
}

void SpritePainter::add(gsl::span<const Sprite> sprites, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	if (!sprites.empty()) {
		if (forceCopy) {
			addCopy(sprites, mask, layer, tieBreaker, clip);
		} else {
			Expects(mask >= 0);
			this->sprites.push_back(SpritePainterEntry(sprites, mask, layer, tieBreaker, this->sprites.size(), std::move(clip)));
			dirty = true;
		}
	}
}

void SpritePainter::addCopy(gsl::span<const Sprite> sprites, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	Expects(mask >= 0);
	if (!sprites.empty()) {
		this->sprites.push_back(SpritePainterEntry(SpritePainterEntryType::SpriteCached, cachedSprites.size(), sprites.size(), mask, layer, tieBreaker, this->sprites.size(), std::move(clip)));
		cachedSprites.reserve(cachedSprites.size() + sprites.size());
		for (auto& s: sprites) {
			cachedSprites.push_back(materialRecycler.clone(s));
		}
		dirty = true;
	}
}

void SpritePainter::add(const TextRenderer& text, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	if (forceCopy) {
		addCopy(text, mask, layer, tieBreaker, clip);
	} else {
		Expects(mask >= 0);
		sprites.push_back(SpritePainterEntry(gsl::span<const TextRenderer>(&text, 1), mask, layer, tieBreaker, sprites.size(), std::move(clip)));
		dirty = true;
	}
}

void SpritePainter::addCopy(const TextRenderer& text, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	Expects(mask >= 0);
	sprites.push_back(SpritePainterEntry(SpritePainterEntryType::TextCached, cachedText.size(), 1, mask, layer, tieBreaker, sprites.size(), std::move(clip)));
	cachedText.push_back(materialRecycler.clone(text));
	dirty = true;
}

void SpritePainter::add(SpritePainterEntry::Callback callback, int mask, int layer, float tieBreaker, std::optional<Rect4f> clip)
{
	Expects(mask >= 0);
	sprites.push_back(SpritePainterEntry(SpritePainterEntryType::Callback, callbacks.size(), 1, mask, layer, tieBreaker, sprites.size(), std::move(clip)));
	callbacks.push_back(std::move(callback));
	dirty = true;
}

void SpritePainter::draw(int mask, Painter& painter)
{
	if (dirty) {
		// TODO: implement hierarchical bucketing.
		// - one bucket per layer
		// - for each layer, one bucket per vertical band of the screen (32px or so)
		// - sort each leaf bucket
		std::sort(sprites.begin(), sprites.end()); // lol
		dirty = false;
	}

	// View
	const auto& cam = painter.getCurrentCamera();
	Rect4f view = cam.getClippingRectangle();

	// Draw!
	for (auto& s : sprites) {
		if ((s.getMask() & mask) != 0) {
			const auto type = s.getType();

			if (type == SpritePainterEntryType::SpriteRef) {
				draw(s.getSprites(), painter, view, s.getClip());
			} else if (type == SpritePainterEntryType::SpriteCached) {
				draw(gsl::span<const Sprite>(cachedSprites.data() + s.getIndex(), s.getCount()), painter, view, s.getClip());
			} else if (type == SpritePainterEntryType::TextRef) {
				draw(s.getTexts(), painter, view, s.getClip());
			} else if (type == SpritePainterEntryType::TextCached) {
				draw(gsl::span<const TextRenderer>(cachedText.data() + s.getIndex(), s.getCount()), painter, view, s.getClip());
			} else if (type == SpritePainterEntryType::Callback) {
				draw(callbacks.at(s.getIndex()), painter, s.getClip());
			}
		}
	}
	painter.flush();
}

void SpritePainter::draw(gsl::span<const Sprite> sprites, Painter& painter, Rect4f view, const std::optional<Rect4f>& clip) const
{
	for (const auto& sprite: sprites) {
		if (sprite.isInView(view)) {
			sprite.draw(painter, clip);
		}
	}
}

void SpritePainter::draw(gsl::span<const TextRenderer> texts, Painter& painter, Rect4f view, const std::optional<Rect4f>& clip) const
{
	for (const auto& text: texts) {
		text.draw(painter, clip);
	}
}

void SpritePainter::draw(const SpritePainterEntry::Callback& callback, Painter& painter, const std::optional<Rect4f>& clip) const
{
	if (clip) {
		painter.setRelativeClip(clip.value());
	}
	callback(painter);
	if (clip) {
		painter.setClip();
	}
}
<|endoftext|>