<commit_before>#include "cinder/app/App.h"
#include "cinder/Capture.h"
#include "cinder/Utilities.h"
#include "cinder/ImageIo.h"
#include "cinder/Thread.h"

#include "cinder/app/RendererGl.h"
#include "cinder/gl/Texture.h"
#include "cinder/gl/Fbo.h"
#include "cinder/gl/shader.h"
#include "cinder/gl/draw.h"
#include "cinder/gl/scoped.h"
#include "cinder/params/Params.h"

#include "IFaceTracker/IFaceTracker.h"

#include "Cinder-VNM/include/TextureHelper.h"
#include "Cinder-VNM/include/CaptureHelper.h"
#include "Cinder-VNM/include/MiniConfigImgui.h"
#include "Cinder-VNM/include/AssetManager.h"

#include "Cinder-ImGui/include/CinderImGui.h"

#include "Clone.h"

#if 0 // defined (CINDER_MSW)
#include "ciWMFVideoPlayer.h"
typedef ciWMFVideoPlayer CiMovieType;
#else
#include "cinder/qtime/QuickTimeGl.h"
typedef qtime::MovieSurfaceRef CiMovieType;
#endif

using namespace ci;
using namespace app;
using namespace std;

#if defined( CINDER_GL_ES )
namespace cinder {
    namespace gl {
        void enableWireframe() {}
        void disableWireframe() {}
    }
}
#endif

class FaceOff : public App
{
public:
    static void prepareSettings(Settings *settings)
    {
        readConfig();

        settings->setWindowSize(APP_W, APP_H);
        settings->setMultiTouchEnabled(false);
        settings->setFrameRate(60.0f);
        settings->setFullScreen(false);
        settings->setTitle("FaceVFX");
    }

    void keyUp(KeyEvent event)
    {
        switch (event.getChar())
        {
        case 'f':
        {
            setFullScreen(!isFullScreen());
            resize();
        }
        break;
        case KeyEvent::KEY_ESCAPE:
            quit();
            break;
        }
    }

    void fileDrop(FileDropEvent event)
    {
        if (event.getNumFiles() > 0)
        {
            console() << event.getFile(0);

            ImageSourceRef img = loadImage(event.getFile(0));
            updateOfflineImage(img);
        }
    }

    void setup();
    ~FaceOff()
    {
        mShouldQuit = true;
        mTrackerThread->join();
    }

    void update();
    void draw();
    void resize();

private:

    void updateOfflineImage(ImageSourceRef img)
    {
        if (!img) return;

        mOfflineTracker->reset();
        mOfflineTracker->update(img);

        auto loadTexFn = [this, img]
        {
            mOfflineFaceTex = mPhotoTex = gl::Texture::create(img, gl::Texture::Format().loadTopDown());
        };
        dispatchAsync(loadTexFn);
    }

    void trackerThreadFn();
    void updateClone();

    TriMesh         mFaceMesh;

    ft::FaceTrackerRef   mOnlineTracker;
    ft::FaceTrackerRef   mOfflineTracker;
    gl::TextureRef mPhotoTex;

    CiMovieType   mMovie;

    //
    vector<Capture::DeviceRef>  mDevices;
    vector<string>              mDeviceNames;
    CaptureHelper   mCapture;
    shared_ptr<thread> mTrackerThread;

    vector<string>              mPeopleNames;

    gl::TextureRef mOfflineFaceTex;
    gl::FboRef     mRenderedOfflineFaceFbo, mFaceMaskFbo;
    Clone       mClone;
    bool           mHasNewRenderedFace = false;

    //  param
    int         mDeviceId = -1;
    int         mPeopleId = -1;
    bool        mDoesCaptureNeedsInit = false;

    bool        mShouldQuit = false;
};

void FaceOff::resize()
{
    APP_W = getWindowWidth();
    APP_H = getWindowHeight();
}

void FaceOff::updateClone()
{
    gl::ScopedMatrices mvp;
    gl::setMatricesWindow(mRenderedOfflineFaceFbo->getSize());
    gl::ScopedViewport viewport(0, 0, mRenderedOfflineFaceFbo->getWidth(), mRenderedOfflineFaceFbo->getHeight());

    // TODO: merge these two passes w/ MRTs
    {
        gl::ScopedFramebuffer fbo(mRenderedOfflineFaceFbo);
        gl::ScopedGlslProg glsl(gl::getStockShader(gl::ShaderDef().texture()));
        gl::ScopedTextureBind t0(mOfflineFaceTex, 0);
        gl::clear(ColorA::black(), false);
        gl::draw(mFaceMesh);
    }

    if (!MOVIE_MODE)
    {
        {
            gl::ScopedFramebuffer fbo(mFaceMaskFbo);
            gl::clear(ColorA::black(), false);
            gl::draw(mFaceMesh);
        }

        // TODO: add gl::ScopedMatrices in mClone.update()
        mClone.update(mRenderedOfflineFaceFbo->getColorTexture(), mCapture.texture, mFaceMaskFbo->getColorTexture());
        mHasNewRenderedFace = true;
    }
}

void FaceOff::trackerThreadFn()
{
    ft::Option option;
    option.scale = 0.5f;
    mOfflineTracker = ft::IFaceTracker::create();
    mOnlineTracker = ft::IFaceTracker::create(option);

    bool shouldInitFaceMesh = false;

    while (!mShouldQuit)
    {
        // TODO: more robust with update_signal
        if (!mCapture.checkNewFrame())
        {
            sleep(1.0f);
            continue;
        }

        if (mDoesCaptureNeedsInit)
        {
            // TODO: more robust with setup_signal
            mDoesCaptureNeedsInit = false;

            CAM_W = mCapture.size.x;
            CAM_H = mCapture.size.y;

            auto createFboFn = [this]
            {
                gl::Fbo::Format fboFormat;
                fboFormat.enableDepthBuffer(false);
                mRenderedOfflineFaceFbo = gl::Fbo::create(CAM_W, CAM_H, fboFormat);
                mFaceMaskFbo = gl::Fbo::create(CAM_W, CAM_H, fboFormat);

                mClone.setup(CAM_W, CAM_H);
                mClone.setStrength(16);
            };
            dispatchAsync(createFboFn);
        }

        if (mPeopleId != PEOPLE_ID)
        {
            mPeopleId = PEOPLE_ID;

            ImageSourceRef img = loadImage(loadAsset("people/" + mPeopleNames[PEOPLE_ID]));
            updateOfflineImage(img);

            shouldInitFaceMesh = true;
        }

        mOnlineTracker->update(mCapture.surface);

        if (!mOnlineTracker->getFound())
        {
            mHasNewRenderedFace = false;
            continue;
        }

        int nPoints = mOnlineTracker->size();
        if (shouldInitFaceMesh)
        {
            shouldInitFaceMesh = false;
            mFaceMesh.getBufferTexCoords0().clear();

            auto imgSize = mOfflineTracker->getImageSize();
            for (int i = 0; i < nPoints; i++)
            {
                vec3 point = mOfflineTracker->getImagePoint(i);
                mFaceMesh.appendTexCoord({ point.x / imgSize.x, point.y / imgSize.y });
            }
            mOnlineTracker->addTriangleIndices(mFaceMesh);
        }

        mFaceMesh.getBufferPositions().clear();
        for (int i = 0; i < nPoints; i++)
        {
            mFaceMesh.appendPosition(mOnlineTracker->getImagePoint(i));
        }

        if (VFX_VISIBLE && mOfflineFaceTex)
        {
            dispatchAsync(bind(&FaceOff::updateClone, this));
        }
    }
}

void FaceOff::setup()
{
    ui::initialize(ui::Options().fontGlobalScale(getWindowContentScale()));

    resize();

    mTrackerThread = make_shared<thread>(bind(&FaceOff::trackerThreadFn, this));

    // list out the devices
    vector<Capture::DeviceRef> devices(Capture::getDevices());
    if (devices.empty())
    {
        console() << "No camera device connected." << endl;
        quit();
        return;
    }

    for (auto device : devices)
    {
        console() << "Found Device " << device->getName() << " ID: " << device->getUniqueId() << endl;

        if (device->checkAvailable())
        {
            mDevices.push_back(device);
            mDeviceNames.push_back(device->getName());
        }
    }

    mPeopleNames = am::shortPaths("people");

    setupConfigImgui();

    // TODO: assert
    if (DEVICE_ID > mDeviceNames.size() - 1)
    {
        DEVICE_ID = 0;
    }
#ifdef CINDER_COCOA_TOUCH
    DEVICE_ID = 1;
#endif
//    ADD_ENUM_TO_INT(mParam, DEVICE_ID, mDeviceNames);

    if (PEOPLE_ID > mPeopleNames.size() - 1)
    {
        PEOPLE_ID = 0;
    }
//    ADD_ENUM_TO_INT(mParam, PEOPLE_ID, mPeopleNames);

    gl::disableDepthRead();
    gl::disableDepthWrite();
}

void FaceOff::update()
{
    if (MOVIE_MODE)
    {
        if (!mMovie)
        {
            fs::path moviePath = getAssetPath(MOVIE_PATH);
            try
            {
                // load up the movie, set it to loop, and begin playing
                mMovie = qtime::MovieSurface::create(moviePath);
                mMovie->setLoop();
                mMovie->play();
                mOfflineFaceTex.reset();
            }
            catch (ci::Exception &exc)
            {
                console() << "Exception caught trying to load the movie from path: " << MOVIE_PATH << ", what: " << exc.what() << std::endl;
                mMovie.reset();
            }
        }
        else
        {
            if (mMovie->checkNewFrame())
            {
                auto surface = mMovie->getSurface();
                if (!mOfflineFaceTex)
                {
                    mOfflineFaceTex = gl::Texture2d::create(*surface, gl::Texture::Format().loadTopDown());
                }
                else
                {
                    mOfflineFaceTex->update(*surface);
                }
            }
        }
    }
    else
    {
        mMovie.reset();
        mOfflineFaceTex = mPhotoTex;
    }

    if (mDeviceId != DEVICE_ID)
    {
        mDeviceId = DEVICE_ID;
        mCapture.setup(CAM_W, CAM_H, mDevices[DEVICE_ID]);
        mDoesCaptureNeedsInit = true;
    }

    mCapture.flip = CAM_FLIP;
}

