<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2017 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 <modules/crystalvisualization/processors/structuremesh.h>
#include <inviwo/core/datastructures/geometry/basicmesh.h>

#include <inviwo/core/interaction/events/pickingevent.h>
#include <inviwo/core/interaction/events/mouseevent.h>
#include <inviwo/core/datastructures/buffer/bufferramprecision.h>
#include <inviwo/core/datastructures/buffer/buffer.h>

namespace inviwo {

// The Class Identifier has to be globally unique. Use a reverse DNS naming scheme
const ProcessorInfo StructureMesh::processorInfo_{
    "org.inviwo.StructureMesh",      // Class identifier
    "Structure Mesh",                // Display name
    "Crystal",              // Category
    CodeState::Experimental,  // Code state
    Tags::None,               // Tags
};
const ProcessorInfo StructureMesh::getProcessorInfo() const {
    return processorInfo_;
}

StructureMesh::StructureMesh()
    : Processor()
    , structure_("coordinates")
    , mesh_("mesh")
    , scalingFactor_("scalingFactor", "Scaling factor", 1.f)
    , fullMesh_("fullMesh", "Full mesh", false)
    , enablePicking_("enablePicking", "Enable Picking", false)
    , spherePicking_(this, 0, [&](PickingEvent* p) { handlePicking(p); })
    , inds_("inds", "Picked atoms") {
    addPort(structure_);
    addPort(mesh_);
    addProperty(scalingFactor_);
    addProperty(fullMesh_);
    addProperty(enablePicking_);
    addProperty(inds_);

    structure_.onChange([&](){
        const auto data = structure_.getVectorData();
        for(int i = colors_.size(); i < data.size(); ++i) {
            colors_.push_back(std::make_unique<FloatVec4Property>("color" + toString(i), "Color " + toString(i),
                                                vec4(1.0f, 1.0f, 1.0f, 1.0f), vec4(0.0f), vec4(1.0f), vec4(0.01f),
                                                InvalidationLevel::InvalidOutput, PropertySemantics::Color));
            addProperty(colors_.back().get(), false);

            radii_.push_back(std::make_unique<FloatProperty>("radius"+ toString(i), "Atom Radius"+ toString(i), 1.0f));
            addProperty(radii_.back().get(), false);
        }
        int numSpheres = 0;
        for (const auto &strucs : structure_)
            numSpheres += strucs->size();
        spherePicking_.resize(numSpheres);

    });

}

void StructureMesh::process() {
    if (fullMesh_.get()) {
	auto mesh = std::make_shared<BasicMesh>();
	mesh_.setData(mesh);
	size_t ind = 0;
	for (const auto &strucs : structure_) {
	    for (long long j = 0; j < static_cast<long long>(strucs->size()); ++j) {
		auto center = scalingFactor_.get()*strucs->at(j);
		BasicMesh::sphere(center, radii_[ind]->get(), colors_[ind]->get(), mesh);
	    }
	    ++ind;
	}
    } else {

        const bool changed = std::any_of(radii_.begin(), radii_.end(),
                                         [](const std::unique_ptr<FloatProperty>& property){
                                            return property->isModified();
                                            }) ||
                              std::any_of(colors_.begin(), colors_.end(),
                                         [](const std::unique_ptr<FloatVec4Property>& property){
                                            return property->isModified();
                                            }) ||
                              enablePicking_.isModified();

        if (structure_.isChanged() || scalingFactor_.isModified() || changed) {

            int numSpheres = 0;
            for (const auto &strucs : structure_)
                numSpheres += strucs->size();

            auto mesh = std::make_shared<Mesh>(DrawType::Points, ConnectivityType::None);

            auto vertexRAM = std::make_shared<BufferRAMPrecision<vec3>>(numSpheres);
            auto colorRAM = std::make_shared<BufferRAMPrecision<vec4>>(numSpheres);
            auto radiiRAM = std::make_shared<BufferRAMPrecision<float>>(numSpheres);

            colorBuffer_ = colorRAM;

            mesh->addBuffer(Mesh::BufferInfo(BufferType::PositionAttrib),
                            std::make_shared<Buffer<vec3>>(vertexRAM));
            mesh->addBuffer(Mesh::BufferInfo(BufferType::ColorAttrib),
                            std::make_shared<Buffer<vec4>>(colorRAM));
            mesh->addBuffer(Mesh::BufferInfo(BufferType::NumberOfBufferTypes, 5),
                            std::make_shared<Buffer<float>>(radiiRAM));

            auto& vertices = vertexRAM->getDataContainer();
            auto& colors = colorRAM->getDataContainer();
            auto& radii = radiiRAM->getDataContainer();

            mesh_.setData(mesh);
            size_t portInd = 0;
            size_t sphereInd = 0;
            for (const auto &strucs : structure_) {
                for (long long j = 0; j < static_cast<long long>(strucs->size()); ++j) {
                    auto center = strucs->at(j);
                    vertices[sphereInd] = scalingFactor_.get()*center;
                    colors[sphereInd] = colors_[portInd]->get();
                    radii[sphereInd] = radii_[portInd]->get();
                    ++sphereInd;
                }
                ++portInd;
            }
            if (enablePicking_.get()) {
                //Set alpha-layer of picked atoms
                if(!inds_.get().empty()) {
                    for (const auto &ind : inds_.get()) {
                        colors[ind].w = 0.5;
                    }
                }
                auto pickingRAM = std::make_shared<BufferRAMPrecision<uint32_t>>(numSpheres);
                auto& data = pickingRAM->getDataContainer();
                // fill in picking IDs
                std::iota(data.begin(), data.end(), static_cast<uint32_t>(spherePicking_.getPickingId(0)));

                mesh->addBuffer(Mesh::BufferInfo(BufferType::NumberOfBufferTypes, 4),
                                std::make_shared<Buffer<uint32_t>>(pickingRAM));
            }
        }

    }
}

void StructureMesh::handlePicking(PickingEvent* p) {
    if (enablePicking_.get()) {
        if (p->getState() == PickingState::Updated &&
            p->getEvent()->hash() == MouseEvent::chash()) {
            auto me = p->getEventAs<MouseEvent>();
            if (me->buttonState() & MouseButton::Left) {
                auto& color = colorBuffer_->getDataContainer();
                std::vector<int> temp = inds_.get();
                auto picked = p->getPickedId();

                if( std::none_of(temp.begin(), temp.end(), [&](unsigned int i){return i == picked;}) ) {
                    temp.push_back(picked);
                    color[picked].w = 0.5;
                } else {
                    temp.erase(std::remove(temp.begin(), temp.end(), picked), temp.end());
                    color[picked].w = 1;
                }
                inds_.set(temp);

                colorBuffer_->getOwner()->invalidateAllOther(colorBuffer_.get());
                invalidate(InvalidationLevel::InvalidOutput);
                p->markAsUsed();
            }
        }
    }

}

} // namespace

<commit_msg>fix problem with picking while moving mouse<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2017 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 <modules/crystalvisualization/processors/structuremesh.h>
#include <inviwo/core/datastructures/geometry/basicmesh.h>

#include <inviwo/core/interaction/events/pickingevent.h>
#include <inviwo/core/interaction/events/mouseevent.h>
#include <inviwo/core/datastructures/buffer/bufferramprecision.h>
#include <inviwo/core/datastructures/buffer/buffer.h>