void FaceOff::draw()
{
    gl::clear(ColorA::black(), false);

    if (!mCapture.isReady())
        return;

    gl::setMatricesWindow(getWindowSize());

    gl::enableAlphaBlending();

    float camAspect = CAM_W / (float)CAM_H;
    float winAspect = getWindowAspectRatio();
    float adaptiveCamW = 0;
    float adaptiveCamH = 0;
    if (camAspect > winAspect)
    {
        adaptiveCamW = APP_W;
        adaptiveCamH = APP_W / camAspect;
    }
    else
    {
        adaptiveCamH = APP_H;
        adaptiveCamW = APP_H * camAspect;
    }
    Area srcArea = { 0, 0, CAM_W, CAM_H };
    Rectf dstRect =
    {
        APP_W * 0.5f - adaptiveCamW * 0.5f,
        APP_H * 0.5f - adaptiveCamH * 0.5f,
        APP_W * 0.5f + adaptiveCamW * 0.5f,
        APP_H * 0.5f + adaptiveCamH * 0.5f
    };

    if (!mOnlineTracker)
    {
        gl::draw(mCapture.texture, srcArea, dstRect);
        return;
    }

    gl::Texture2dRef fullscreenTex;
    if (VFX_VISIBLE && mHasNewRenderedFace)
    {
        fullscreenTex = mClone.getResultTexture();
    }
    else if (VFX_VISIBLE && MOVIE_MODE)
    {
        fullscreenTex = mRenderedOfflineFaceFbo->getColorTexture();
    }
    else
    {
        fullscreenTex = mCapture.texture;
    }
    gl::draw(fullscreenTex, srcArea, dstRect);

    if (OFFLINE_VISIBLE)
    {
        gl::draw(mOfflineFaceTex);
    }

    FPS = getAverageFps();

    gl::disableAlphaBlending();

    if (WIREFRAME_MODE && (MOVIE_MODE || mHasNewRenderedFace))
    {
        gl::enableWireframe();

        gl::ScopedModelMatrix modelMatrix;
        gl::ScopedColor color(ColorA(0, 1.0f, 0, 1.0f));
        gl::translate(dstRect.x1, dstRect.y1);
        gl::scale(adaptiveCamW / CAM_W, adaptiveCamH / CAM_H);
        gl::draw(mFaceMesh);

        if (OFFLINE_VISIBLE)
        {
            gl::draw(mOfflineTracker->getImageMesh());
        }

        gl::disableWireframe();
    }
}

CINDER_APP(FaceOff, RendererGl, &FaceOff::prepareSettings)
<commit_msg>Improve gui<commit_after>#include "cinder/app/App.h"
#include "cinder/Capture.h"
#include "cinder/Utilities.h"
#include "cinder/ImageIo.h"
#include "cinder/Thread.h"

#include "cinder/app/RendererGl.h"
#include "cinder/gl/Texture.h"
#include "cinder/gl/Fbo.h"
#include "cinder/gl/shader.h"
#include "cinder/gl/draw.h"
#include "cinder/gl/scoped.h"
#include "cinder/params/Params.h"

#include "IFaceTracker/IFaceTracker.h"

#include "Cinder-VNM/include/TextureHelper.h"
#include "Cinder-VNM/include/CaptureHelper.h"
#include "Cinder-VNM/include/MiniConfig.h"
#include "Cinder-VNM/include/AssetManager.h"

#include "Cinder-ImGui/include/CinderImGui.h"

#include "Clone.h"

#if 0 // defined (CINDER_MSW)
#include "ciWMFVideoPlayer.h"
typedef ciWMFVideoPlayer CiMovieType;
#else
#include "cinder/qtime/QuickTimeGl.h"
typedef qtime::MovieSurfaceRef CiMovieType;
#endif

using namespace ci;
using namespace app;
using namespace std;

#if defined( CINDER_GL_ES )
namespace cinder {
    namespace gl {
        void enableWireframe() {}
        void disableWireframe() {}
    }
}
#endif

class FaceOff : public App
{
public:
    static void prepareSettings(Settings *settings)
    {
        readConfig();

        settings->setWindowSize(APP_W, APP_H);
        settings->setMultiTouchEnabled(false);
        settings->setFrameRate(60.0f);
        settings->setFullScreen(false);
        settings->setTitle("FaceVFX");
    }

    void keyUp(KeyEvent event)
    {
        switch (event.getChar())
        {
        case 'f':
        {
            setFullScreen(!isFullScreen());
            resize();
        }
        break;
        case KeyEvent::KEY_ESCAPE:
            quit();
            break;
        }
    }

    void fileDrop(FileDropEvent event)
    {
        if (event.getNumFiles() > 0)
        {
            console() << event.getFile(0);

            ImageSourceRef img = loadImage(event.getFile(0));
            updateOfflineImage(img);
        }
    }

    void setup();
    ~FaceOff()
    {
        mShouldQuit = true;
        mTrackerThread->join();
    }

    void update();
    void draw();
    void resize();

private:

    void updateGui();

    void updateOfflineImage(ImageSourceRef img)
    {
        if (!img) return;

        mOfflineTracker->reset();
        mOfflineTracker->update(img);

        auto loadTexFn = [this, img]
        {
            mOfflineFaceTex = mPhotoTex = gl::Texture::create(img, gl::Texture::Format().loadTopDown());
        };
        dispatchAsync(loadTexFn);
    }

    void trackerThreadFn();
    void updateClone();

    TriMesh         mFaceMesh;

    ft::FaceTrackerRef   mOnlineTracker;
    ft::FaceTrackerRef   mOfflineTracker;
    gl::TextureRef mPhotoTex;

    CiMovieType   mMovie;

    //
    vector<Capture::DeviceRef>  mDevices;
    vector<string>              mDeviceNames;
    CaptureHelper   mCapture;
    shared_ptr<thread> mTrackerThread;

    vector<string>              mPeopleNames;

    gl::TextureRef mOfflineFaceTex;
    gl::FboRef     mRenderedOfflineFaceFbo, mFaceMaskFbo;
    Clone       mClone;
    bool           mHasNewRenderedFace = false;

    //  param
    int         mDeviceId = -1;
    int         mPeopleId = -1;
    bool        mDoesCaptureNeedsInit = false;

    bool        mShouldQuit = false;
};

void FaceOff::resize()
{
    APP_W = getWindowWidth();
    APP_H = getWindowHeight();
}

void FaceOff::updateClone()
{
    gl::ScopedMatrices mvp;
    gl::setMatricesWindow(mRenderedOfflineFaceFbo->getSize());
    gl::ScopedViewport viewport(0, 0, mRenderedOfflineFaceFbo->getWidth(), mRenderedOfflineFaceFbo->getHeight());

    // TODO: merge these two passes w/ MRTs
    {
        gl::ScopedFramebuffer fbo(mRenderedOfflineFaceFbo);
        gl::ScopedGlslProg glsl(gl::getStockShader(gl::ShaderDef().texture()));
        gl::ScopedTextureBind t0(mOfflineFaceTex, 0);
        gl::clear(ColorA::black(), false);
        gl::draw(mFaceMesh);
    }

    if (!MOVIE_MODE)
    {
        {
            gl::ScopedFramebuffer fbo(mFaceMaskFbo);
            gl::clear(ColorA::black(), false);
            gl::draw(mFaceMesh);
        }

        // TODO: add gl::ScopedMatrices in mClone.update()
        mClone.update(mRenderedOfflineFaceFbo->getColorTexture(), mCapture.texture, mFaceMaskFbo->getColorTexture());
        mHasNewRenderedFace = true;
    }
}

void FaceOff::trackerThreadFn()
{
    ft::Option option;
    option.scale = 0.5f;
    mOfflineTracker = ft::IFaceTracker::create();
    mOnlineTracker = ft::IFaceTracker::create(option);

    bool shouldInitFaceMesh = false;

    while (!mShouldQuit)
    {
        // TODO: more robust with update_signal
        if (!mCapture.checkNewFrame())
        {
            sleep(1.0f);
            continue;
        }

        if (mDoesCaptureNeedsInit)
        {
            // TODO: more robust with setup_signal
            mDoesCaptureNeedsInit = false;

            CAM_W = mCapture.size.x;
            CAM_H = mCapture.size.y;

            auto createFboFn = [this]
            {
                gl::Fbo::Format fboFormat;
                fboFormat.enableDepthBuffer(false);
                mRenderedOfflineFaceFbo = gl::Fbo::create(CAM_W, CAM_H, fboFormat);
                mFaceMaskFbo = gl::Fbo::create(CAM_W, CAM_H, fboFormat);

                mClone.setup(CAM_W, CAM_H);
                mClone.setStrength(16);
            };
            dispatchAsync(createFboFn);
        }

        if (mPeopleId != PEOPLE_ID)
        {
            mPeopleId = PEOPLE_ID;

            ImageSourceRef img = loadImage(loadAsset("people/" + mPeopleNames[PEOPLE_ID]));
            updateOfflineImage(img);

            shouldInitFaceMesh = true;
        }

        mOnlineTracker->update(mCapture.surface);

        if (!mOnlineTracker->getFound())
        {
            mHasNewRenderedFace = false;
            continue;
        }

        int nPoints = mOnlineTracker->size();
        if (shouldInitFaceMesh)
        {
            shouldInitFaceMesh = false;
            mFaceMesh.getBufferTexCoords0().clear();

            auto imgSize = mOfflineTracker->getImageSize();
            for (int i = 0; i < nPoints; i++)
            {
                vec3 point = mOfflineTracker->getImagePoint(i);
                mFaceMesh.appendTexCoord({ point.x / imgSize.x, point.y / imgSize.y });
            }
            mOnlineTracker->addTriangleIndices(mFaceMesh);
        }

        mFaceMesh.getBufferPositions().clear();
        for (int i = 0; i < nPoints; i++)
        {
            mFaceMesh.appendPosition(mOnlineTracker->getImagePoint(i));
        }

        if (VFX_VISIBLE && mOfflineFaceTex)
        {
            dispatchAsync(bind(&FaceOff::updateClone, this));
        }
    }
}

void FaceOff::setup()
{
    ui::initialize(ui::Options().fontGlobalScale(getWindowContentScale()));

    resize();

    mTrackerThread = make_shared<thread>(bind(&FaceOff::trackerThreadFn, this));

    // list out the devices
    vector<Capture::DeviceRef> devices(Capture::getDevices());
    if (devices.empty())
    {
        console() << "No camera device connected." << endl;
        quit();
        return;
    }

    for (auto device : devices)
    {
        console() << "Found Device " << device->getName() << " ID: " << device->getUniqueId() << endl;

        if (device->checkAvailable())
        {
            mDevices.push_back(device);
            mDeviceNames.push_back(device->getName());
        }
    }

    mPeopleNames = am::shortPaths("people");

    // TODO: assert
    if (DEVICE_ID > mDeviceNames.size() - 1)
    {
        DEVICE_ID = 0;
    }
#ifdef CINDER_COCOA_TOUCH
    DEVICE_ID = 1; // select selfie camera
#endif

    if (PEOPLE_ID > mPeopleNames.size() - 1)
    {
        PEOPLE_ID = 0;
    }

    gl::disableDepthRead();
    gl::disableDepthWrite();
}

void FaceOff::update()
{
    if (MOVIE_MODE)
    {
        if (!mMovie)
        {
            fs::path moviePath = getAssetPath(MOVIE_PATH);
            try
            {
                // load up the movie, set it to loop, and begin playing
                mMovie = qtime::MovieSurface::create(moviePath);
                mMovie->setLoop();
                mMovie->play();
                mOfflineFaceTex.reset();
            }
            catch (ci::Exception &exc)
            {
                console() << "Exception caught trying to load the movie from path: " << MOVIE_PATH << ", what: " << exc.what() << std::endl;
                mMovie.reset();
            }
        }
        else
        {
            if (mMovie->checkNewFrame())
            {
                auto surface = mMovie->getSurface();
                if (!mOfflineFaceTex)
                {
                    mOfflineFaceTex = gl::Texture2d::create(*surface, gl::Texture::Format().loadTopDown());
                }
                else
                {
                    mOfflineFaceTex->update(*surface);
                }
            }
        }
    }
    else
    {
        mMovie.reset();
        mOfflineFaceTex = mPhotoTex;
    }

    if (mDeviceId != DEVICE_ID)
    {
        mDeviceId = DEVICE_ID;
        mCapture.setup(CAM_W, CAM_H, mDevices[DEVICE_ID]);
        mDoesCaptureNeedsInit = true;
    }

    if (mCapture.isFrontCamera)
        mCapture.flip = CAM_FLIP;
    else
        mCapture.flip = false;
}

void FaceOff::updateGui()
{
    ui::ScopedWindow window("Settings", {toPixels(getWindowWidth() / 2), toPixels(getWindowHeight() / 2)}, 0.5f);

    if (ui::Button("Save Config"))
    {
        writeConfig();
    }

    if (ui::Button("Save Image"))
    {
        auto windowSurf = copyWindowSurface();
#ifdef CINDER_COCOA_TOUCH
        cocoa::writeToSavedPhotosAlbum(windowSurf);
#else
        fs::path writePath = getAppPath() / ("screenshot_" + toString(getElapsedFrames()) + ".png");
        writeImage(writePath, windowSurf);
#endif
    }

    if (ui::Button("Quit"))
    {
        App::get()->quit();
    }

    ui::Text("FPS: %d", FPS);
    ui::Checkbox("CAM_FLIP", &CAM_FLIP);
    ui::Checkbox("OFFLINE_VISIBLE", &OFFLINE_VISIBLE);
    ui::Checkbox("VFX_VISIBLE", &VFX_VISIBLE);
    ui::Checkbox("WIREFRAME_MODE", &WIREFRAME_MODE);

    ui::Checkbox("MOVIE_MODE", &MOVIE_MODE);
    ui::InputText("MOVIE_PATH", &MOVIE_PATH);

    ui::Combo("DEVICE_ID", &DEVICE_ID, mDeviceNames);
    ui::Combo("PEOPLE_ID", &PEOPLE_ID, mPeopleNames);
}

void FaceOff::draw()
{
    gl::clear(ColorA::black(), false);

    if (!mCapture.isReady())
        return;

    gl::setMatricesWindow(getWindowSize());

    gl::enableAlphaBlending();

    float camAspect = CAM_W / (float)CAM_H;
    float winAspect = getWindowAspectRatio();
    float adaptiveCamW = 0;
    float adaptiveCamH = 0;
    if (camAspect > winAspect)
    {
        adaptiveCamW = APP_W;
        adaptiveCamH = APP_W / camAspect;
    }
    else
    {
        adaptiveCamH = APP_H;
        adaptiveCamW = APP_H * camAspect;
    }
    Area srcArea = { 0, 0, CAM_W, CAM_H };
    Rectf dstRect =
    {
        APP_W * 0.5f - adaptiveCamW * 0.5f,
        APP_H * 0.5f - adaptiveCamH * 0.5f,
        APP_W * 0.5f + adaptiveCamW * 0.5f,
        APP_H * 0.5f + adaptiveCamH * 0.5f
    };

    if (!mOnlineTracker)
    {
        gl::draw(mCapture.texture, srcArea, dstRect);
        return;
    }

    gl::Texture2dRef fullscreenTex;
    if (VFX_VISIBLE && mHasNewRenderedFace)
    {
        fullscreenTex = mClone.getResultTexture();
    }
    else if (VFX_VISIBLE && MOVIE_MODE)
    {
        fullscreenTex = mRenderedOfflineFaceFbo->getColorTexture();
    }
    else
    {
        fullscreenTex = mCapture.texture;
    }
    gl::draw(fullscreenTex, srcArea, dstRect);

    if (OFFLINE_VISIBLE)
    {
        gl::draw(mOfflineFaceTex);
    }

    FPS = getAverageFps();

    gl::disableAlphaBlending();

    if (WIREFRAME_MODE && (MOVIE_MODE || mHasNewRenderedFace))
    {
        gl::enableWireframe();

        gl::ScopedModelMatrix modelMatrix;
        gl::ScopedColor color(ColorA(0, 1.0f, 0, 1.0f));
        gl::translate(dstRect.x1, dstRect.y1);
        gl::scale(adaptiveCamW / CAM_W, adaptiveCamH / CAM_H);
        gl::draw(mFaceMesh);

        if (OFFLINE_VISIBLE)
        {
            gl::draw(mOfflineTracker->getImageMesh());
        }

        gl::disableWireframe();
    }

    updateGui();
}

CINDER_APP(FaceOff, RendererGl, &FaceOff::prepareSettings)
<|endoftext|>


<commit_before>/*
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef GEXCEPTION_HXX
#define GEXCEPTION_HXX

#include <glib.h>

#include <exception>

/**
 * A GQuark for std::exception.
 */
G_GNUC_CONST
static inline GQuark
exception_quark()
{
    return g_quark_from_static_string("std::exception");
}

static inline void
SetGError(GError **error_r, const std::exception &e)
{
    g_set_error_literal(error_r, exception_quark(), 0, e.what());
}

static inline GError *
ToGError(const std::exception &e)
{
    return g_error_new_literal(exception_quark(), 0, e.what());
}

#endif
<commit_msg>GException: support std::exception_ptr<commit_after>/*
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef GEXCEPTION_HXX
#define GEXCEPTION_HXX

#include <glib.h>

#include <exception>

/**
 * A GQuark for std::exception.
 */
G_GNUC_CONST
static inline GQuark
exception_quark()
{
    return g_quark_from_static_string("std::exception");
}

static inline void
SetGError(GError **error_r, const std::exception &e)
{
    g_set_error_literal(error_r, exception_quark(), 0, e.what());
}

static inline GError *
ToGError(const std::exception &e)
{
    return g_error_new_literal(exception_quark(), 0, e.what());
}

static inline GError *
ToGError(std::exception_ptr ep)
{
    try {
        std::rethrow_exception(ep);
    } catch (const std::exception &e) {
        return ToGError(e);
    } catch (...) {
        return g_error_new_literal(exception_quark(), 0,
                                   "Unexpected C++ exception");
    }
}

#endif
<|endoftext|>


<commit_before>#include "types.hpp"
#include "common.hpp"
#include <cmath>
#include <opencv2/opencv.hpp>
#include <mpel/builtins.hpp>

namespace mpel {

potential_field_interpolator::potential_field_interpolator() {
	//throw UnimplementedException();
}

/* returns the norm of a point */
double norm(PointRef p) {
	return sqrt(p.x * p.x + p.y * p.y);
}

/* return the squared euler distance between two points */
double sqr_dist(PointRef p1, PointRef p2) {
	double dx = p1.x - p2.x;
	double dy = p1.y - p2.y;
	return dx * dx + dy * dy;
}

/* construct the attractive potential at every location in the grid
 * rows, cols denotes the size of the workspace
 * goal denotes the point where we wish to reach
 * c is the constant scaling factor used for the field f(x) = c * ||x - xg||^2
 */
cv::Mat attractive_potential(size_t rows, size_t cols, Point goal, double c = 250) {
	cv::Mat pot = cv::Mat::zeros(rows, cols, CV_64FC1);
	for (size_t i = 0; i < rows; ++i) {
		for (size_t j = 0; j < cols; ++j) {
			Point pt(j, i);
			pot.at<double>(pt) = sqr_dist(goal, pt);
		}
	}
	cv::normalize(pot, pot, 0, c, cv::NORM_MINMAX);
	return pot;
}

/* repulsive field due to obstacles in the field
 * m is the 2D map of the workspace
 * d is the distance parameter used in constructing the repulsive potential
 * c is a constant scaling factor
 */
cv::Mat repulsive_potential(MapRef m, double d = 250, double c = 250) {
	// compute the distance transform of this image
	cv::Mat dt;
	cv::distanceTransform(m, dt, CV_DIST_L2, CV_DIST_MASK_PRECISE);
	dt.convertTo(dt, CV_64F);

	// compute the following function for repulsive potential
	//              /  1      1 \ 2
	// f(x) =  c * | ----- - --- |   for r(x) < d0
	//              \ r(x)    d /

	cv::Mat pot = cv::Mat::zeros(m.rows, m.cols, CV_64FC1);

	for (int i = 0; i < m.rows; ++i) {
		for (int j = 0; j < m.cols; ++j) {
			Point pt(j, i);
			double dist = dt.at<double>(pt);
			if (dist < d) {
				double p = (1./(dist+1) - 1./d) * (1./(dist+1) - 1./d);
				if (p > c) p = c;
				pot.at<double>(pt) = p;
			}
		}
	}
	cv::normalize(pot, pot, 0, c, cv::NORM_MINMAX);
	return pot;
}

/* return the direction of max descent in the given matrix
 * at the given location. The return value is a point of the
 * form [dx, dy] where dx and dy are the displacements in the
 * x and y directions respectively. n specifies the size of
 * the square neighborhood to consider
 */
Point max_descent_direction(cv::Mat m, PointRef pt, int n = 2) {
	double min_grad = 0;
	Point dir = Point(0, 0);
	double val = m.at<double>(pt);
	cv::Rect r(0, 0, m.cols, m.rows);
	for (int i = -n; i <= n; ++i) {
		for (int j = -n; j <= n; ++j) {
			Point tmp = Point(j,i);
			if (not r.contains(pt+tmp)) continue;
			double grad = (m.at<double>(pt+tmp) - val)/norm(tmp);
			if (grad <= min_grad) {
				min_grad = grad;
				dir = tmp;
			}
		}
	}
	return dir;
}

/* interpolate a single segment in the path using attraction and
 * repulsion terms provided as arguments to the function. The parameter
 * d specifies how close we need to get to the final goal.
 */
Path interpolate_segment(Point in, Point out, const cv::Mat& attr, const cv::Mat& rep, double d = 10) {
	Path p;
	cv::Mat pot_field = attr + rep;
	Point curr = in;
	p.push_back(curr);
	Point old_dir = Point(0, 0);
	while (sqr_dist(curr, out) > d*d) {
		Point dir = max_descent_direction(pot_field, curr);

		/* highly probable even in simple environments, debug message just in case
		 * we get stuck in a local minima and can't decide on the direction
		 * returns the partially interpolated path
		 */
		if (dir + old_dir == Point(0,0)) {
			std::cout << "Stuck in a local minima :(" << std::endl;
			return p;
		}
		p.push_back(curr + dir);
		curr = curr + dir;
		old_dir = dir;
	}
	return p;
}

Path potential_field_interpolator::operator()(MapRef map, PathRef _path) {
	Path path = subdivide_path(_path, 10);
	Path p;
	Point curr = path.front();
	cv::Mat rep = repulsive_potential(map);

	size_t tmp_goal = 1;
	while (tmp_goal < path.size()) {
		// find a temporary goal point in the map
		while (is_collision(map, path[tmp_goal]) and tmp_goal < path.size() - 1) tmp_goal++;
		cv::Mat attr = attractive_potential(map.rows, map.cols, path[tmp_goal]);
		Path tmp = interpolate_segment(curr, path[tmp_goal], attr, rep);
		p.insert(p.end(), tmp.begin(), tmp.end());
		curr = p.back();
		tmp_goal++;
	}
	p.push_back(path.back());

	return p;
}

}
<commit_msg>Bugfix in potential_field_interpolator [Issue 8]<commit_after>#include "types.hpp"
#include "common.hpp"
#include <cmath>
#include <opencv2/opencv.hpp>
#include <mpel/builtins.hpp>