namespace inviwo {

// The Class Identifier has to be globally unique. Use a reverse DNS naming scheme
const ProcessorInfo StructureMesh::processorInfo_{
    "org.inviwo.StructureMesh",      // Class identifier
    "Structure Mesh",                // Display name
    "Crystal",              // Category
    CodeState::Experimental,  // Code state
    Tags::None,               // Tags
};
const ProcessorInfo StructureMesh::getProcessorInfo() const {
    return processorInfo_;
}

StructureMesh::StructureMesh()
    : Processor()
    , structure_("coordinates")
    , mesh_("mesh")
    , scalingFactor_("scalingFactor", "Scaling factor", 1.f)
    , fullMesh_("fullMesh", "Full mesh", false)
    , enablePicking_("enablePicking", "Enable Picking", false)
    , spherePicking_(this, 0, [&](PickingEvent* p) { handlePicking(p); })
    , inds_("inds", "Picked atoms") {
    addPort(structure_);
    addPort(mesh_);
    addProperty(scalingFactor_);
    addProperty(fullMesh_);
    addProperty(enablePicking_);
    addProperty(inds_);

    structure_.onChange([&](){
        const auto data = structure_.getVectorData();
        for(int i = colors_.size(); i < data.size(); ++i) {
            colors_.push_back(std::make_unique<FloatVec4Property>("color" + toString(i), "Color " + toString(i),
                                                vec4(1.0f, 1.0f, 1.0f, 1.0f), vec4(0.0f), vec4(1.0f), vec4(0.01f),
                                                InvalidationLevel::InvalidOutput, PropertySemantics::Color));
            addProperty(colors_.back().get(), false);

            radii_.push_back(std::make_unique<FloatProperty>("radius"+ toString(i), "Atom Radius"+ toString(i), 1.0f));
            addProperty(radii_.back().get(), false);
        }
        int numSpheres = 0;
        for (const auto &strucs : structure_)
            numSpheres += strucs->size();
        spherePicking_.resize(numSpheres);

    });

}

void StructureMesh::process() {
    if (fullMesh_.get()) {
	auto mesh = std::make_shared<BasicMesh>();
	mesh_.setData(mesh);
	size_t ind = 0;
	for (const auto &strucs : structure_) {
	    for (long long j = 0; j < static_cast<long long>(strucs->size()); ++j) {
		auto center = scalingFactor_.get()*strucs->at(j);
		BasicMesh::sphere(center, radii_[ind]->get(), colors_[ind]->get(), mesh);
	    }
	    ++ind;
	}
    } else {

        const bool changed = std::any_of(radii_.begin(), radii_.end(),
                                         [](const std::unique_ptr<FloatProperty>& property){
                                            return property->isModified();
                                            }) ||
                              std::any_of(colors_.begin(), colors_.end(),
                                         [](const std::unique_ptr<FloatVec4Property>& property){
                                            return property->isModified();
                                            }) ||
                              enablePicking_.isModified();

        if (structure_.isChanged() || scalingFactor_.isModified() || changed) {

            int numSpheres = 0;
            for (const auto &strucs : structure_)
                numSpheres += strucs->size();

            auto mesh = std::make_shared<Mesh>(DrawType::Points, ConnectivityType::None);

            auto vertexRAM = std::make_shared<BufferRAMPrecision<vec3>>(numSpheres);
            auto colorRAM = std::make_shared<BufferRAMPrecision<vec4>>(numSpheres);
            auto radiiRAM = std::make_shared<BufferRAMPrecision<float>>(numSpheres);

            colorBuffer_ = colorRAM;

            mesh->addBuffer(Mesh::BufferInfo(BufferType::PositionAttrib),
                            std::make_shared<Buffer<vec3>>(vertexRAM));
            mesh->addBuffer(Mesh::BufferInfo(BufferType::ColorAttrib),
                            std::make_shared<Buffer<vec4>>(colorRAM));
            mesh->addBuffer(Mesh::BufferInfo(BufferType::NumberOfBufferTypes, 5),
                            std::make_shared<Buffer<float>>(radiiRAM));

            auto& vertices = vertexRAM->getDataContainer();
            auto& colors = colorRAM->getDataContainer();
            auto& radii = radiiRAM->getDataContainer();

            mesh_.setData(mesh);
            size_t portInd = 0;
            size_t sphereInd = 0;
            for (const auto &strucs : structure_) {
                for (long long j = 0; j < static_cast<long long>(strucs->size()); ++j) {
                    auto center = strucs->at(j);
                    vertices[sphereInd] = scalingFactor_.get()*center;
                    colors[sphereInd] = colors_[portInd]->get();
                    radii[sphereInd] = radii_[portInd]->get();
                    ++sphereInd;
                }
                ++portInd;
            }
            if (enablePicking_.get()) {
                //Set alpha-layer of picked atoms
                if(!inds_.get().empty()) {
                    for (const auto &ind : inds_.get()) {
                        colors[ind].w = 0.5;
                    }
                }
                auto pickingRAM = std::make_shared<BufferRAMPrecision<uint32_t>>(numSpheres);
                auto& data = pickingRAM->getDataContainer();
                // fill in picking IDs
                std::iota(data.begin(), data.end(), static_cast<uint32_t>(spherePicking_.getPickingId(0)));

                mesh->addBuffer(Mesh::BufferInfo(BufferType::NumberOfBufferTypes, 4),
                                std::make_shared<Buffer<uint32_t>>(pickingRAM));
            }
        }

    }
}

void StructureMesh::handlePicking(PickingEvent* p) {
    if (enablePicking_.get()) {
        if (p->getState() == PickingState::Updated &&
            p->getEvent()->hash() == MouseEvent::chash()) {
            auto me = p->getEventAs<MouseEvent>();
            if ((me->buttonState() & MouseButton::Left) && me->state() != MouseState::Move) {
                auto& color = colorBuffer_->getDataContainer();
                std::vector<int> temp = inds_.get();
                auto picked = p->getPickedId();

                if( std::none_of(temp.begin(), temp.end(), [&](unsigned int i){return i == picked;}) ) {
                    temp.push_back(picked);
                    color[picked].w = 0.5;
                } else {
                    temp.erase(std::remove(temp.begin(), temp.end(), picked), temp.end());
                    color[picked].w = 1;
                }
                inds_.set(temp);

                colorBuffer_->getOwner()->invalidateAllOther(colorBuffer_.get());
                invalidate(InvalidationLevel::InvalidOutput);
                p->markAsUsed();
            }
        }
    }

}

} // namespace

<|endoftext|>


<commit_before>//==============================================================================
// HeunSolver plugin
//==============================================================================

#include "heunsolver.h"
#include "heunsolverplugin.h"

//==============================================================================

namespace OpenCOR {
namespace HeunSolver {

//==============================================================================

PLUGININFO_FUNC HeunSolverPluginInfo()
{
    Descriptions descriptions;

    descriptions.insert("en", QString::fromUtf8("A plugin which implements the Heun's method to solve ODEs"));
    descriptions.insert("fr", QString::fromUtf8("Une extension qui implémente la méthode de Heun pour résoudre des EDOs"));

    return new PluginInfo(PluginInfo::InterfaceVersion001,
                          PluginInfo::General,
                          PluginInfo::Simulation,
                          true,
                          QStringList() << "CoreSolver",
                          descriptions);
}

//==============================================================================

Solver::Type HeunSolverPlugin::type() const
{
    // Return the type of the solver

    return Solver::Ode;
}

//==============================================================================

QString HeunSolverPlugin::name() const
{
    // Return the name of the solver

    return "Heun";
}

//==============================================================================

Solver::Properties HeunSolverPlugin::properties() const
{
    // Return the properties supported by the solver

    Solver::Properties res = Solver::Properties();
    Descriptions stepPropertyDescriptions;

    stepPropertyDescriptions.insert("en", QString::fromUtf8("Step"));
    stepPropertyDescriptions.insert("fr", QString::fromUtf8("Pas"));

    res.append(Solver::Property(Solver::Double, StepId, stepPropertyDescriptions, StepDefaultValue, true));

    return res;
}

//==============================================================================

void * HeunSolverPlugin::instance() const
{
    // Create and return an instance of the solver

    return new HeunSolver();
}

//==============================================================================

}   // namespace HeunSolver
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Some minor cleaning up.<commit_after>//==============================================================================
// HeunSolver plugin
//==============================================================================

#include "heunsolver.h"
#include "heunsolverplugin.h"

//==============================================================================

namespace OpenCOR {
namespace HeunSolver {

//==============================================================================

PLUGININFO_FUNC HeunSolverPluginInfo()
{
    Descriptions descriptions;

    descriptions.insert("en", QString::fromUtf8("A plugin which implements the Heun's method to solve ODEs"));
    descriptions.insert("fr", QString::fromUtf8("Une extension qui implémente la méthode de Heun pour résoudre des EDOs"));

    return new PluginInfo(PluginInfo::InterfaceVersion001,
                          PluginInfo::General,
                          PluginInfo::Simulation,
                          true,
                          QStringList() << "CoreSolver",
                          descriptions);
}

//==============================================================================

Solver::Type HeunSolverPlugin::type() const
{
    // Return the type of the solver

    return Solver::Ode;
}

//==============================================================================

QString HeunSolverPlugin::name() const
{
    // Return the name of the solver

    return "Heun";
}

//==============================================================================

Solver::Properties HeunSolverPlugin::properties() const
{
    // Return the properties supported by the solver

    Solver::Properties res = Solver::Properties();
    Descriptions stepPropertyDescriptions;

    stepPropertyDescriptions.insert("en", QString::fromUtf8("Step"));
    stepPropertyDescriptions.insert("fr", QString::fromUtf8("Pas"));

    res.append(Solver::Property(Solver::Double, StepId, stepPropertyDescriptions, StepDefaultValue, true));

    return res;
}

//==============================================================================

void * HeunSolverPlugin::instance() const
{
    // Create and return an instance of the solver

    return new HeunSolver();
}

//==============================================================================

}   // namespace HeunSolver
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>// Copyright © 2010-2015 The CefSharp Project. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
#pragma once

#include "Stdafx.h"

#include "CefBrowserWrapper.h"
#include "CefAppUnmanagedWrapper.h"
#include "JavascriptRootObjectWrapper.h"
#include "Serialization\V8Serialization.h"
#include "Serialization\JsObjectsSerialization.h"
#include "Async/JavascriptAsyncMethodCallback.h"
#include "..\CefSharp.Core\Internals\Messaging\Messages.h"
#include "..\CefSharp.Core\Internals\Serialization\Primitives.h"

using namespace System::Diagnostics;
using namespace System::Collections::Generic;
using namespace CefSharp::Internals::Messaging;
using namespace CefSharp::Internals::Serialization;

namespace CefSharp
{
    const CefString CefAppUnmanagedWrapper::kPromiseCreatorFunction = "cefsharp_CreatePromise";
    const CefString CefAppUnmanagedWrapper::kPromiseCreatorScript = ""
        "function cefsharp_CreatePromise() {"
        "   var object = {};"
        "   var promise = new Promise(function(resolve, reject) {"
        "       object.resolve = resolve;object.reject = reject;"
        "   });"
        "   return{ p: promise, res : object.resolve,  rej: object.reject};"
        "}";

    CefRefPtr<CefRenderProcessHandler> CefAppUnmanagedWrapper::GetRenderProcessHandler()
    {
        return this;
    };

    // CefRenderProcessHandler
    void CefAppUnmanagedWrapper::OnBrowserCreated(CefRefPtr<CefBrowser> browser)
    {
        auto wrapper = gcnew CefBrowserWrapper(browser);
        _onBrowserCreated->Invoke(wrapper);

        //Multiple CefBrowserWrappers created when opening popups
        _browserWrappers->TryAdd(browser->GetIdentifier(), wrapper);
    }

    void CefAppUnmanagedWrapper::OnBrowserDestroyed(CefRefPtr<CefBrowser> browser)
    {
        CefBrowserWrapper^ wrapper;
        if (_browserWrappers->TryRemove(browser->GetIdentifier(), wrapper))
        {
            _onBrowserDestroyed->Invoke(wrapper);
            delete wrapper;
        }
    };

    void CefAppUnmanagedWrapper::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
    {
        auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier(), true);

        if (!Object::ReferenceEquals(_javascriptRootObject, nullptr) || !Object::ReferenceEquals(_javascriptAsyncRootObject, nullptr))
        {
            auto rootObjectWrappers = browserWrapper->JavascriptRootObjectWrappers;
            auto frameId = frame->GetIdentifier();

            if (rootObjectWrappers->ContainsKey(frameId))
            {
                LOG(WARNING) << "A context has been created for the same browser / frame without context released called previously";
            }
            else
            {
                auto rootObject = gcnew JavascriptRootObjectWrapper(browser->GetIdentifier(), browserWrapper->BrowserProcess);
                rootObject->Bind(_javascriptRootObject, _javascriptAsyncRootObject, context->GetGlobal());

                rootObjectWrappers->TryAdd(frameId, rootObject);
            }
        }
    };

    void CefAppUnmanagedWrapper::OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
    {
        auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier(), true);

        auto rootObjectWrappers = browserWrapper->JavascriptRootObjectWrappers;

        JavascriptRootObjectWrapper^ wrapper;
        if (rootObjectWrappers->TryRemove(frame->GetIdentifier(), wrapper))
        {
            delete wrapper;
        }
    };