namespace mpel {

potential_field_interpolator::potential_field_interpolator() {
	//throw UnimplementedException();
}

/* returns the norm of a point */
double norm(PointRef p) {
	return sqrt(p.x * p.x + p.y * p.y);
}

/* return the squared euler distance between two points */
double sqr_dist(PointRef p1, PointRef p2) {
	double dx = p1.x - p2.x;
	double dy = p1.y - p2.y;
	return dx * dx + dy * dy;
}

/* construct the attractive potential at every location in the grid
 * rows, cols denotes the size of the workspace
 * goal denotes the point where we wish to reach
 * c is the constant scaling factor used for the field f(x) = c * ||x - xg||^2
 */
cv::Mat attractive_potential(size_t rows, size_t cols, Point goal, double c = 250) {
	cv::Mat pot = cv::Mat::zeros(rows, cols, CV_64FC1);
	for (size_t i = 0; i < rows; ++i) {
		for (size_t j = 0; j < cols; ++j) {
			Point pt(j, i);
			pot.at<double>(pt) = sqr_dist(goal, pt);
		}
	}
	cv::normalize(pot, pot, 0, c, cv::NORM_MINMAX);
	return pot;
}

/* repulsive field due to obstacles in the field
 * m is the 2D map of the workspace
 * d is the distance parameter used in constructing the repulsive potential
 * c is a constant scaling factor
 */
cv::Mat repulsive_potential(MapRef m, double d = 250, double c = 250) {
	// compute the distance transform of this image
	cv::Mat dt;
	cv::distanceTransform(m, dt, CV_DIST_L2, CV_DIST_MASK_PRECISE);
	dt.convertTo(dt, CV_64F);

	// compute the following function for repulsive potential
	//              /  1      1 \ 2
	// f(x) =  c * | ----- - --- |   for r(x) < d0
	//              \ r(x)    d /

	cv::Mat pot = cv::Mat::zeros(m.rows, m.cols, CV_64FC1);

	for (int i = 0; i < m.rows; ++i) {
		for (int j = 0; j < m.cols; ++j) {
			Point pt(j, i);
			double dist = dt.at<double>(pt);
			if (dist < d) {
				double p = (1./(dist+1) - 1./d) * (1./(dist+1) - 1./d);
				if (p > c) p = c;
				pot.at<double>(pt) = p;
			}
		}
	}
	cv::normalize(pot, pot, 0, c, cv::NORM_MINMAX);
	return pot;
}

/* return the direction of max descent in the given matrix
 * at the given location. The return value is a point of the
 * form [dx, dy] where dx and dy are the displacements in the
 * x and y directions respectively. n specifies the size of
 * the square neighborhood to consider
 */
Point max_descent_direction(cv::Mat m, PointRef pt, int n = 2) {
	double min_grad = 0;
	Point dir = Point(0, 0);
	double val = m.at<double>(pt);
	cv::Rect r(0, 0, m.cols, m.rows);
	for (int i = -n; i <= n; ++i) {
		for (int j = -n; j <= n; ++j) {
			Point tmp = Point(j,i);
			if (not r.contains(pt+tmp)) continue;
			double grad = (m.at<double>(pt+tmp) - val)/norm(tmp);
			if (grad <= min_grad) {
				min_grad = grad;
				dir = tmp;
			}
		}
	}
	return dir;
}

/* interpolate a single segment in the path using attraction and
 * repulsion terms provided as arguments to the function. The parameter
 * d specifies how close we need to get to the final goal.
 */
Path interpolate_segment(Point in, Point out, const cv::Mat& attr, const cv::Mat& rep, double d = 10) {
	Path p;
	cv::Mat pot_field = attr + rep;
	Point curr = in;
	p.push_back(curr);
	Point old_dir = Point(0, 0);
	while (sqr_dist(curr, out) > d*d) {
		Point dir = max_descent_direction(pot_field, curr);

		/* highly probable even in simple environments, debug message just in case
		 * we get stuck in a local minima and can't decide on the direction
		 * returns the partially interpolated path
		 */
		if (dir + old_dir == Point(0,0)) {
			std::cout << "Stuck in a local minima :(" << std::endl;
			return p;
		}
		p.push_back(curr + dir);
		curr = curr + dir;
		old_dir = dir;
	}
	return p;
}

Path potential_field_interpolator::operator()(MapRef map, PathRef _path) {
	Path path = subdivide_path(_path, 10);
	Path p;
	Point curr = path.front();
	cv::Mat rep = repulsive_potential(map);

	size_t tmp_goal = 1; // index of the temporary goal location
	while (tmp_goal < path.size()) {
		// find a temporary goal point in the map
		while (is_collision(map, path[tmp_goal]) and tmp_goal < path.size() - 1) tmp_goal++;
		cv::Mat attr = attractive_potential(map.rows, map.cols, path[tmp_goal]);
		double d = (tmp_goal == path.size() - 1 ? 0 : 10);
		Path tmp = interpolate_segment(curr, path[tmp_goal], attr, rep, d);
		p.insert(p.end(), tmp.begin(), tmp.end());
		curr = p.back();
		tmp_goal++;
	}
	if (p.back() != path.back()) {
		Segment s(p.back(), path.back());
		if (not is_collision(map, s))
			p.push_back(path.back());
	}
	if (p.back() != path.back()) {
		std::cout << "Cannot recover from local minima, path not found" << std::endl;
	}

	return p;
}

}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: unocontrolbase.cxx,v $
 *
 *  $Revision: 1.9 $
 *
 *  last change: $Author: kz $ $Date: 2007-02-14 15:34:36 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_toolkit.hxx"

#ifndef _COM_SUN_STAR_AWT_XLAYOUTCONSTRAINS_HPP_
#include <com/sun/star/awt/XLayoutConstrains.hpp>
#endif
#ifndef _COM_SUN_STAR_AWT_XTEXTLAYOUTCONSTRAINS_HPP_
#include <com/sun/star/awt/XTextLayoutConstrains.hpp>
#endif

#include <toolkit/controls/unocontrolbase.hxx>
#include <toolkit/helper/property.hxx>

#include <tools/debug.hxx>

//  ----------------------------------------------------
//  class UnoControlBase
//  ----------------------------------------------------

sal_Bool UnoControlBase::ImplHasProperty( sal_uInt16 nPropId )
{
    ::rtl::OUString aPropName( GetPropertyName( nPropId ) );
    return ImplHasProperty( aPropName );
}

sal_Bool UnoControlBase::ImplHasProperty( const ::rtl::OUString& aPropertyName )
{
    ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >  xPSet( mxModel, ::com::sun::star::uno::UNO_QUERY );
    if ( !xPSet.is() )
        return sal_False;
    ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySetInfo >  xInfo = xPSet->getPropertySetInfo();
    if ( !xInfo.is() )
        return sal_False;

    return xInfo->hasPropertyByName( aPropertyName );
}

void UnoControlBase::ImplSetPropertyValues( const ::com::sun::star::uno::Sequence< ::rtl::OUString >& aPropertyNames, const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any >& aValues, sal_Bool bUpdateThis )
{
    ::com::sun::star::uno::Reference< ::com::sun::star::beans::XMultiPropertySet > xMPS( mxModel, ::com::sun::star::uno::UNO_QUERY );
    if ( !mxModel.is() )
        return;

    DBG_ASSERT( xMPS.is(), "UnoControlBase::ImplSetPropertyValues: no multi property set interface!" );
    if ( xMPS.is() )
    {
        if ( !bUpdateThis )
            ImplLockPropertyChangeNotifications( aPropertyNames, true );

        try
        {
            xMPS->setPropertyValues( aPropertyNames, aValues );
        }
        catch( const ::com::sun::star::uno::Exception& )
        {
            if ( !bUpdateThis )
                ImplLockPropertyChangeNotifications( aPropertyNames, false );
        }
        if ( !bUpdateThis )
            ImplLockPropertyChangeNotifications( aPropertyNames, false );
    }
    else
    {
        int dummy = 0;
        (void)dummy;
    }
}

void UnoControlBase::ImplSetPropertyValue( const ::rtl::OUString& aPropertyName, const ::com::sun::star::uno::Any& aValue, sal_Bool bUpdateThis )
{
    // Model ggf. schon abgemeldet, aber ein Event schlaegt noch zu...
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >  xPSet( mxModel, ::com::sun::star::uno::UNO_QUERY );
        if ( !bUpdateThis )
            ImplLockPropertyChangeNotification( aPropertyName, true );

        try
        {
            xPSet->setPropertyValue( aPropertyName, aValue );
        }
        catch( const com::sun::star::uno::Exception& )
        {
            if ( !bUpdateThis )
                ImplLockPropertyChangeNotification( aPropertyName, false );
            throw;
        }
        if ( !bUpdateThis )
            ImplLockPropertyChangeNotification( aPropertyName, false );
    }
}

::com::sun::star::uno::Any UnoControlBase::ImplGetPropertyValue( const ::rtl::OUString& aPropertyName )
{
    ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >  xPSet( mxModel, ::com::sun::star::uno::UNO_QUERY );
    if ( xPSet.is() )
        return xPSet->getPropertyValue( aPropertyName );
    else
        return ::com::sun::star::uno::Any();
}

sal_Bool UnoControlBase::ImplGetPropertyValue_BOOL( sal_uInt16 nProp )
{
    sal_Bool b = sal_False;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= b;
    }
    return b;
}