    CefBrowserWrapper^ CefAppUnmanagedWrapper::FindBrowserWrapper(int browserId, bool mustExist)
    {
        CefBrowserWrapper^ wrapper = nullptr;

        _browserWrappers->TryGetValue(browserId, wrapper);

        if (mustExist && wrapper == nullptr)
        {
            throw gcnew InvalidOperationException(String::Format("Failed to identify BrowserWrapper in OnContextCreated. : {0}", browserId));
        }

        return wrapper;
    }

    bool CefAppUnmanagedWrapper::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefProcessId sourceProcessId, CefRefPtr<CefProcessMessage> message)
    {
        auto handled = false;
        auto name = message->GetName();
        auto argList = message->GetArgumentList();

        auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier(), false);
        //Error handling for missing/closed browser
        if (browserWrapper == nullptr)
        {
            if (name == kJavascriptCallbackDestroyRequest ||
                name == kJavascriptRootObjectRequest ||
                name == kJavascriptAsyncMethodCallResponse)
            {
                //If we can't find the browser wrapper then we'll just
                //ignore this as it's likely already been disposed of
                return true;
            }

            CefString responseName;
            if (name == kEvaluateJavascriptRequest)
            {
                responseName = kEvaluateJavascriptResponse;
            }
            else if (name == kJavascriptCallbackRequest)
            {
                responseName = kJavascriptCallbackResponse;
            }
            else
            {
                //TODO: Should be throw an exception here? It's likely that only a CefSharp developer would see this
                // when they added a new message and havn't yet implemented the render process functionality.
                throw gcnew Exception("Unsupported message type");
            }

            auto callbackId = GetInt64(argList, 1);
            auto response = CefProcessMessage::Create(responseName);
            auto responseArgList = response->GetArgumentList();
            auto errorMessage = String::Format("Request BrowserId : {0} not found it's likely the browser is already closed", browser->GetIdentifier());

            //success: false
            responseArgList->SetBool(0, false);
            SetInt64(callbackId, responseArgList, 1);
            responseArgList->SetString(2, StringUtils::ToNative(errorMessage));
            browser->SendProcessMessage(sourceProcessId, response);

            return true;
        }

        //these messages are roughly handled the same way
        if (name == kEvaluateJavascriptRequest || name == kJavascriptCallbackRequest)
        {
            bool success;
            CefRefPtr<CefV8Value> result;
            CefString errorMessage;
            CefRefPtr<CefProcessMessage> response;
            //both messages have the frameId stored at 0 and callbackId stored at index 1
            auto frameId = GetInt64(argList, 0);
            int64 callbackId = GetInt64(argList, 1);

            JavascriptRootObjectWrapper^ rootObjectWrapper;
            auto rootObjectWrappers = browserWrapper->JavascriptRootObjectWrappers->TryGetValue(frameId, rootObjectWrapper);
            auto callbackRegistry = rootObjectWrapper == nullptr ? nullptr : rootObjectWrapper->CallbackRegistry;

            if (name == kEvaluateJavascriptRequest)
            {
                auto script = argList->GetString(2);

                response = CefProcessMessage::Create(kEvaluateJavascriptResponse);

                auto frame = browser->GetFrame(frameId);
                if (frame.get())
                {
                    auto context = frame->GetV8Context();

                    if (context.get() && context->Enter())
                    {
                        try
                        {
                            CefRefPtr<CefV8Exception> exception;
                            success = context->Eval(script, result, exception);

                            //we need to do this here to be able to store the v8context
                            if (success)
                            {
                                auto responseArgList = response->GetArgumentList();
                                SerializeV8Object(result, responseArgList, 2, callbackRegistry);
                            }
                            else
                            {
                                errorMessage = exception->GetMessage();
                            }
                        }
                        finally
                        {
                            context->Exit();
                        }
                    }
                    else
                    {
                        errorMessage = "Unable to Enter Context";
                    }
                }
                else
                {
                    errorMessage = "Unable to Get Frame matching Id";
                }
            }
            else
            {
                auto jsCallbackId = GetInt64(argList, 2);
                auto parameterList = argList->GetList(3);
                CefV8ValueList params;
                for (CefV8ValueList::size_type i = 0; i < parameterList->GetSize(); i++)
                {
                    params.push_back(DeserializeV8Object(parameterList, static_cast<int>(i)));
                }

                response = CefProcessMessage::Create(kJavascriptCallbackResponse);

                auto callbackWrapper = callbackRegistry->FindWrapper(jsCallbackId);
                auto context = callbackWrapper->GetContext();
                auto value = callbackWrapper->GetValue();

                if (context.get() && context->Enter())
                {
                    try
                    {
                        result = value->ExecuteFunction(nullptr, params);
                        success = result.get() != nullptr;

                        //we need to do this here to be able to store the v8context
                        if (success)
                        {
                            auto responseArgList = response->GetArgumentList();
                            SerializeV8Object(result, responseArgList, 2, callbackRegistry);
                        }
                        else
                        {
                            auto exception = value->GetException();
                            if (exception.get())
                            {
                                errorMessage = exception->GetMessage();
                            }
                        }
                    }
                    finally
                    {
                        context->Exit();
                    }
                }
                else
                {
                    errorMessage = "Unable to Enter Context";
                }
            }

            if (response.get())
            {
                auto responseArgList = response->GetArgumentList();
                responseArgList->SetBool(0, success);
                SetInt64(callbackId, responseArgList, 1);
                if (!success)
                {
                    responseArgList->SetString(2, errorMessage);
                }
                browser->SendProcessMessage(sourceProcessId, response);
            }

            handled = true;
        }
        else if (name == kJavascriptCallbackDestroyRequest)
        {
            auto jsCallbackId = GetInt64(argList, 0);
            auto frameId = GetInt64(argList, 1);
            JavascriptRootObjectWrapper^ rootObjectWrapper;
            auto rootObjectWrappers = browserWrapper->JavascriptRootObjectWrappers->TryGetValue(frameId, rootObjectWrapper);

            rootObjectWrapper->CallbackRegistry->Deregister(jsCallbackId);

            handled = true;
        }
        else if (name == kJavascriptRootObjectRequest)
        {
            _javascriptAsyncRootObject = DeserializeJsRootObject(argList, 0);
            _javascriptRootObject = DeserializeJsRootObject(argList, 1);
            handled = true;
        }
        else if (name == kJavascriptAsyncMethodCallResponse)
        {
            auto frameId = GetInt64(argList, 0);
            auto callbackId = GetInt64(argList, 1);

            JavascriptRootObjectWrapper^ rootObjectWrapper;
            auto rootObjectWrappers = browserWrapper->JavascriptRootObjectWrappers->TryGetValue(frameId, rootObjectWrapper);

            if (rootObjectWrapper != nullptr)
            {
                JavascriptAsyncMethodCallback^ callback;
                if (rootObjectWrapper->TryGetAndRemoveMethodCallback(callbackId, callback))
                {
                    auto success = argList->GetBool(2);
                    if (success)
                    {
                        callback->Success(DeserializeV8Object(argList, 3));
                    }
                    else
                    {
                        callback->Fail(argList->GetString(3));
                    }
                    //dispose
                    delete callback;
                }
            }
            handled = true;
        }

        return handled;
    };

    void CefAppUnmanagedWrapper::OnRenderThreadCreated(CefRefPtr<CefListValue> extraInfo)
    {
        auto extensionList = extraInfo->GetList(0);

        for (size_t i = 0; i < extensionList->GetSize(); i++)
        {
            auto extension = extensionList->GetList(i);
            auto ext = gcnew CefExtension(StringUtils::ToClr(extension->GetString(0)), StringUtils::ToClr(extension->GetString(1)));

            _extensions->Add(ext);
        }
    }

    void CefAppUnmanagedWrapper::OnWebKitInitialized()
    {
        //we need to do this because the builtin Promise object is not accesible
        CefRegisterExtension("cefsharp/promisecreator", kPromiseCreatorScript, NULL);

        for each(CefExtension^ extension in _extensions->AsReadOnly())
        {
            //only support extensions without handlers now
            CefRegisterExtension(StringUtils::ToNative(extension->Name), StringUtils::ToNative(extension->JavascriptCode), NULL);
        }
    }

    void CefAppUnmanagedWrapper::OnRegisterCustomSchemes(CefRefPtr<CefSchemeRegistrar> registrar)
    {
        for each (CefCustomScheme^ scheme in _schemes->AsReadOnly())
        {
            registrar->AddCustomScheme(StringUtils::ToNative(scheme->SchemeName), scheme->IsStandard, scheme->IsLocal, scheme->IsDisplayIsolated);
        }
    }
}<commit_msg>ensure destroyed root objects are not accessed in cef app unmanaged warpper message handling<commit_after>// Copyright © 2010-2015 The CefSharp Project. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
#pragma once

#include "Stdafx.h"

#include "CefBrowserWrapper.h"
#include "CefAppUnmanagedWrapper.h"
#include "JavascriptRootObjectWrapper.h"
#include "Serialization\V8Serialization.h"
#include "Serialization\JsObjectsSerialization.h"
#include "Async/JavascriptAsyncMethodCallback.h"
#include "..\CefSharp.Core\Internals\Messaging\Messages.h"
#include "..\CefSharp.Core\Internals\Serialization\Primitives.h"

using namespace System::Diagnostics;
using namespace System::Collections::Generic;
using namespace CefSharp::Internals::Messaging;
using namespace CefSharp::Internals::Serialization;

namespace CefSharp
{
    const CefString CefAppUnmanagedWrapper::kPromiseCreatorFunction = "cefsharp_CreatePromise";
    const CefString CefAppUnmanagedWrapper::kPromiseCreatorScript = ""
        "function cefsharp_CreatePromise() {"
        "   var object = {};"
        "   var promise = new Promise(function(resolve, reject) {"
        "       object.resolve = resolve;object.reject = reject;"
        "   });"
        "   return{ p: promise, res : object.resolve,  rej: object.reject};"
        "}";

    CefRefPtr<CefRenderProcessHandler> CefAppUnmanagedWrapper::GetRenderProcessHandler()
    {
        return this;
    };

    // CefRenderProcessHandler
    void CefAppUnmanagedWrapper::OnBrowserCreated(CefRefPtr<CefBrowser> browser)
    {
        auto wrapper = gcnew CefBrowserWrapper(browser);
        _onBrowserCreated->Invoke(wrapper);

        //Multiple CefBrowserWrappers created when opening popups
        _browserWrappers->TryAdd(browser->GetIdentifier(), wrapper);
    }