sal_Int16 UnoControlBase::ImplGetPropertyValue_INT16( sal_uInt16 nProp )
{
    sal_Int16 n = 0;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= n;
    }
    return n;
}

sal_uInt16 UnoControlBase::ImplGetPropertyValue_UINT16( sal_uInt16 nProp )
{
    sal_uInt16 n = 0;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= n;
    }
    return n;
}

sal_Int32 UnoControlBase::ImplGetPropertyValue_INT32( sal_uInt16 nProp )
{
    sal_Int32 n = 0;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= n;
    }
    return n;
}

sal_uInt32 UnoControlBase::ImplGetPropertyValue_UINT32( sal_uInt16 nProp )
{
    sal_uInt32 n = 0;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= n;
    }
    return n;
}

double UnoControlBase::ImplGetPropertyValue_DOUBLE( sal_uInt16 nProp )
{
    double n = 0;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= n;
    }
    return n;
}

::rtl::OUString UnoControlBase::ImplGetPropertyValue_UString( sal_uInt16 nProp )
{
    ::rtl::OUString aStr;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= aStr;
    }
    return aStr;
}

::com::sun::star::awt::Size UnoControlBase::Impl_getMinimumSize()
{
    ::com::sun::star::awt::Size aSz;
    ::com::sun::star::uno::Reference< ::com::sun::star::awt::XWindowPeer >  xP = ImplGetCompatiblePeer( sal_True );
    DBG_ASSERT( xP.is(), "Layout: No Peer!" );
    if ( xP.is() )
    {
        ::com::sun::star::uno::Reference< ::com::sun::star::awt::XLayoutConstrains >  xL( xP, ::com::sun::star::uno::UNO_QUERY );
        if ( xL.is() )
            aSz = xL->getMinimumSize();

        if ( !getPeer().is() || ( getPeer() != xP ) )
            xP->dispose();
    }
    return aSz;
}

::com::sun::star::awt::Size UnoControlBase::Impl_getPreferredSize()
{
    ::com::sun::star::awt::Size aSz;
    ::com::sun::star::uno::Reference< ::com::sun::star::awt::XWindowPeer >  xP = ImplGetCompatiblePeer( sal_True );
    DBG_ASSERT( xP.is(), "Layout: No Peer!" );
    if ( xP.is() )
    {
        ::com::sun::star::uno::Reference< ::com::sun::star::awt::XLayoutConstrains >  xL( xP, ::com::sun::star::uno::UNO_QUERY );
        if ( xL.is() )
            aSz = xL->getPreferredSize();

        if ( !getPeer().is() || ( getPeer() != xP ) )
            xP->dispose();
    }
    return aSz;
}

::com::sun::star::awt::Size UnoControlBase::Impl_calcAdjustedSize( const ::com::sun::star::awt::Size& rNewSize )
{
    ::com::sun::star::awt::Size aSz;
    ::com::sun::star::uno::Reference< ::com::sun::star::awt::XWindowPeer >  xP = ImplGetCompatiblePeer( sal_True );
    DBG_ASSERT( xP.is(), "Layout: No Peer!" );
    if ( xP.is() )
    {
        ::com::sun::star::uno::Reference< ::com::sun::star::awt::XLayoutConstrains > xL( xP, ::com::sun::star::uno::UNO_QUERY );
        if ( xL.is() )
            aSz = xL->calcAdjustedSize( rNewSize );

        if ( !getPeer().is() || ( getPeer() != xP ) )
            xP->dispose();
    }
    return aSz;
}

::com::sun::star::awt::Size UnoControlBase::Impl_getMinimumSize( sal_Int16 nCols, sal_Int16 nLines )
{
    ::com::sun::star::awt::Size aSz;
    ::com::sun::star::uno::Reference< ::com::sun::star::awt::XWindowPeer >  xP = ImplGetCompatiblePeer( sal_True );
    DBG_ASSERT( xP.is(), "Layout: No Peer!" );
    if ( xP.is() )
    {
        ::com::sun::star::uno::Reference< ::com::sun::star::awt::XTextLayoutConstrains >  xL( xP, ::com::sun::star::uno::UNO_QUERY );
        if ( xL.is() )
            aSz = xL->getMinimumSize( nCols, nLines );

        if ( !getPeer().is() || ( getPeer() != xP ) )
            xP->dispose();
    }
    return aSz;
}

void UnoControlBase::Impl_getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines )
{
    ::com::sun::star::uno::Reference< ::com::sun::star::awt::XWindowPeer >  xP = ImplGetCompatiblePeer( sal_True );
    DBG_ASSERT( xP.is(), "Layout: No Peer!" );
    if ( xP.is() )
    {
        ::com::sun::star::uno::Reference< ::com::sun::star::awt::XTextLayoutConstrains >  xL( xP, ::com::sun::star::uno::UNO_QUERY );
        if ( xL.is() )
            xL->getColumnsAndLines( nCols, nLines );

        if ( !getPeer().is() || ( getPeer() != xP ) )
            xP->dispose();
    }
}

<commit_msg>INTEGRATION: CWS changefileheader (1.9.146); FILE MERGED 2008/04/01 12:56:52 thb 1.9.146.2: #i85898# Stripping all external header guards 2008/03/28 15:40:17 rt 1.9.146.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: unocontrolbase.cxx,v $
 * $Revision: 1.10 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_toolkit.hxx"
#include <com/sun/star/awt/XLayoutConstrains.hpp>
#include <com/sun/star/awt/XTextLayoutConstrains.hpp>

#include <toolkit/controls/unocontrolbase.hxx>
#include <toolkit/helper/property.hxx>

#include <tools/debug.hxx>

//  ----------------------------------------------------
//  class UnoControlBase
//  ----------------------------------------------------

sal_Bool UnoControlBase::ImplHasProperty( sal_uInt16 nPropId )
{
    ::rtl::OUString aPropName( GetPropertyName( nPropId ) );
    return ImplHasProperty( aPropName );
}

sal_Bool UnoControlBase::ImplHasProperty( const ::rtl::OUString& aPropertyName )
{
    ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >  xPSet( mxModel, ::com::sun::star::uno::UNO_QUERY );
    if ( !xPSet.is() )
        return sal_False;
    ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySetInfo >  xInfo = xPSet->getPropertySetInfo();
    if ( !xInfo.is() )
        return sal_False;

    return xInfo->hasPropertyByName( aPropertyName );
}

void UnoControlBase::ImplSetPropertyValues( const ::com::sun::star::uno::Sequence< ::rtl::OUString >& aPropertyNames, const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any >& aValues, sal_Bool bUpdateThis )
{
    ::com::sun::star::uno::Reference< ::com::sun::star::beans::XMultiPropertySet > xMPS( mxModel, ::com::sun::star::uno::UNO_QUERY );
    if ( !mxModel.is() )
        return;

    DBG_ASSERT( xMPS.is(), "UnoControlBase::ImplSetPropertyValues: no multi property set interface!" );
    if ( xMPS.is() )
    {
        if ( !bUpdateThis )
            ImplLockPropertyChangeNotifications( aPropertyNames, true );

        try
        {
            xMPS->setPropertyValues( aPropertyNames, aValues );
        }
        catch( const ::com::sun::star::uno::Exception& )
        {
            if ( !bUpdateThis )
                ImplLockPropertyChangeNotifications( aPropertyNames, false );
        }
        if ( !bUpdateThis )
            ImplLockPropertyChangeNotifications( aPropertyNames, false );
    }
    else
    {
        int dummy = 0;
        (void)dummy;
    }
}

void UnoControlBase::ImplSetPropertyValue( const ::rtl::OUString& aPropertyName, const ::com::sun::star::uno::Any& aValue, sal_Bool bUpdateThis )
{
    // Model ggf. schon abgemeldet, aber ein Event schlaegt noch zu...
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >  xPSet( mxModel, ::com::sun::star::uno::UNO_QUERY );
        if ( !bUpdateThis )
            ImplLockPropertyChangeNotification( aPropertyName, true );

        try
        {
            xPSet->setPropertyValue( aPropertyName, aValue );
        }
        catch( const com::sun::star::uno::Exception& )
        {
            if ( !bUpdateThis )
                ImplLockPropertyChangeNotification( aPropertyName, false );
            throw;
        }
        if ( !bUpdateThis )
            ImplLockPropertyChangeNotification( aPropertyName, false );
    }
}

::com::sun::star::uno::Any UnoControlBase::ImplGetPropertyValue( const ::rtl::OUString& aPropertyName )
{
    ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >  xPSet( mxModel, ::com::sun::star::uno::UNO_QUERY );
    if ( xPSet.is() )
        return xPSet->getPropertyValue( aPropertyName );
    else
        return ::com::sun::star::uno::Any();
}

sal_Bool UnoControlBase::ImplGetPropertyValue_BOOL( sal_uInt16 nProp )
{
    sal_Bool b = sal_False;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= b;
    }
    return b;
}

sal_Int16 UnoControlBase::ImplGetPropertyValue_INT16( sal_uInt16 nProp )
{
    sal_Int16 n = 0;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= n;
    }
    return n;
}

sal_uInt16 UnoControlBase::ImplGetPropertyValue_UINT16( sal_uInt16 nProp )
{
    sal_uInt16 n = 0;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= n;
    }
    return n;
}

sal_Int32 UnoControlBase::ImplGetPropertyValue_INT32( sal_uInt16 nProp )
{
    sal_Int32 n = 0;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= n;
    }
    return n;
}

sal_uInt32 UnoControlBase::ImplGetPropertyValue_UINT32( sal_uInt16 nProp )
{
    sal_uInt32 n = 0;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= n;
    }
    return n;
}

double UnoControlBase::ImplGetPropertyValue_DOUBLE( sal_uInt16 nProp )
{
    double n = 0;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= n;
    }
    return n;
}

::rtl::OUString UnoControlBase::ImplGetPropertyValue_UString( sal_uInt16 nProp )
{
    ::rtl::OUString aStr;
    if ( mxModel.is() )
    {
        ::com::sun::star::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) );
        aVal >>= aStr;
    }
    return aStr;
}

::com::sun::star::awt::Size UnoControlBase::Impl_getMinimumSize()
{
    ::com::sun::star::awt::Size aSz;
    ::com::sun::star::uno::Reference< ::com::sun::star::awt::XWindowPeer >  xP = ImplGetCompatiblePeer( sal_True );
    DBG_ASSERT( xP.is(), "Layout: No Peer!" );
    if ( xP.is() )
    {
        ::com::sun::star::uno::Reference< ::com::sun::star::awt::XLayoutConstrains >  xL( xP, ::com::sun::star::uno::UNO_QUERY );
        if ( xL.is() )
            aSz = xL->getMinimumSize();

        if ( !getPeer().is() || ( getPeer() != xP ) )
            xP->dispose();
    }
    return aSz;
}