    void CefAppUnmanagedWrapper::OnBrowserDestroyed(CefRefPtr<CefBrowser> browser)
    {
        CefBrowserWrapper^ wrapper;
        if (_browserWrappers->TryRemove(browser->GetIdentifier(), wrapper))
        {
            _onBrowserDestroyed->Invoke(wrapper);
            delete wrapper;
        }
    };

    void CefAppUnmanagedWrapper::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
    {
        auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier(), true);

        if (!Object::ReferenceEquals(_javascriptRootObject, nullptr) || !Object::ReferenceEquals(_javascriptAsyncRootObject, nullptr))
        {
            auto rootObjectWrappers = browserWrapper->JavascriptRootObjectWrappers;
            auto frameId = frame->GetIdentifier();

            if (rootObjectWrappers->ContainsKey(frameId))
            {
                LOG(WARNING) << "A context has been created for the same browser / frame without context released called previously";
            }
            else
            {
                auto rootObject = gcnew JavascriptRootObjectWrapper(browser->GetIdentifier(), browserWrapper->BrowserProcess);
                rootObject->Bind(_javascriptRootObject, _javascriptAsyncRootObject, context->GetGlobal());

                rootObjectWrappers->TryAdd(frameId, rootObject);
            }
        }
    };

    void CefAppUnmanagedWrapper::OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
    {
        auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier(), true);

        auto rootObjectWrappers = browserWrapper->JavascriptRootObjectWrappers;

        JavascriptRootObjectWrapper^ wrapper;
        if (rootObjectWrappers->TryRemove(frame->GetIdentifier(), wrapper))
        {
            delete wrapper;
        }
    };

    CefBrowserWrapper^ CefAppUnmanagedWrapper::FindBrowserWrapper(int browserId, bool mustExist)
    {
        CefBrowserWrapper^ wrapper = nullptr;

        _browserWrappers->TryGetValue(browserId, wrapper);

        if (mustExist && wrapper == nullptr)
        {
            throw gcnew InvalidOperationException(String::Format("Failed to identify BrowserWrapper in OnContextCreated. : {0}", browserId));
        }

        return wrapper;
    }

    bool CefAppUnmanagedWrapper::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefProcessId sourceProcessId, CefRefPtr<CefProcessMessage> message)
    {
        auto handled = false;
        auto name = message->GetName();
        auto argList = message->GetArgumentList();

        auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier(), false);
        //Error handling for missing/closed browser
        if (browserWrapper == nullptr)
        {
            if (name == kJavascriptCallbackDestroyRequest ||
                name == kJavascriptRootObjectRequest ||
                name == kJavascriptAsyncMethodCallResponse)
            {
                //If we can't find the browser wrapper then we'll just
                //ignore this as it's likely already been disposed of
                return true;
            }

            CefString responseName;
            if (name == kEvaluateJavascriptRequest)
            {
                responseName = kEvaluateJavascriptResponse;
            }
            else if (name == kJavascriptCallbackRequest)
            {
                responseName = kJavascriptCallbackResponse;
            }
            else
            {
                //TODO: Should be throw an exception here? It's likely that only a CefSharp developer would see this
                // when they added a new message and havn't yet implemented the render process functionality.
                throw gcnew Exception("Unsupported message type");
            }

            auto callbackId = GetInt64(argList, 1);
            auto response = CefProcessMessage::Create(responseName);
            auto responseArgList = response->GetArgumentList();
            auto errorMessage = String::Format("Request BrowserId : {0} not found it's likely the browser is already closed", browser->GetIdentifier());

            //success: false
            responseArgList->SetBool(0, false);
            SetInt64(callbackId, responseArgList, 1);
            responseArgList->SetString(2, StringUtils::ToNative(errorMessage));
            browser->SendProcessMessage(sourceProcessId, response);

            return true;
        }

        //these messages are roughly handled the same way
        if (name == kEvaluateJavascriptRequest || name == kJavascriptCallbackRequest)
        {
            bool success;
            CefRefPtr<CefV8Value> result;
            CefString errorMessage;
            CefRefPtr<CefProcessMessage> response;
            //both messages have the frameId stored at 0 and callbackId stored at index 1
            auto frameId = GetInt64(argList, 0);
            int64 callbackId = GetInt64(argList, 1);

            JavascriptRootObjectWrapper^ rootObjectWrapper;
            browserWrapper->JavascriptRootObjectWrappers->TryGetValue(frameId, rootObjectWrapper);
            auto callbackRegistry = rootObjectWrapper == nullptr ? nullptr : rootObjectWrapper->CallbackRegistry;
            if (callbackRegistry == nullptr)
            {
                success = false;
                errorMessage = StringUtils::ToNative("Frame " + frameId + " is not valid anymore.");
            } else if (name == kEvaluateJavascriptRequest)
            {
                auto script = argList->GetString(2);

                response = CefProcessMessage::Create(kEvaluateJavascriptResponse);

                auto frame = browser->GetFrame(frameId);
                if (frame.get())
                {
                    auto context = frame->GetV8Context();

                    if (context.get() && context->Enter())
                    {
                        try
                        {
                            CefRefPtr<CefV8Exception> exception;
                            success = context->Eval(script, result, exception);

                            //we need to do this here to be able to store the v8context
                            if (success)
                            {
                                auto responseArgList = response->GetArgumentList();
                                SerializeV8Object(result, responseArgList, 2, callbackRegistry);
                            }
                            else
                            {
                                errorMessage = exception->GetMessage();
                            }
                        }
                        finally
                        {
                            context->Exit();
                        }
                    }
                    else
                    {
                        errorMessage = "Unable to Enter Context";
                    }
                }
                else
                {
                    errorMessage = "Unable to Get Frame matching Id";
                }
            }
            else
            {
                auto jsCallbackId = GetInt64(argList, 2);
                auto parameterList = argList->GetList(3);
                CefV8ValueList params;
                for (CefV8ValueList::size_type i = 0; i < parameterList->GetSize(); i++)
                {
                    params.push_back(DeserializeV8Object(parameterList, static_cast<int>(i)));
                }

                response = CefProcessMessage::Create(kJavascriptCallbackResponse);

                auto callbackWrapper = callbackRegistry->FindWrapper(jsCallbackId);
                auto context = callbackWrapper->GetContext();
                auto value = callbackWrapper->GetValue();

                if (context.get() && context->Enter())
                {
                    try
                    {
                        result = value->ExecuteFunction(nullptr, params);
                        success = result.get() != nullptr;

                        //we need to do this here to be able to store the v8context
                        if (success)
                        {
                            auto responseArgList = response->GetArgumentList();
                            SerializeV8Object(result, responseArgList, 2, callbackRegistry);
                        }
                        else
                        {
                            auto exception = value->GetException();
                            if (exception.get())
                            {
                                errorMessage = exception->GetMessage();
                            }
                        }
                    }
                    finally
                    {
                        context->Exit();
                    }
                }
                else
                {
                    errorMessage = "Unable to Enter Context";
                }
            }

            if (response.get())
            {
                auto responseArgList = response->GetArgumentList();
                responseArgList->SetBool(0, success);
                SetInt64(callbackId, responseArgList, 1);
                if (!success)
                {
                    responseArgList->SetString(2, errorMessage);
                }
                browser->SendProcessMessage(sourceProcessId, response);
            }

            handled = true;
        }
        else if (name == kJavascriptCallbackDestroyRequest)
        {
            auto jsCallbackId = GetInt64(argList, 0);
            auto frameId = GetInt64(argList, 1);
            JavascriptRootObjectWrapper^ rootObjectWrapper;
            browserWrapper->JavascriptRootObjectWrappers->TryGetValue(frameId, rootObjectWrapper);
            if (rootObjectWrapper != nullptr && rootObjectWrapper->CallbackRegistry != nullptr)
            {
                rootObjectWrapper->CallbackRegistry->Deregister(jsCallbackId);
            }

            handled = true;
        }
        else if (name == kJavascriptRootObjectRequest)
        {
            _javascriptAsyncRootObject = DeserializeJsRootObject(argList, 0);
            _javascriptRootObject = DeserializeJsRootObject(argList, 1);
            handled = true;
        }
        else if (name == kJavascriptAsyncMethodCallResponse)
        {
            auto frameId = GetInt64(argList, 0);
            auto callbackId = GetInt64(argList, 1);

            JavascriptRootObjectWrapper^ rootObjectWrapper;
            browserWrapper->JavascriptRootObjectWrappers->TryGetValue(frameId, rootObjectWrapper);

            if (rootObjectWrapper != nullptr)
            {
                JavascriptAsyncMethodCallback^ callback;
                if (rootObjectWrapper->TryGetAndRemoveMethodCallback(callbackId, callback))
                {
                    auto success = argList->GetBool(2);
                    if (success)
                    {
                        callback->Success(DeserializeV8Object(argList, 3));
                    }
                    else
                    {
                        callback->Fail(argList->GetString(3));
                    }
                    //dispose
                    delete callback;
                }
            }
            handled = true;
        }

        return handled;
    };

    void CefAppUnmanagedWrapper::OnRenderThreadCreated(CefRefPtr<CefListValue> extraInfo)
    {
        auto extensionList = extraInfo->GetList(0);

        for (size_t i = 0; i < extensionList->GetSize(); i++)
        {
            auto extension = extensionList->GetList(i);
            auto ext = gcnew CefExtension(StringUtils::ToClr(extension->GetString(0)), StringUtils::ToClr(extension->GetString(1)));

            _extensions->Add(ext);
        }
    }

    void CefAppUnmanagedWrapper::OnWebKitInitialized()
    {
        //we need to do this because the builtin Promise object is not accesible
        CefRegisterExtension("cefsharp/promisecreator", kPromiseCreatorScript, NULL);

        for each(CefExtension^ extension in _extensions->AsReadOnly())
        {
            //only support extensions without handlers now
            CefRegisterExtension(StringUtils::ToNative(extension->Name), StringUtils::ToNative(extension->JavascriptCode), NULL);
        }
    }

    void CefAppUnmanagedWrapper::OnRegisterCustomSchemes(CefRefPtr<CefSchemeRegistrar> registrar)
    {
        for each (CefCustomScheme^ scheme in _schemes->AsReadOnly())
        {
            registrar->AddCustomScheme(StringUtils::ToNative(scheme->SchemeName), scheme->IsStandard, scheme->IsLocal, scheme->IsDisplayIsolated);
        }
    }
}<|endoftext|>


<commit_before>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "InterlockingGenerator.h"

#include <algorithm> // max

#include "Application.h"
#include "Slice.h"
#include "slicer.h"
#include "utils/polygonUtils.h"
#include "utils/VoxelUtils.h"

namespace cura
{

// TODO: make dilation user parameter

// TODO: option for different amount of dilation for shell removal

// TODO more documentation

// TODO more const

// TODO fix test

// TODO: only go up to the min layer count

//TODO omit 0ca229dd6af196358733d076df108209c737f6b0 prevent empty layers from being generated on top

void InterlockingGenerator::generateInterlockingStructure(std::vector<Slicer*>& volumes)
{
    for (size_t mesh_a_idx = 0; mesh_a_idx < volumes.size(); mesh_a_idx++)
    {
        Slicer& mesh_a = *volumes[mesh_a_idx];
        size_t extruder_nr_a = mesh_a.mesh->settings.get<ExtruderTrain&>("wall_0_extruder_nr").extruder_nr;
        for (size_t mesh_b_idx = mesh_a_idx + 1; mesh_b_idx < volumes.size(); mesh_b_idx++)
        {
            Slicer& mesh_b = *volumes[mesh_b_idx];
            size_t extruder_nr_b = mesh_b.mesh->settings.get<ExtruderTrain&>("wall_0_extruder_nr").extruder_nr;
            if (extruder_nr_a != extruder_nr_b
                && mesh_a.mesh->getAABB().offset(ignored_gap).hit(mesh_b.mesh->getAABB()) // early out for when meshes dont share any overlap in their bounding box
            )
            {
                generateInterlockingStructure(mesh_a, mesh_b);
            }
        }

    }
}
void InterlockingGenerator::generateInterlockingStructure(Slicer& mesh_a, Slicer& mesh_b)
{
    /*
    if ( ! Application::getInstance().current_slice->scene.current_mesh_group->settings.get<bool>("interlocking_structure_gen"))
    {
        return;
    }
    */

    coord_t beam_widths[2];
    beam_widths[0] = 2 * mesh_a.mesh->settings.get<coord_t>("wall_line_width_0"); // TODO: make setting
    beam_widths[1] = 2 * mesh_b.mesh->settings.get<coord_t>("wall_line_width_0"); // TODO: make setting

    // TODO: make settigns for these:
    coord_t cell_width = beam_widths[0] + beam_widths[1];
    coord_t beam_layer_count = 2;

    PointMatrix rotation(22.5); // TODO: make setting

    DilationKernel interface_dilation(GridPoint3(2,2,2), DilationKernel::Type::PRISM);

    constexpr bool air_filtering = true;
    DilationKernel air_dilation(GridPoint3(3,3,3), DilationKernel::Type::DIAMOND);

    Point3 cell_size(cell_width, cell_width, 2 * beam_layer_count);

    size_t max_layer_count = 0;
    {
        for (Slicer* mesh : {&mesh_a, &mesh_b})
        {
            max_layer_count = std::max(max_layer_count, mesh->layers.size());
        }
        max_layer_count++; // introduce ghost layer on top for correct skin computation of topmost layer.
    }

    InterlockingGenerator gen(mesh_a, mesh_b, beam_widths, max_layer_count, rotation, cell_size, beam_layer_count, air_filtering);

    std::vector<std::unordered_set<GridPoint3>> voxels_per_mesh = gen.getShellVoxels(interface_dilation);

    std::vector<Polygons> layer_regions(max_layer_count);
    gen.computeLayerRegions(layer_regions);

    std::unordered_set<GridPoint3>& has_any_mesh = voxels_per_mesh[0];
    std::unordered_set<GridPoint3>& has_all_meshes = voxels_per_mesh[1];
    has_any_mesh.merge(has_all_meshes); // perform intersection and union simultaneously

    if (air_filtering)
    {
        std::unordered_set<GridPoint3> air_cells;
        gen.addBoundaryCells(layer_regions, air_dilation, air_cells);

        for (const GridPoint3& p : air_cells)
        {
            has_all_meshes.erase(p);
        }
    }

    std::vector<std::vector<Polygons>> cell_area_per_mesh_per_layer;
    gen.generateMicrostructure(cell_area_per_mesh_per_layer);

    gen.applyMicrostructureToOutlines(has_all_meshes, cell_area_per_mesh_per_layer, layer_regions);
}

std::vector<std::unordered_set<GridPoint3>> InterlockingGenerator::getShellVoxels(const DilationKernel& kernel) const
{
    std::vector<std::unordered_set<GridPoint3>> voxels_per_mesh(2);

    // mark all cells which contain some boundary
    for (size_t mesh_idx = 0; mesh_idx < 2; mesh_idx++)
    {
        Slicer* mesh = (mesh_idx == 0)? &mesh_a : &mesh_b;
        std::unordered_set<GridPoint3>& mesh_voxels = voxels_per_mesh[mesh_idx];

        std::vector<Polygons> rotated_polygons_per_layer(mesh->layers.size());
        for (size_t layer_nr = 0; layer_nr < mesh->layers.size(); layer_nr++)
        {
            SlicerLayer& layer = mesh->layers[layer_nr];
            rotated_polygons_per_layer[layer_nr] = layer.polygons;
            rotated_polygons_per_layer[layer_nr].applyMatrix(rotation);
        }

        addBoundaryCells(rotated_polygons_per_layer, kernel, mesh_voxels);
    }

    return voxels_per_mesh;
}

void InterlockingGenerator::addBoundaryCells(std::vector<Polygons>& layers, const DilationKernel& kernel, std::unordered_set<GridPoint3>& cells) const
{
    auto voxel_emplacer = [&cells](GridPoint3 p) { cells.emplace(p); return true; };

    for (size_t layer_nr = 0; layer_nr < layers.size(); layer_nr++)
    {
        coord_t z = layer_nr;
        vu.walkDilatedPolygons(layers[layer_nr], z, kernel, voxel_emplacer);
        Polygons skin = layers[layer_nr];
        if (layer_nr > 0)
        {
            skin = skin.xorPolygons(layers[layer_nr - 1]);
        }
        skin = skin.offset(-cell_size.x / 2).offset(cell_size.x / 2); // remove superfluous small areas, which would anyway be included because of walkPolygons
        vu.walkDilatedAreas(skin, z, kernel, voxel_emplacer);
    }
}

void InterlockingGenerator::computeLayerRegions(std::vector<Polygons>& layer_regions) const
{
    for (unsigned int layer_nr = 0; layer_nr < layer_regions.size(); layer_nr++)
    {
        Polygons& layer_region = layer_regions[layer_nr];
        coord_t z;
        for (Slicer* mesh : {&mesh_a, &mesh_b})
        {
            if (layer_nr >= mesh->layers.size()) break;
            SlicerLayer& layer = mesh->layers[layer_nr];
            z = layer.z;
            layer_region.add(layer.polygons);
        }
        layer_region = layer_region.offset(ignored_gap).offset(-ignored_gap); // Morphological close to merge meshes into single volume
        layer_region.applyMatrix(rotation);
    }
}

void InterlockingGenerator::generateMicrostructure(std::vector<std::vector<Polygons>>& cell_area_per_mesh_per_layer) const
{
    cell_area_per_mesh_per_layer.resize(2);
    cell_area_per_mesh_per_layer[0].resize(2);
    const coord_t beam_w_sum = beam_widths[0] + beam_widths[1];
    const coord_t middle = cell_size.x * beam_widths[0] / beam_w_sum;
    const coord_t width[2] = { middle, cell_size.x - middle };
    for (size_t mesh_idx : {0, 1})
    {
        Point offset(mesh_idx? middle : 0, 0);
        Point area_size(width[mesh_idx], cell_size.y);

        PolygonRef poly = cell_area_per_mesh_per_layer[0][mesh_idx].newPoly();
        poly.emplace_back(offset);
        poly.emplace_back(offset + Point(area_size.X, 0));
        poly.emplace_back(offset + area_size);
        poly.emplace_back(offset + Point(0, area_size.Y));
    }
    cell_area_per_mesh_per_layer[1] = cell_area_per_mesh_per_layer[0];
    for (Polygons& polys : cell_area_per_mesh_per_layer[1])
    {
        for (PolygonRef poly : polys)
        {
            for (Point& p : poly)
            {
                std::swap(p.X, p.Y);
            }
        }
    }
}

void InterlockingGenerator::applyMicrostructureToOutlines(const std::unordered_set<GridPoint3>& cells, std::vector<std::vector<Polygons>>& cell_area_per_mesh_per_layer, const std::vector<Polygons>& layer_regions) const
{
    PointMatrix unapply_rotation = rotation.inverse();

    std::vector<Polygons> structure_per_layer[2]; // for each mesh the structure on each layer
    structure_per_layer[0].resize((max_layer_count + 1) / beam_layer_count);
    structure_per_layer[1].resize((max_layer_count + 1) / beam_layer_count);
    // Only compute cell structure for half the layers, because since our beams are two layers high, every odd layer of the structure will be the same as the layer below.
    for (const GridPoint3& grid_loc : cells)
    {
        Point3 bottom_corner = vu.toLowerCorner(grid_loc);
        for (size_t mesh_idx = 0; mesh_idx < 2; mesh_idx++)
        {
            for (unsigned int layer_nr = bottom_corner.z; layer_nr < bottom_corner.z + cell_size.z && layer_nr < max_layer_count; layer_nr += beam_layer_count)
            {
                Polygons areas_here = cell_area_per_mesh_per_layer[(layer_nr / beam_layer_count) % cell_area_per_mesh_per_layer.size()][mesh_idx];
                areas_here.translate(Point(bottom_corner.x, bottom_corner.y));
                structure_per_layer[mesh_idx][layer_nr / beam_layer_count].add(areas_here);
            }
        }
    }

    for (size_t mesh_idx = 0; mesh_idx < 2; mesh_idx++)
    {
        for (Polygons& layer_structure : structure_per_layer[mesh_idx])
        {
            layer_structure = layer_structure.unionPolygons();
            layer_structure.applyMatrix(unapply_rotation);
        }
    }

    for (size_t mesh_idx = 0; mesh_idx < 2; mesh_idx++)
    {
        Slicer* mesh = (mesh_idx == 0)? &mesh_a : &mesh_b;
        for (size_t layer_nr = 0; layer_nr < max_layer_count; layer_nr++)
        {
            if (layer_nr >= mesh->layers.size()) break;
            const Polygons& layer_region = layer_regions[layer_nr];
            const Polygons* areas_here = &structure_per_layer[mesh_idx][layer_nr / beam_layer_count];
            Polygons areas_here_limited_to_layer_outlines;
            if ( ! air_filtering)
            {
                areas_here_limited_to_layer_outlines = layer_region.intersection(*areas_here); // Prevent structure from protruding out of the models
                areas_here = &areas_here_limited_to_layer_outlines;
            }

            const Polygons& areas_other = structure_per_layer[ ! mesh_idx][layer_nr / beam_layer_count];

            SlicerLayer& layer = mesh->layers[layer_nr];
            layer.polygons = layer.polygons.unionPolygons(*areas_here) // extend layer areas outward with newly added beams
                                            .difference(areas_other); // reduce layer areas inward with beams from other mesh
        }
    }
}

}//namespace cura
<commit_msg>fix boundary intersection when not air_filtering<commit_after>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "InterlockingGenerator.h"