::com::sun::star::awt::Size UnoControlBase::Impl_getPreferredSize()
{
    ::com::sun::star::awt::Size aSz;
    ::com::sun::star::uno::Reference< ::com::sun::star::awt::XWindowPeer >  xP = ImplGetCompatiblePeer( sal_True );
    DBG_ASSERT( xP.is(), "Layout: No Peer!" );
    if ( xP.is() )
    {
        ::com::sun::star::uno::Reference< ::com::sun::star::awt::XLayoutConstrains >  xL( xP, ::com::sun::star::uno::UNO_QUERY );
        if ( xL.is() )
            aSz = xL->getPreferredSize();

        if ( !getPeer().is() || ( getPeer() != xP ) )
            xP->dispose();
    }
    return aSz;
}

::com::sun::star::awt::Size UnoControlBase::Impl_calcAdjustedSize( const ::com::sun::star::awt::Size& rNewSize )
{
    ::com::sun::star::awt::Size aSz;
    ::com::sun::star::uno::Reference< ::com::sun::star::awt::XWindowPeer >  xP = ImplGetCompatiblePeer( sal_True );
    DBG_ASSERT( xP.is(), "Layout: No Peer!" );
    if ( xP.is() )
    {
        ::com::sun::star::uno::Reference< ::com::sun::star::awt::XLayoutConstrains > xL( xP, ::com::sun::star::uno::UNO_QUERY );
        if ( xL.is() )
            aSz = xL->calcAdjustedSize( rNewSize );

        if ( !getPeer().is() || ( getPeer() != xP ) )
            xP->dispose();
    }
    return aSz;
}

::com::sun::star::awt::Size UnoControlBase::Impl_getMinimumSize( sal_Int16 nCols, sal_Int16 nLines )
{
    ::com::sun::star::awt::Size aSz;
    ::com::sun::star::uno::Reference< ::com::sun::star::awt::XWindowPeer >  xP = ImplGetCompatiblePeer( sal_True );
    DBG_ASSERT( xP.is(), "Layout: No Peer!" );
    if ( xP.is() )
    {
        ::com::sun::star::uno::Reference< ::com::sun::star::awt::XTextLayoutConstrains >  xL( xP, ::com::sun::star::uno::UNO_QUERY );
        if ( xL.is() )
            aSz = xL->getMinimumSize( nCols, nLines );

        if ( !getPeer().is() || ( getPeer() != xP ) )
            xP->dispose();
    }
    return aSz;
}

void UnoControlBase::Impl_getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines )
{
    ::com::sun::star::uno::Reference< ::com::sun::star::awt::XWindowPeer >  xP = ImplGetCompatiblePeer( sal_True );
    DBG_ASSERT( xP.is(), "Layout: No Peer!" );
    if ( xP.is() )
    {
        ::com::sun::star::uno::Reference< ::com::sun::star::awt::XTextLayoutConstrains >  xL( xP, ::com::sun::star::uno::UNO_QUERY );
        if ( xL.is() )
            xL->getColumnsAndLines( nCols, nLines );

        if ( !getPeer().is() || ( getPeer() != xP ) )
            xP->dispose();
    }
}

<|endoftext|>


<commit_before>/*
 * For licensing please refer to the LICENSE.md file
 */

#include "GmshReader.hpp"
#include "GmshReaderException.hpp"

#include <algorithm>
#include <boost/container/flat_set.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/numeric.hpp>
#include <chrono>
#include <iomanip>
#include <iostream>
#include <numeric>

#include <json/json.h>

namespace gmsh
{
Reader::Reader(std::string const& fileName, NodalOrdering ordering, IndexingBase base)
    : fileName(fileName),
      useZeroBasedIndexing(base == IndexingBase::Zero),
      useLocalNodalConnectivity(ordering == NodalOrdering::Local)
{
    gmshFile.precision(sizeof(double));
    fillMesh();
}

void Reader::fillMesh()
{
    auto const start = std::chrono::high_resolution_clock::now();

    gmshFile.open(fileName.c_str());

    if (!gmshFile.is_open())
    {
        throw GmshReaderException("Filename " + fileName + " is not valid");
    }

    std::string token, null;

    std::vector<std::string> physicalNames;

    // Loop around file and read in keyword tokens
    while (!gmshFile.eof())
    {
        gmshFile >> token;

        if (token == "$MeshFormat")
        {
            float gmshVersion; // File format version
            short dataType;    // Precision

            gmshFile >> gmshVersion >> dataType >> null;
            checkSupportedGmsh(gmshVersion);
        }
        else if (token == "$PhysicalNames")
        {
            std::string physicalName;
            int physicalIds = 0;
            gmshFile >> physicalIds;
            for (auto i = 0; i < physicalIds; ++i)
            {
                int dimension = 0, physicalId = 0;
                gmshFile >> dimension >> physicalId >> physicalName;

                // Extract the name from the quotes
                physicalName.erase(remove(physicalName.begin(), physicalName.end(), '\"'),
                                   physicalName.end());
                physicalGroupMap.emplace(physicalId, physicalName);
            }
            token.clear();
            gmshFile >> token;
        }
        else if (token == "$Nodes")
        {
            int nodeIds = 0;
            gmshFile >> nodeIds;
            nodeList.resize(nodeIds);

            for (auto& node : nodeList)
            {
                gmshFile >> node.id >> node.coordinates[0] >> node.coordinates[1] >>
                    node.coordinates[2];
            }
        }
        else if (token == "$Elements")
        {
            int elementIds = 0;
            gmshFile >> elementIds;

            for (int elementId = 0; elementId < elementIds; elementId++)
            {
                int id = 0, numberOfTags = 0, elementTypeId = 0;

                gmshFile >> id >> elementTypeId >> numberOfTags;

                auto const numberOfNodes = mapElementData(elementTypeId);

                std::vector<int> tags(numberOfTags, 0);
                std::vector<int> nodalConnectivity(numberOfNodes, 0);

                for (auto& tag : tags)
                {
                    gmshFile >> tag;
                }
                for (auto& nodeId : nodalConnectivity)
                {
                    gmshFile >> nodeId;
                }

                auto const physicalId = tags[0];

                ElementData elementData(nodalConnectivity, tags, elementTypeId, id);

                // Update the total number of partitions on the fly
                number_of_partitions = std::max(elementData.maxProcessId(), number_of_partitions);

                gmshMesh[physicalGroupMap[physicalId]].push_back(elementData);

                meshes[{physicalGroupMap[physicalId], elementTypeId}].push_back(elementData);

                if (elementData.isSharedByMultipleProcesses())
                {
                    for (int i = 4; i < tags[2] + 3; ++i)
                    {
                        auto const ownership = std::make_pair(tags[3], -tags[i]);

                        if (interfaceElementMap.find(ownership) != interfaceElementMap.end())
                        {
                            for (auto const& nodeId : elementData.nodalConnectivity())
                            {
                                interfaceElementMap[ownership].emplace(nodeId);
                            }
                        }
                        else
                        {
                            std::set<int> interfaceNodes(elementData.nodalConnectivity().begin(),
                                                         elementData.nodalConnectivity().end());

                            interfaceElementMap.emplace(ownership, interfaceNodes);
                        }
                    }
                }
            }
        }
    }
    gmshFile.close();

    auto const end = std::chrono::high_resolution_clock::now();

    std::chrono::duration<double> elapsed_seconds = end - start;
    std::cout << "Mesh data structure filled in " << elapsed_seconds.count() << "s\n";
}

int Reader::mapElementData(int const elementTypeId)
{
    // Return the number of local nodes per element
    switch (elementTypeId)
    {
        case LINE2: return 2; break;
        case TRIANGLE3: return 3; break;
        case QUADRILATERAL4: return 4; break;
        case TETRAHEDRON4: return 4; break;
        case HEXAHEDRON8: return 8; break;
        case PRISM6: return 6; break;
        case PYRAMID5: return 5; break;
        case LINE3: return 3; break;
        case TRIANGLE6: return 6; break;
        case QUADRILATERAL9: return 9; break;
        case TETRAHEDRON10: return 10; break;
        case HEXAHEDRON27: return 27; break;
        case PRISM18: return 18; break;
        case PYRAMID14: return 14; break;
        case POINT: return 1; break;
        case QUADRILATERAL8: return 8; break;
        case HEXAHEDRON20: return 20; break;
        case PRISM15: return 15; break;
        case PYRAMID13: return 13; break;
        case TRIANGLE9: return 19; break;
        case TRIANGLE10: return 10; break;
        case TRIANGLE12: return 12; break;
        case TRIANGLE15: return 15; break;
        case TRIANGLE15_IC: return 15; break;
        case TRIANGLE21: return 21; break;
        case EDGE4: return 4; break;
        case EDGE5: return 5; break;
        case EDGE6: return 6; break;
        case TETRAHEDRON20: return 20; break;
        case TETRAHEDRON35: return 35; break;
        case TETRAHEDRON56: return 56; break;
        case HEXAHEDRON64: return 64; break;
        case HEXAHEDRON125: return 125; break;
        default:
            throw GmshReaderException("The elementTypeId " + std::to_string(elementTypeId) +
                                      " is not implemented");
    }
    return -1;
}

void Reader::checkSupportedGmsh(float const gmshVersion)
{
    if (gmshVersion < 2.2)
    {
        throw std::runtime_error("GmshVersion " + std::to_string(gmshVersion) +
                                 " is not supported");
    }
}

void Reader::writeMeshToJson(bool const printIndices) const
{
    for (auto partition = 0; partition < number_of_partitions; ++partition)
    {
        std::map<StringKey, Value> localProcessMesh;

        Mesh process_mesh;

        for (auto const& mesh : gmshMesh)
        {
            for (auto const& element : mesh.second)
            {
                if (element.isOwnedByProcess(partition + 1))
                {
                    // Build a local copy of the elements to be offset later
                    localProcessMesh[mesh.first].push_back(element);
                }
            }
        }

        // Find all of the elements which belong to this process
        for (auto const& mesh : meshes)
        {
            auto const mesh_key = mesh.first;
            std::cout << "Searching " << mesh_key.first << std::endl;
            for (auto const& element : mesh.second)
            {
                if (element.isOwnedByProcess(partition + 1))
                {
                    // Copy the elements into the process mesh
                    process_mesh[mesh.first].push_back(element);
                }
            }
        }

        auto localToGlobalMapping = fillLocalToGlobalMap(localProcessMesh);
        auto local_global_mapping = fillLocalToGlobalMap(process_mesh);

        auto localNodes = fillLocalNodeList(localToGlobalMapping);

        if (useLocalNodalConnectivity)
        {
            reorderLocalMesh(localProcessMesh, localToGlobalMapping);
        }

        // Sort the elements based on the elementTypeId to output grouped
        // meshes for contiguous storage
        for (auto& mesh : localProcessMesh)
        {
            boost::sort(mesh.second,
                        [](auto const& a, auto const& b) { return a.typeId() < b.typeId(); });
        }

        // Check if this local mesh needs to be converted to zero based indexing
        // then correct the nodal connectivities, the mappings and the nodal and
        // element ids of the data structures
        if (useZeroBasedIndexing)
        {
            for (auto& l2g : localToGlobalMapping) --l2g;
            for (auto& localNode : localNodes) --localNode.id;
            for (auto& mesh : localProcessMesh)
            {
                for (auto& element : mesh.second)
                {
                    element.convertToZeroBasedIndexing();
                }
            }
        }

        writeInJsonFormat(localProcessMesh,
                          localToGlobalMapping,
                          localNodes,
                          partition,
                          number_of_partitions > 1,
                          printIndices);

        std::cout << std::string(2, ' ') << "Finished writing out JSON file for mesh partition "
                  << partition << "\n"
                  << std::flush;
    }
}

std::vector<int>
Reader::fillLocalToGlobalMap(std::map<StringKey, Value> const& localProcessMesh) const
{
    std::vector<int> localToGlobalMapping;

    for (auto const& mesh : localProcessMesh)
    {
        for (auto const& element : mesh.second)
        {
            boost::copy(element.nodalConnectivity(), std::back_inserter(localToGlobalMapping));
        }
    }

    // Sort and remove duplicates
    boost::sort(localToGlobalMapping);
    localToGlobalMapping.erase(std::unique(localToGlobalMapping.begin(),
                                           localToGlobalMapping.end()),
                               localToGlobalMapping.end());
    return localToGlobalMapping;
}

std::vector<int> fillLocalToGlobalMap(Mesh const& process_mesh) const
{
    std::vector<int> localToGlobalMapping;

    for (auto const& mesh : process_mesh)
    {
        for (auto const& element : mesh.second)
        {
            boost::copy(element.nodalConnectivity(), std::back_inserter(localToGlobalMapping));
        }
    }

    // Sort and remove duplicates
    boost::sort(localToGlobalMapping);
    localToGlobalMapping.erase(std::unique(localToGlobalMapping.begin(),
                                           localToGlobalMapping.end()),
                               localToGlobalMapping.end());
    return localToGlobalMapping;
}

void Reader::reorderLocalMesh(std::map<StringKey, Value>& localProcessMesh,
                              std::vector<int> const& localToGlobalMapping) const
{
    for (auto& mesh : localProcessMesh)
    {
        for (auto& element : mesh.second)
        {
            for (auto& node : element.nodalConnectivity())
            {
                auto found = boost::lower_bound(localToGlobalMapping, node);

                // Reset the node value to that inside the local ordering with
                // the default of one based ordering
                node = std::distance(localToGlobalMapping.begin(), found) + 1;
            }
        }
    }
}

std::vector<NodeData> Reader::fillLocalNodeList(std::vector<int> const& localToGlobalMapping) const
{
    std::vector<NodeData> localNodeList;
    for (auto const& map : localToGlobalMapping)
    {
        localNodeList.push_back(nodeList[map - 1]);
    }
    return localNodeList;
}

void Reader::writeInJsonFormat(std::map<StringKey, Value> const& localProcessMesh,
                               std::vector<int> const& localToGlobalMapping,
                               std::vector<NodeData> const& nodalCoordinates,
                               int const processId,
                               bool const isMeshDistributed,
                               bool const printIndices) const
{
    // Write out each file to Json format
    Json::Value event;

    std::string filename = fileName.substr(0, fileName.find_last_of(".")) + ".mesh";

    if (isMeshDistributed) filename += std::to_string(processId);

    std::fstream writer;
    writer.open(filename, std::ios::out);

    // Write out the nodal coordinates
    Json::Value nodeGroup;
    for (auto const& node : nodalCoordinates)
    {
        Json::Value coordinates(Json::arrayValue);
        for (auto const& xyz : node.coordinates)
        {
            coordinates.append(Json::Value(xyz));
        }
        nodeGroup["Coordinates"].append(coordinates);
        if (printIndices) nodeGroup["Indices"].append(node.id);
    }
    event["Nodes"].append(nodeGroup);

    for (auto const& mesh : localProcessMesh)
    {
        // Find all of the unique elementTypeIds
        std::set<int> elementTypeIds;
        for (auto const& element : mesh.second)
        {
            elementTypeIds.insert(element.typeId());
        }

        // Add in each of the element type groups into the json object
        for (auto const& elementTypeId : elementTypeIds)
        {
            Json::Value elementGroup;

            // Find the elements which are of the element type in the local mesh
            auto lower = boost::lower_bound(mesh.second, elementTypeId, [](auto a, auto b) {
                return a.typeId() < b;
            });
            auto upper = boost::upper_bound(mesh.second, elementTypeId, [](auto a, auto b) {
                return a < b.typeId();
            });

            std::for_each(lower, upper, [&](auto const& element) {
                Json::Value connectivity(Json::arrayValue);
                for (auto const& node : element.nodalConnectivity())
                {
                    connectivity.append(node);
                }
                elementGroup["NodalConnectivity"].append(connectivity);
                if (printIndices) elementGroup["Indices"].append(element.id());
            });

            elementGroup["Name"] = mesh.first;
            elementGroup["Type"] = elementTypeId;

            event["Elements"].append(elementGroup);
        }
    }

    if (isMeshDistributed)
    {
        for (auto const& l2g : localToGlobalMapping)
        {
            event["LocalToGlobalMap"].append(l2g);
        }

        int globalStartId = 0;

        for (auto const& interface : interfaceElementMap)
        {
            const auto masterId = interface.first.first;
            const auto slaveId  = interface.first.second;

            if (masterId < slaveId)
            {
                std::set<int> intersection;
                auto const& v1 = interface.second;
                auto const& v2 = interfaceElementMap.at(std::pair<int, int>(slaveId, masterId));

                std::set_intersection(v1.begin(),
                                      v1.end(),
                                      v2.begin(),
                                      v2.end(),
                                      std::inserter(intersection, intersection.begin()));

                if ((processId == masterId - 1 or processId == slaveId - 1))
                {
                    Json::Value interfaceGroup, nodeIds, globalIds;

                    for (auto const& nodeId : intersection)
                    {
                        nodeIds.append(nodeId);
                    }
                    interfaceGroup["Master"].append(interface.first.first);
                    interfaceGroup["Value"].append(processId == masterId - 1 ? 1 : -1);
                    interfaceGroup["Slave"].append(interface.first.second);
                    interfaceGroup["NodeIds"].append(nodeIds);
                    interfaceGroup["GlobalStartId"].append(globalStartId);

                    event["Interface"].append(interfaceGroup);
                }
                globalStartId += intersection.size();
            }
        }
        event["NumInterfaceNodes"].append(globalStartId);
    }
    Json::StyledWriter jsonwriter;
    writer << jsonwriter.write(event);
    writer.close();
}
}
<commit_msg>Fixed typo<commit_after>/*
 * For licensing please refer to the LICENSE.md file
 */