#include <algorithm> // max

#include "Application.h"
#include "Slice.h"
#include "slicer.h"
#include "utils/polygonUtils.h"
#include "utils/VoxelUtils.h"

namespace cura
{

// TODO: make dilation user parameter

// TODO: option for different amount of dilation for shell removal

// TODO more documentation

// TODO more const

// TODO fix test

// TODO: only go up to the min layer count

//TODO omit 0ca229dd6af196358733d076df108209c737f6b0 prevent empty layers from being generated on top

void InterlockingGenerator::generateInterlockingStructure(std::vector<Slicer*>& volumes)
{
    for (size_t mesh_a_idx = 0; mesh_a_idx < volumes.size(); mesh_a_idx++)
    {
        Slicer& mesh_a = *volumes[mesh_a_idx];
        size_t extruder_nr_a = mesh_a.mesh->settings.get<ExtruderTrain&>("wall_0_extruder_nr").extruder_nr;
        for (size_t mesh_b_idx = mesh_a_idx + 1; mesh_b_idx < volumes.size(); mesh_b_idx++)
        {
            Slicer& mesh_b = *volumes[mesh_b_idx];
            size_t extruder_nr_b = mesh_b.mesh->settings.get<ExtruderTrain&>("wall_0_extruder_nr").extruder_nr;
            if (extruder_nr_a != extruder_nr_b
                && mesh_a.mesh->getAABB().offset(ignored_gap).hit(mesh_b.mesh->getAABB()) // early out for when meshes dont share any overlap in their bounding box
            )
            {
                generateInterlockingStructure(mesh_a, mesh_b);
            }
        }

    }
}
void InterlockingGenerator::generateInterlockingStructure(Slicer& mesh_a, Slicer& mesh_b)
{
    /*
    if ( ! Application::getInstance().current_slice->scene.current_mesh_group->settings.get<bool>("interlocking_structure_gen"))
    {
        return;
    }
    */

    coord_t beam_widths[2];
    beam_widths[0] = 2 * mesh_a.mesh->settings.get<coord_t>("wall_line_width_0"); // TODO: make setting
    beam_widths[1] = 2 * mesh_b.mesh->settings.get<coord_t>("wall_line_width_0"); // TODO: make setting

    // TODO: make settigns for these:
    coord_t cell_width = beam_widths[0] + beam_widths[1];
    coord_t beam_layer_count = 2;

    PointMatrix rotation(22.5); // TODO: make setting

    DilationKernel interface_dilation(GridPoint3(2,2,2), DilationKernel::Type::PRISM);

    constexpr bool air_filtering = true;
    DilationKernel air_dilation(GridPoint3(3,3,3), DilationKernel::Type::DIAMOND);

    Point3 cell_size(cell_width, cell_width, 2 * beam_layer_count);

    size_t max_layer_count = 0;
    {
        for (Slicer* mesh : {&mesh_a, &mesh_b})
        {
            max_layer_count = std::max(max_layer_count, mesh->layers.size());
        }
        max_layer_count++; // introduce ghost layer on top for correct skin computation of topmost layer.
    }

    InterlockingGenerator gen(mesh_a, mesh_b, beam_widths, max_layer_count, rotation, cell_size, beam_layer_count, air_filtering);

    std::vector<std::unordered_set<GridPoint3>> voxels_per_mesh = gen.getShellVoxels(interface_dilation);

    std::vector<Polygons> layer_regions(max_layer_count);
    gen.computeLayerRegions(layer_regions);

    std::unordered_set<GridPoint3>& has_any_mesh = voxels_per_mesh[0];
    std::unordered_set<GridPoint3>& has_all_meshes = voxels_per_mesh[1];
    has_any_mesh.merge(has_all_meshes); // perform intersection and union simultaneously

    if (air_filtering)
    {
        std::unordered_set<GridPoint3> air_cells;
        gen.addBoundaryCells(layer_regions, air_dilation, air_cells);

        for (const GridPoint3& p : air_cells)
        {
            has_all_meshes.erase(p);
        }
    }

    std::vector<std::vector<Polygons>> cell_area_per_mesh_per_layer;
    gen.generateMicrostructure(cell_area_per_mesh_per_layer);

    gen.applyMicrostructureToOutlines(has_all_meshes, cell_area_per_mesh_per_layer, layer_regions);
}

std::vector<std::unordered_set<GridPoint3>> InterlockingGenerator::getShellVoxels(const DilationKernel& kernel) const
{
    std::vector<std::unordered_set<GridPoint3>> voxels_per_mesh(2);

    // mark all cells which contain some boundary
    for (size_t mesh_idx = 0; mesh_idx < 2; mesh_idx++)
    {
        Slicer* mesh = (mesh_idx == 0)? &mesh_a : &mesh_b;
        std::unordered_set<GridPoint3>& mesh_voxels = voxels_per_mesh[mesh_idx];

        std::vector<Polygons> rotated_polygons_per_layer(mesh->layers.size());
        for (size_t layer_nr = 0; layer_nr < mesh->layers.size(); layer_nr++)
        {
            SlicerLayer& layer = mesh->layers[layer_nr];
            rotated_polygons_per_layer[layer_nr] = layer.polygons;
            rotated_polygons_per_layer[layer_nr].applyMatrix(rotation);
        }

        addBoundaryCells(rotated_polygons_per_layer, kernel, mesh_voxels);
    }

    return voxels_per_mesh;
}

void InterlockingGenerator::addBoundaryCells(std::vector<Polygons>& layers, const DilationKernel& kernel, std::unordered_set<GridPoint3>& cells) const
{
    auto voxel_emplacer = [&cells](GridPoint3 p) { cells.emplace(p); return true; };

    for (size_t layer_nr = 0; layer_nr < layers.size(); layer_nr++)
    {
        coord_t z = layer_nr;
        vu.walkDilatedPolygons(layers[layer_nr], z, kernel, voxel_emplacer);
        Polygons skin = layers[layer_nr];
        if (layer_nr > 0)
        {
            skin = skin.xorPolygons(layers[layer_nr - 1]);
        }
        skin = skin.offset(-cell_size.x / 2).offset(cell_size.x / 2); // remove superfluous small areas, which would anyway be included because of walkPolygons
        vu.walkDilatedAreas(skin, z, kernel, voxel_emplacer);
    }
}

void InterlockingGenerator::computeLayerRegions(std::vector<Polygons>& layer_regions) const
{
    for (unsigned int layer_nr = 0; layer_nr < layer_regions.size(); layer_nr++)
    {
        Polygons& layer_region = layer_regions[layer_nr];
        coord_t z;
        for (Slicer* mesh : {&mesh_a, &mesh_b})
        {
            if (layer_nr >= mesh->layers.size()) break;
            SlicerLayer& layer = mesh->layers[layer_nr];
            z = layer.z;
            layer_region.add(layer.polygons);
        }
        layer_region = layer_region.offset(ignored_gap).offset(-ignored_gap); // Morphological close to merge meshes into single volume
        layer_region.applyMatrix(rotation);
    }
}

void InterlockingGenerator::generateMicrostructure(std::vector<std::vector<Polygons>>& cell_area_per_mesh_per_layer) const
{
    cell_area_per_mesh_per_layer.resize(2);
    cell_area_per_mesh_per_layer[0].resize(2);
    const coord_t beam_w_sum = beam_widths[0] + beam_widths[1];
    const coord_t middle = cell_size.x * beam_widths[0] / beam_w_sum;
    const coord_t width[2] = { middle, cell_size.x - middle };
    for (size_t mesh_idx : {0, 1})
    {
        Point offset(mesh_idx? middle : 0, 0);
        Point area_size(width[mesh_idx], cell_size.y);

        PolygonRef poly = cell_area_per_mesh_per_layer[0][mesh_idx].newPoly();
        poly.emplace_back(offset);
        poly.emplace_back(offset + Point(area_size.X, 0));
        poly.emplace_back(offset + area_size);
        poly.emplace_back(offset + Point(0, area_size.Y));
    }
    cell_area_per_mesh_per_layer[1] = cell_area_per_mesh_per_layer[0];
    for (Polygons& polys : cell_area_per_mesh_per_layer[1])
    {
        for (PolygonRef poly : polys)
        {
            for (Point& p : poly)
            {
                std::swap(p.X, p.Y);
            }
        }
    }
}

void InterlockingGenerator::applyMicrostructureToOutlines(const std::unordered_set<GridPoint3>& cells, std::vector<std::vector<Polygons>>& cell_area_per_mesh_per_layer, const std::vector<Polygons>& layer_regions) const
{
    PointMatrix unapply_rotation = rotation.inverse();

    std::vector<Polygons> structure_per_layer[2]; // for each mesh the structure on each layer
    structure_per_layer[0].resize((max_layer_count + 1) / beam_layer_count);
    structure_per_layer[1].resize((max_layer_count + 1) / beam_layer_count);
    // Only compute cell structure for half the layers, because since our beams are two layers high, every odd layer of the structure will be the same as the layer below.
    for (const GridPoint3& grid_loc : cells)
    {
        Point3 bottom_corner = vu.toLowerCorner(grid_loc);
        for (size_t mesh_idx = 0; mesh_idx < 2; mesh_idx++)
        {
            for (unsigned int layer_nr = bottom_corner.z; layer_nr < bottom_corner.z + cell_size.z && layer_nr < max_layer_count; layer_nr += beam_layer_count)
            {
                Polygons areas_here = cell_area_per_mesh_per_layer[(layer_nr / beam_layer_count) % cell_area_per_mesh_per_layer.size()][mesh_idx];
                areas_here.translate(Point(bottom_corner.x, bottom_corner.y));
                structure_per_layer[mesh_idx][layer_nr / beam_layer_count].add(areas_here);
            }
        }
    }

    for (size_t mesh_idx = 0; mesh_idx < 2; mesh_idx++)
    {
        for (size_t layer_nr = 0; layer_nr < structure_per_layer[mesh_idx].size(); layer_nr++)
        {
            Polygons& layer_structure = structure_per_layer[mesh_idx][layer_nr];
            layer_structure = layer_structure.unionPolygons();
            if ( ! air_filtering)
            {
                const Polygons& layer_region = layer_regions[layer_nr];
                layer_structure = layer_region.intersection(layer_structure); // Prevent structure from protruding out of the models
            }
            layer_structure.applyMatrix(unapply_rotation);
        }
    }

    for (size_t mesh_idx = 0; mesh_idx < 2; mesh_idx++)
    {
        Slicer* mesh = (mesh_idx == 0)? &mesh_a : &mesh_b;
        for (size_t layer_nr = 0; layer_nr < max_layer_count; layer_nr++)
        {
            if (layer_nr >= mesh->layers.size()) break;
            const Polygons& areas_here = structure_per_layer[mesh_idx][layer_nr / beam_layer_count];
            const Polygons& areas_other = structure_per_layer[ ! mesh_idx][layer_nr / beam_layer_count];

            SlicerLayer& layer = mesh->layers[layer_nr];
            layer.polygons = layer.polygons.unionPolygons(areas_here) // extend layer areas outward with newly added beams
                                            .difference(areas_other); // reduce layer areas inward with beams from other mesh
        }
    }
}

}//namespace cura
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef DLL_RBM_TRAINER_HPP
#define DLL_RBM_TRAINER_HPP

#include <memory>

#include "cpp_utils/algorithm.hpp"

#include "decay_type.hpp"
#include "batch.hpp"
#include "layer_traits.hpp"

namespace dll {

enum class init_watcher_t { INIT };
constexpr const init_watcher_t init_watcher = init_watcher_t::INIT;

template<typename RBM, typename RW, typename Enable = void>
struct watcher_type {
    using watcher_t = typename RBM::desc::template watcher_t<RBM>;
};

template<typename RBM, typename RW>
struct watcher_type<RBM, RW, std::enable_if_t<cpp::not_u<std::is_void<RW>::value>::value>> {
    using watcher_t = RW;
};

/*!
 * \brief A generic trainer for Restricted Boltzmann Machine
 *
 * This trainer use the specified trainer of the RBM to perform unsupervised
 * training.
 */
template<typename RBM, bool EnableWatcher, typename RW>
struct rbm_trainer {
    using rbm_t = RBM;

    template<typename R,bool Denoising>
    using trainer_t = typename rbm_t::desc::template trainer_t<R,Denoising>;

    using watcher_t = typename watcher_type<rbm_t, RW>::watcher_t;

    mutable watcher_t watcher;

    rbm_trainer() : watcher() {}

    template<typename... Arg>
    rbm_trainer(init_watcher_t /*init*/, Arg... args) : watcher(args...) {}

    template<typename Iterator, cpp_enable_if_cst(layer_traits<rbm_t>::init_weights())>
    static void init_weights(RBM& rbm, Iterator first, Iterator last){
        rbm.init_weights(first, last);
    }

    template<typename Iterator, cpp_disable_if_cst(layer_traits<rbm_t>::init_weights())>
    static void init_weights(RBM&, Iterator, Iterator){
        //NOP
    }

    template<bool Denoising, typename IIterator, typename EIterator, cpp_enable_if_cst(layer_traits<rbm_t>::has_shuffle())>
    static void shuffle(IIterator ifirst, IIterator ilast, EIterator efirst, EIterator elast){
        static std::random_device rd;
        static std::mt19937_64 g(rd());

        if(Denoising){
            cpp::parallel_shuffle(ifirst, ilast, efirst, elast, g);
        } else {
            std::shuffle(ifirst, ilast, g);
        }
    }

    template<bool Denoising, typename IIterator, typename EIterator, cpp_disable_if_cst(layer_traits<rbm_t>::has_shuffle())>
    static void shuffle(IIterator, IIterator, EIterator, EIterator){}

    template<bool Denoising, typename IIterator, typename EIterator, typename IVector, typename EVector, cpp_enable_if_cst(layer_traits<rbm_t>::has_shuffle())>
    static auto prepare_it(IIterator ifirst, IIterator ilast, EIterator efirst, EIterator elast, IVector& ivec, EVector& evec){
        std::copy(ifirst, ilast, std::back_inserter(ivec));

        auto input_first = ivec.begin();
        auto input_last = ivec.end();

        if(Denoising){
            std::copy(efirst, elast, std::back_inserter(evec));

            auto expected_first = evec.begin();
            auto expected_last = evec.end();
            return std::make_tuple(input_first, input_last, expected_first, expected_last);
        } else {
            return std::make_tuple(input_first, input_last, input_first, input_last);
        }
    }

    template<bool Denoising, typename IIterator, typename EIterator, typename IVector, typename EVector, cpp_disable_if_cst(layer_traits<rbm_t>::has_shuffle())>
    static auto prepare_it(IIterator ifirst, IIterator ilast, EIterator efirst, EIterator elast, IVector&, EVector&){
        return std::make_tuple(ifirst, ilast, efirst, elast);
    }

    template<typename rbm_t, typename Iterator>
    using fix_iterator_t = std::conditional_t<
        layer_traits<rbm_t>::has_shuffle(),
        typename std::vector<typename std::iterator_traits<Iterator>::value_type>::iterator,
        Iterator>;

    std::size_t batch_size = 0;
    std::size_t total_batches = 0;
    typename rbm_t::weight last_error = 0.0;

    //Note: input_first/input_last only relevant for its size, not
    //values since they can point to the input of the first level
    //and not the current level
    template<bool Denoising = true, typename Iterator>
    void init_training(RBM& rbm, Iterator input_first, Iterator input_last){
        rbm.momentum = rbm.initial_momentum;

        if(EnableWatcher){
            watcher.training_begin(rbm);
        }

        //Compute the number of batches
        batch_size = get_batch_size(rbm);

        auto size = distance(input_first, input_last);

        //TODO Better handling of incomplete batch size would solve this problem (this could be done by
        //cleaning the data before the last batch)
        if(size % batch_size != 0){
            std::cout << "WARNING: The number of samples should be divisible by the batch size" << std::endl;
            std::cout << "         This may cause discrepancies in the results." << std::endl;
        }

        //Only used for debugging purposes, no need to be precise
        total_batches = size / batch_size;

        last_error = 0.0;
    }

    template<typename Iterator>
    typename rbm_t::weight train(RBM& rbm, Iterator first, Iterator last, std::size_t max_epochs){
        return train<false>(rbm, first, last, first, last, max_epochs);
    }

    template<bool Denoising = false>
    static auto get_trainer(RBM& rbm){
        //Allocate the trainer on the heap (may be large)
        return std::make_unique<trainer_t<rbm_t, Denoising>>(rbm);
    }

    typename rbm_t::weight finalize_training(RBM& rbm){
        if(EnableWatcher){
            watcher.training_end(rbm);
        }

        return last_error;
    }

    template<bool Denoising = true, typename IIterator, typename EIterator>
    typename rbm_t::weight train(RBM& rbm, IIterator ifirst, IIterator ilast, EIterator efirst, EIterator elast, std::size_t max_epochs){
        //In case of shuffle, we don't want to shuffle the input, therefore create a copy and shuffle it

        std::vector<typename std::iterator_traits<IIterator>::value_type> input_copy;
        std::vector<typename std::iterator_traits<EIterator>::value_type> expected_copy;

        auto iterators = prepare_it<Denoising>(ifirst, ilast, efirst, elast, input_copy, expected_copy);

        decltype(auto) input_first = std::get<0>(iterators);
        decltype(auto) input_last = std::get<1>(iterators);

        decltype(auto) expected_first = std::get<2>(iterators);
        decltype(auto) expected_last = std::get<3>(iterators);

        //Initialize RBM and trainign parameters
        init_training<Denoising>(rbm, input_first, input_last);

        //Some RBM may init weights based on the training data
        //Note: This can't be done in init_training, since it will
        //sometimes be called with the wrong input values
        init_weights(rbm, input_first, input_last);

        //Allocate the trainer
        auto trainer = get_trainer<Denoising>(rbm);

        //Train for max_epochs epoch
        for(std::size_t epoch = 0; epoch < max_epochs; ++epoch){
            //Shuffle if necessary
            shuffle<Denoising>(input_first, input_last, expected_first, expected_last);

            //Create a new context for this epoch
            rbm_training_context context;

            //Start a new epoch
            init_epoch();

            //Train on all the data
            train_sub(input_first, input_last, expected_first, trainer, context, rbm);

            //Finalize the current epoch
            finalize_epoch(epoch, context, rbm);

        }

        return finalize_training(rbm);
    }

    std::size_t batches = 0;
    std::size_t samples = 0;

    void init_epoch(){
        batches = 0;
        samples = 0;
    }

    template<typename IIT, typename EIT, typename Trainer>
    void train_sub(IIT input_first, IIT input_last, EIT expected_first, Trainer& trainer, rbm_training_context& context, rbm_t& rbm){
        auto iit = input_first;
        auto eit = expected_first;
        auto end = input_last;

        while(iit != end){
            auto istart = iit;
            auto estart = eit;

            std::size_t i = 0;
            while(iit != end && i < batch_size){
                ++iit;
                ++eit;
                ++samples;
                ++i;
            }

            ++batches;

            auto input_batch = make_batch(istart, iit);
            auto expected_batch = make_batch(estart, eit);
            trainer->train_batch(input_batch, expected_batch, context);

            context.reconstruction_error += context.batch_error;
            context.sparsity += context.batch_sparsity;

            if(EnableWatcher && layer_traits<rbm_t>::free_energy()){
                for(auto& v : input_batch){
                    context.free_energy += rbm.free_energy(v);
                }
            }

            if(EnableWatcher && layer_traits<rbm_t>::is_verbose()){
                watcher.batch_end(rbm, context, batches, total_batches);
            }
        }
    }