#include "GmshReader.hpp"
#include "GmshReaderException.hpp"

#include <algorithm>
#include <boost/container/flat_set.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/numeric.hpp>
#include <chrono>
#include <iomanip>
#include <iostream>
#include <numeric>

#include <json/json.h>

namespace gmsh
{
Reader::Reader(std::string const& fileName, NodalOrdering ordering, IndexingBase base)
    : fileName(fileName),
      useZeroBasedIndexing(base == IndexingBase::Zero),
      useLocalNodalConnectivity(ordering == NodalOrdering::Local)
{
    gmshFile.precision(sizeof(double));
    fillMesh();
}

void Reader::fillMesh()
{
    auto const start = std::chrono::high_resolution_clock::now();

    gmshFile.open(fileName.c_str());

    if (!gmshFile.is_open())
    {
        throw GmshReaderException("Filename " + fileName + " is not valid");
    }

    std::string token, null;

    std::vector<std::string> physicalNames;

    // Loop around file and read in keyword tokens
    while (!gmshFile.eof())
    {
        gmshFile >> token;

        if (token == "$MeshFormat")
        {
            float gmshVersion; // File format version
            short dataType;    // Precision

            gmshFile >> gmshVersion >> dataType >> null;
            checkSupportedGmsh(gmshVersion);
        }
        else if (token == "$PhysicalNames")
        {
            std::string physicalName;
            int physicalIds = 0;
            gmshFile >> physicalIds;
            for (auto i = 0; i < physicalIds; ++i)
            {
                int dimension = 0, physicalId = 0;
                gmshFile >> dimension >> physicalId >> physicalName;

                // Extract the name from the quotes
                physicalName.erase(remove(physicalName.begin(), physicalName.end(), '\"'),
                                   physicalName.end());
                physicalGroupMap.emplace(physicalId, physicalName);
            }
            token.clear();
            gmshFile >> token;
        }
        else if (token == "$Nodes")
        {
            int nodeIds = 0;
            gmshFile >> nodeIds;
            nodeList.resize(nodeIds);

            for (auto& node : nodeList)
            {
                gmshFile >> node.id >> node.coordinates[0] >> node.coordinates[1] >>
                    node.coordinates[2];
            }
        }
        else if (token == "$Elements")
        {
            int elementIds = 0;
            gmshFile >> elementIds;

            for (int elementId = 0; elementId < elementIds; elementId++)
            {
                int id = 0, numberOfTags = 0, elementTypeId = 0;

                gmshFile >> id >> elementTypeId >> numberOfTags;

                auto const numberOfNodes = mapElementData(elementTypeId);

                std::vector<int> tags(numberOfTags, 0);
                std::vector<int> nodalConnectivity(numberOfNodes, 0);

                for (auto& tag : tags)
                {
                    gmshFile >> tag;
                }
                for (auto& nodeId : nodalConnectivity)
                {
                    gmshFile >> nodeId;
                }

                auto const physicalId = tags[0];

                ElementData elementData(nodalConnectivity, tags, elementTypeId, id);

                // Update the total number of partitions on the fly
                number_of_partitions = std::max(elementData.maxProcessId(), number_of_partitions);

                gmshMesh[physicalGroupMap[physicalId]].push_back(elementData);

                meshes[{physicalGroupMap[physicalId], elementTypeId}].push_back(elementData);

                if (elementData.isSharedByMultipleProcesses())
                {
                    for (int i = 4; i < tags[2] + 3; ++i)
                    {
                        auto const ownership = std::make_pair(tags[3], -tags[i]);

                        if (interfaceElementMap.find(ownership) != interfaceElementMap.end())
                        {
                            for (auto const& nodeId : elementData.nodalConnectivity())
                            {
                                interfaceElementMap[ownership].emplace(nodeId);
                            }
                        }
                        else
                        {
                            std::set<int> interfaceNodes(elementData.nodalConnectivity().begin(),
                                                         elementData.nodalConnectivity().end());

                            interfaceElementMap.emplace(ownership, interfaceNodes);
                        }
                    }
                }
            }
        }
    }
    gmshFile.close();

    auto const end = std::chrono::high_resolution_clock::now();

    std::chrono::duration<double> elapsed_seconds = end - start;
    std::cout << "Mesh data structure filled in " << elapsed_seconds.count() << "s\n";
}

int Reader::mapElementData(int const elementTypeId)
{
    // Return the number of local nodes per element
    switch (elementTypeId)
    {
        case LINE2: return 2; break;
        case TRIANGLE3: return 3; break;
        case QUADRILATERAL4: return 4; break;
        case TETRAHEDRON4: return 4; break;
        case HEXAHEDRON8: return 8; break;
        case PRISM6: return 6; break;
        case PYRAMID5: return 5; break;
        case LINE3: return 3; break;
        case TRIANGLE6: return 6; break;
        case QUADRILATERAL9: return 9; break;
        case TETRAHEDRON10: return 10; break;
        case HEXAHEDRON27: return 27; break;
        case PRISM18: return 18; break;
        case PYRAMID14: return 14; break;
        case POINT: return 1; break;
        case QUADRILATERAL8: return 8; break;
        case HEXAHEDRON20: return 20; break;
        case PRISM15: return 15; break;
        case PYRAMID13: return 13; break;
        case TRIANGLE9: return 19; break;
        case TRIANGLE10: return 10; break;
        case TRIANGLE12: return 12; break;
        case TRIANGLE15: return 15; break;
        case TRIANGLE15_IC: return 15; break;
        case TRIANGLE21: return 21; break;
        case EDGE4: return 4; break;
        case EDGE5: return 5; break;
        case EDGE6: return 6; break;
        case TETRAHEDRON20: return 20; break;
        case TETRAHEDRON35: return 35; break;
        case TETRAHEDRON56: return 56; break;
        case HEXAHEDRON64: return 64; break;
        case HEXAHEDRON125: return 125; break;
        default:
            throw GmshReaderException("The elementTypeId " + std::to_string(elementTypeId) +
                                      " is not implemented");
    }
    return -1;
}

void Reader::checkSupportedGmsh(float const gmshVersion)
{
    if (gmshVersion < 2.2)
    {
        throw std::runtime_error("GmshVersion " + std::to_string(gmshVersion) +
                                 " is not supported");
    }
}

void Reader::writeMeshToJson(bool const printIndices) const
{
    for (auto partition = 0; partition < number_of_partitions; ++partition)
    {
        std::map<StringKey, Value> localProcessMesh;

        Mesh process_mesh;

        for (auto const& mesh : gmshMesh)
        {
            for (auto const& element : mesh.second)
            {
                if (element.isOwnedByProcess(partition + 1))
                {
                    // Build a local copy of the elements to be offset later
                    localProcessMesh[mesh.first].push_back(element);
                }
            }
        }

        // Find all of the elements which belong to this process
        for (auto const& mesh : meshes)
        {
            auto const mesh_key = mesh.first;
            std::cout << "Searching " << mesh_key.first << std::endl;
            for (auto const& element : mesh.second)
            {
                if (element.isOwnedByProcess(partition + 1))
                {
                    // Copy the elements into the process mesh
                    process_mesh[mesh.first].push_back(element);
                }
            }
        }

        auto localToGlobalMapping = fillLocalToGlobalMap(localProcessMesh);
        auto local_global_mapping = fillLocalToGlobalMap(process_mesh);

        auto localNodes = fillLocalNodeList(localToGlobalMapping);

        if (useLocalNodalConnectivity)
        {
            reorderLocalMesh(localProcessMesh, localToGlobalMapping);
        }

        // Sort the elements based on the elementTypeId to output grouped
        // meshes for contiguous storage
        for (auto& mesh : localProcessMesh)
        {
            boost::sort(mesh.second,
                        [](auto const& a, auto const& b) { return a.typeId() < b.typeId(); });
        }

        // Check if this local mesh needs to be converted to zero based indexing
        // then correct the nodal connectivities, the mappings and the nodal and
        // element ids of the data structures
        if (useZeroBasedIndexing)
        {
            for (auto& l2g : localToGlobalMapping) --l2g;
            for (auto& localNode : localNodes) --localNode.id;
            for (auto& mesh : localProcessMesh)
            {
                for (auto& element : mesh.second)
                {
                    element.convertToZeroBasedIndexing();
                }
            }
        }

        writeInJsonFormat(localProcessMesh,
                          localToGlobalMapping,
                          localNodes,
                          partition,
                          number_of_partitions > 1,
                          printIndices);

        std::cout << std::string(2, ' ') << "Finished writing out JSON file for mesh partition "
                  << partition << "\n"
                  << std::flush;
    }
}

std::vector<int>
Reader::fillLocalToGlobalMap(std::map<StringKey, Value> const& localProcessMesh) const
{
    std::vector<int> localToGlobalMapping;

    for (auto const& mesh : localProcessMesh)
    {
        for (auto const& element : mesh.second)
        {
            boost::copy(element.nodalConnectivity(), std::back_inserter(localToGlobalMapping));
        }
    }

    // Sort and remove duplicates
    boost::sort(localToGlobalMapping);
    localToGlobalMapping.erase(std::unique(localToGlobalMapping.begin(),
                                           localToGlobalMapping.end()),
                               localToGlobalMapping.end());
    return localToGlobalMapping;
}

std::vector<int> Reader::fillLocalToGlobalMap(Mesh const& process_mesh) const
{
    std::vector<int> localToGlobalMapping;

    for (auto const& mesh : process_mesh)
    {
        for (auto const& element : mesh.second)
        {
            boost::copy(element.nodalConnectivity(), std::back_inserter(localToGlobalMapping));
        }
    }

    // Sort and remove duplicates
    boost::sort(localToGlobalMapping);
    localToGlobalMapping.erase(std::unique(localToGlobalMapping.begin(),
                                           localToGlobalMapping.end()),
                               localToGlobalMapping.end());
    return localToGlobalMapping;
}

void Reader::reorderLocalMesh(std::map<StringKey, Value>& localProcessMesh,
                              std::vector<int> const& localToGlobalMapping) const
{
    for (auto& mesh : localProcessMesh)
    {
        for (auto& element : mesh.second)
        {
            for (auto& node : element.nodalConnectivity())
            {
                auto found = boost::lower_bound(localToGlobalMapping, node);

                // Reset the node value to that inside the local ordering with
                // the default of one based ordering
                node = std::distance(localToGlobalMapping.begin(), found) + 1;
            }
        }
    }
}

std::vector<NodeData> Reader::fillLocalNodeList(std::vector<int> const& localToGlobalMapping) const
{
    std::vector<NodeData> localNodeList;
    for (auto const& map : localToGlobalMapping)
    {
        localNodeList.push_back(nodeList[map - 1]);
    }
    return localNodeList;
}

void Reader::writeInJsonFormat(std::map<StringKey, Value> const& localProcessMesh,
                               std::vector<int> const& localToGlobalMapping,
                               std::vector<NodeData> const& nodalCoordinates,
                               int const processId,
                               bool const isMeshDistributed,
                               bool const printIndices) const
{
    // Write out each file to Json format
    Json::Value event;

    std::string filename = fileName.substr(0, fileName.find_last_of(".")) + ".mesh";

    if (isMeshDistributed) filename += std::to_string(processId);

    std::fstream writer;
    writer.open(filename, std::ios::out);

    // Write out the nodal coordinates
    Json::Value nodeGroup;
    for (auto const& node : nodalCoordinates)
    {
        Json::Value coordinates(Json::arrayValue);
        for (auto const& xyz : node.coordinates)
        {
            coordinates.append(Json::Value(xyz));
        }
        nodeGroup["Coordinates"].append(coordinates);
        if (printIndices) nodeGroup["Indices"].append(node.id);
    }
    event["Nodes"].append(nodeGroup);

    for (auto const& mesh : localProcessMesh)
    {
        // Find all of the unique elementTypeIds
        std::set<int> elementTypeIds;
        for (auto const& element : mesh.second)
        {
            elementTypeIds.insert(element.typeId());
        }

        // Add in each of the element type groups into the json object
        for (auto const& elementTypeId : elementTypeIds)
        {
            Json::Value elementGroup;

            // Find the elements which are of the element type in the local mesh
            auto lower = boost::lower_bound(mesh.second, elementTypeId, [](auto a, auto b) {
                return a.typeId() < b;
            });
            auto upper = boost::upper_bound(mesh.second, elementTypeId, [](auto a, auto b) {
                return a < b.typeId();
            });

            std::for_each(lower, upper, [&](auto const& element) {
                Json::Value connectivity(Json::arrayValue);
                for (auto const& node : element.nodalConnectivity())
                {
                    connectivity.append(node);
                }
                elementGroup["NodalConnectivity"].append(connectivity);
                if (printIndices) elementGroup["Indices"].append(element.id());
            });

            elementGroup["Name"] = mesh.first;
            elementGroup["Type"] = elementTypeId;

            event["Elements"].append(elementGroup);
        }
    }

    if (isMeshDistributed)
    {
        for (auto const& l2g : localToGlobalMapping)
        {
            event["LocalToGlobalMap"].append(l2g);
        }

        int globalStartId = 0;

        for (auto const& interface : interfaceElementMap)
        {
            const auto masterId = interface.first.first;
            const auto slaveId  = interface.first.second;

            if (masterId < slaveId)
            {
                std::set<int> intersection;
                auto const& v1 = interface.second;
                auto const& v2 = interfaceElementMap.at(std::pair<int, int>(slaveId, masterId));

                std::set_intersection(v1.begin(),
                                      v1.end(),
                                      v2.begin(),
                                      v2.end(),
                                      std::inserter(intersection, intersection.begin()));

                if ((processId == masterId - 1 or processId == slaveId - 1))
                {
                    Json::Value interfaceGroup, nodeIds, globalIds;

                    for (auto const& nodeId : intersection)
                    {
                        nodeIds.append(nodeId);
                    }
                    interfaceGroup["Master"].append(interface.first.first);
                    interfaceGroup["Value"].append(processId == masterId - 1 ? 1 : -1);
                    interfaceGroup["Slave"].append(interface.first.second);
                    interfaceGroup["NodeIds"].append(nodeIds);
                    interfaceGroup["GlobalStartId"].append(globalStartId);

                    event["Interface"].append(interfaceGroup);
                }
                globalStartId += intersection.size();
            }
        }
        event["NumInterfaceNodes"].append(globalStartId);
    }
    Json::StyledWriter jsonwriter;
    writer << jsonwriter.write(event);
    writer.close();
}
}
<|endoftext|>