    void finalize_epoch(std::size_t epoch, rbm_training_context& context, rbm_t& rbm){
        //Average all the gathered information
        context.reconstruction_error /= batches;
        context.sparsity /= batches;
        context.free_energy /= samples;

        //After some time increase the momentum
        if(layer_traits<rbm_t>::has_momentum() && epoch == rbm.final_momentum_epoch){
            rbm.momentum = rbm.final_momentum;
        }

        //Notify the watcher
        if(EnableWatcher){
            watcher.epoch_end(epoch, context, rbm);
        }

        //Save the error for the return value
        last_error = context.reconstruction_error;
    }
};

} //end of dll namespace

#endif
<commit_msg>Less overhead for slow iterators<commit_after>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef DLL_RBM_TRAINER_HPP
#define DLL_RBM_TRAINER_HPP

#include <memory>

#include "cpp_utils/algorithm.hpp"

#include "decay_type.hpp"
#include "batch.hpp"
#include "layer_traits.hpp"

namespace dll {

enum class init_watcher_t { INIT };
constexpr const init_watcher_t init_watcher = init_watcher_t::INIT;

template<typename RBM, typename RW, typename Enable = void>
struct watcher_type {
    using watcher_t = typename RBM::desc::template watcher_t<RBM>;
};

template<typename RBM, typename RW>
struct watcher_type<RBM, RW, std::enable_if_t<cpp::not_u<std::is_void<RW>::value>::value>> {
    using watcher_t = RW;
};

/*!
 * \brief A generic trainer for Restricted Boltzmann Machine
 *
 * This trainer use the specified trainer of the RBM to perform unsupervised
 * training.
 */
template<typename RBM, bool EnableWatcher, typename RW>
struct rbm_trainer {
    using rbm_t = RBM;

    template<typename R,bool Denoising>
    using trainer_t = typename rbm_t::desc::template trainer_t<R,Denoising>;

    using watcher_t = typename watcher_type<rbm_t, RW>::watcher_t;

    mutable watcher_t watcher;

    rbm_trainer() : watcher() {}

    template<typename... Arg>
    rbm_trainer(init_watcher_t /*init*/, Arg... args) : watcher(args...) {}

    template<typename Iterator, cpp_enable_if_cst(layer_traits<rbm_t>::init_weights())>
    static void init_weights(RBM& rbm, Iterator first, Iterator last){
        rbm.init_weights(first, last);
    }

    template<typename Iterator, cpp_disable_if_cst(layer_traits<rbm_t>::init_weights())>
    static void init_weights(RBM&, Iterator, Iterator){
        //NOP
    }

    template<bool Denoising, typename IIterator, typename EIterator, cpp_enable_if_cst(layer_traits<rbm_t>::has_shuffle())>
    static void shuffle(IIterator ifirst, IIterator ilast, EIterator efirst, EIterator elast){
        static std::random_device rd;
        static std::mt19937_64 g(rd());

        if(Denoising){
            cpp::parallel_shuffle(ifirst, ilast, efirst, elast, g);
        } else {
            std::shuffle(ifirst, ilast, g);
        }
    }

    template<bool Denoising, typename IIterator, typename EIterator, cpp_disable_if_cst(layer_traits<rbm_t>::has_shuffle())>
    static void shuffle(IIterator, IIterator, EIterator, EIterator){}

    template<bool Denoising, typename IIterator, typename EIterator, typename IVector, typename EVector, cpp_enable_if_cst(layer_traits<rbm_t>::has_shuffle())>
    static auto prepare_it(IIterator ifirst, IIterator ilast, EIterator efirst, EIterator elast, IVector& ivec, EVector& evec){
        std::copy(ifirst, ilast, std::back_inserter(ivec));

        auto input_first = ivec.begin();
        auto input_last = ivec.end();

        if(Denoising){
            std::copy(efirst, elast, std::back_inserter(evec));

            auto expected_first = evec.begin();
            auto expected_last = evec.end();
            return std::make_tuple(input_first, input_last, expected_first, expected_last);
        } else {
            return std::make_tuple(input_first, input_last, input_first, input_last);
        }
    }

    template<bool Denoising, typename IIterator, typename EIterator, typename IVector, typename EVector, cpp_disable_if_cst(layer_traits<rbm_t>::has_shuffle())>
    static auto prepare_it(IIterator ifirst, IIterator ilast, EIterator efirst, EIterator elast, IVector&, EVector&){
        return std::make_tuple(ifirst, ilast, efirst, elast);
    }

    template<typename rbm_t, typename Iterator>
    using fix_iterator_t = std::conditional_t<
        layer_traits<rbm_t>::has_shuffle(),
        typename std::vector<typename std::iterator_traits<Iterator>::value_type>::iterator,
        Iterator>;

    std::size_t batch_size = 0;
    std::size_t total_batches = 0;
    typename rbm_t::weight last_error = 0.0;

    //Note: input_first/input_last only relevant for its size, not
    //values since they can point to the input of the first level
    //and not the current level
    template<bool Denoising = true, typename Iterator>
    void init_training(RBM& rbm, Iterator input_first, Iterator input_last){
        rbm.momentum = rbm.initial_momentum;

        if(EnableWatcher){
            watcher.training_begin(rbm);
        }

        //Get the size of each batches
        batch_size = get_batch_size(rbm);

        if(std::is_same<typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>::value){
            auto size = distance(input_first, input_last);

            //TODO Better handling of incomplete batch size would solve this problem (this could be done by
            //cleaning the data before the last batch)
            if(size % batch_size != 0){
                std::cout << "WARNING: The number of samples should be divisible by the batch size" << std::endl;
                std::cout << "         This may cause discrepancies in the results." << std::endl;
            }

            //Only used for debugging purposes, no need to be precise
            total_batches = size / batch_size;
        } else {
            total_batches = 0;
        }

        last_error = 0.0;
    }

    template<typename Iterator>
    typename rbm_t::weight train(RBM& rbm, Iterator first, Iterator last, std::size_t max_epochs){
        return train<false>(rbm, first, last, first, last, max_epochs);
    }

    template<bool Denoising = false>
    static auto get_trainer(RBM& rbm){
        //Allocate the trainer on the heap (may be large)
        return std::make_unique<trainer_t<rbm_t, Denoising>>(rbm);
    }

    typename rbm_t::weight finalize_training(RBM& rbm){
        if(EnableWatcher){
            watcher.training_end(rbm);
        }

        return last_error;
    }

    template<bool Denoising = true, typename IIterator, typename EIterator>
    typename rbm_t::weight train(RBM& rbm, IIterator ifirst, IIterator ilast, EIterator efirst, EIterator elast, std::size_t max_epochs){
        //In case of shuffle, we don't want to shuffle the input, therefore create a copy and shuffle it

        std::vector<typename std::iterator_traits<IIterator>::value_type> input_copy;
        std::vector<typename std::iterator_traits<EIterator>::value_type> expected_copy;

        auto iterators = prepare_it<Denoising>(ifirst, ilast, efirst, elast, input_copy, expected_copy);

        decltype(auto) input_first = std::get<0>(iterators);
        decltype(auto) input_last = std::get<1>(iterators);

        decltype(auto) expected_first = std::get<2>(iterators);
        decltype(auto) expected_last = std::get<3>(iterators);

        //Initialize RBM and trainign parameters
        init_training<Denoising>(rbm, input_first, input_last);

        //Some RBM may init weights based on the training data
        //Note: This can't be done in init_training, since it will
        //sometimes be called with the wrong input values
        init_weights(rbm, input_first, input_last);

        //Allocate the trainer
        auto trainer = get_trainer<Denoising>(rbm);

        //Train for max_epochs epoch
        for(std::size_t epoch = 0; epoch < max_epochs; ++epoch){
            //Shuffle if necessary
            shuffle<Denoising>(input_first, input_last, expected_first, expected_last);

            //Create a new context for this epoch
            rbm_training_context context;

            //Start a new epoch
            init_epoch();

            //Train on all the data
            train_sub(input_first, input_last, expected_first, trainer, context, rbm);

            //Finalize the current epoch
            finalize_epoch(epoch, context, rbm);

        }

        return finalize_training(rbm);
    }

    std::size_t batches = 0;
    std::size_t samples = 0;

    void init_epoch(){
        batches = 0;
        samples = 0;
    }

    template<typename IIT, typename EIT, typename Trainer>
    void train_sub(IIT input_first, IIT input_last, EIT expected_first, Trainer& trainer, rbm_training_context& context, rbm_t& rbm){
        auto iit = input_first;
        auto eit = expected_first;
        auto end = input_last;

        while(iit != end){
            auto istart = iit;
            auto estart = eit;

            std::size_t i = 0;
            while(iit != end && i < batch_size){
                ++iit;
                ++eit;
                ++samples;
                ++i;
            }

            ++batches;

            auto input_batch = make_batch(istart, iit);
            auto expected_batch = make_batch(estart, eit);
            trainer->train_batch(input_batch, expected_batch, context);

            context.reconstruction_error += context.batch_error;
            context.sparsity += context.batch_sparsity;

            if(EnableWatcher && layer_traits<rbm_t>::free_energy()){
                for(auto& v : input_batch){
                    context.free_energy += rbm.free_energy(v);
                }
            }

            if(EnableWatcher && layer_traits<rbm_t>::is_verbose()){
                watcher.batch_end(rbm, context, batches, total_batches);
            }
        }
    }

    void finalize_epoch(std::size_t epoch, rbm_training_context& context, rbm_t& rbm){
        //Average all the gathered information
        context.reconstruction_error /= batches;
        context.sparsity /= batches;
        context.free_energy /= samples;

        //After some time increase the momentum
        if(layer_traits<rbm_t>::has_momentum() && epoch == rbm.final_momentum_epoch){
            rbm.momentum = rbm.final_momentum;
        }

        //Notify the watcher
        if(EnableWatcher){
            watcher.epoch_end(epoch, context, rbm);
        }

        //Save the error for the return value
        last_error = context.reconstruction_error;
    }
};

} //end of dll namespace

#endif
<|endoftext|>