<commit_before>#include "tile.hpp"
#include "font_face_set.hpp"
#include "harfbuzz_shaper.hpp"

// node
#include <node_buffer.h>

#include <set>
#include <algorithm>
#include <memory>

// freetype2
extern "C"
{
#include <ft2build.h>
#include FT_FREETYPE_H
// #include FT_STROKER_H
}

struct ShapeBaton {
    v8::Persistent<v8::Function> callback;
    Tile *tile;
    std::string fontstack;
};

v8::Persistent<v8::FunctionTemplate> Tile::constructor;

Tile::Tile(const char *data, size_t length) : node::ObjectWrap() {
    tile.ParseFromArray(data, length);
}

Tile::~Tile() {}

void Tile::Init(v8::Handle<v8::Object> target) {
    v8::HandleScope scope;

    v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(New);
    v8::Local<v8::String> name = v8::String::NewSymbol("Tile");

    constructor = v8::Persistent<v8::FunctionTemplate>::New(tpl);

    // node::ObjectWrap uses the first internal field to store the wrapped pointer.
    constructor->InstanceTemplate()->SetInternalFieldCount(1);
    constructor->SetClassName(name);

    // Add all prototype methods, getters and setters here.
    constructor->PrototypeTemplate()->SetAccessor(v8::String::NewSymbol("length"), Length);
    NODE_SET_PROTOTYPE_METHOD(constructor, "serialize", Serialize);
    NODE_SET_PROTOTYPE_METHOD(constructor, "shape", Shape);

    // This has to be last, otherwise the properties won't show up on the
    // object in JavaScript.
    target->Set(name, constructor->GetFunction());
}

v8::Handle<v8::Value> Tile::New(const v8::Arguments& args) {
    if (!args.IsConstructCall()) {
        return ThrowException(v8::Exception::TypeError(v8::String::New("Constructor must be called with new keyword")));
    }
    if (args.Length() < 1 || !node::Buffer::HasInstance(args[0])) {
        return ThrowException(v8::Exception::TypeError(v8::String::New("First argument must be a buffer")));
    }

    v8::Local<v8::Object> buffer = args[0]->ToObject();

    Tile* tile = new Tile(node::Buffer::Data(buffer), node::Buffer::Length(buffer));
    tile->Wrap(args.This());

    return args.This();
}

bool Tile::HasInstance(v8::Handle<v8::Value> val) {
    if (!val->IsObject()) return false;
    return constructor->HasInstance(val->ToObject());
}

v8::Handle<v8::Value> Tile::Length(v8::Local<v8::String> property, const v8::AccessorInfo &info) {
    v8::HandleScope scope;
    Tile* tile = node::ObjectWrap::Unwrap<Tile>(info.This());
    v8::Local<v8::Number> length = v8::Uint32::New(tile->tile.layers_size());
    return scope.Close(length);
}

v8::Handle<v8::Value> Tile::Serialize(const v8::Arguments& args) {
    v8::HandleScope scope;
    llmr::vector::tile& tile = node::ObjectWrap::Unwrap<Tile>(args.This())->tile;
    std::string serialized = tile.SerializeAsString();
    return scope.Close(node::Buffer::New(serialized.data(), serialized.length())->handle_);
}

v8::Handle<v8::Value> Tile::Shape(const v8::Arguments& args) {
    v8::HandleScope scope;

    if (args.Length() < 1) {
        return ThrowException(v8::Exception::TypeError(
            v8::String::New("First argument must be a font stack")));
    }

    if (args.Length() < 2 || !args[1]->IsFunction()) {
        return ThrowException(v8::Exception::TypeError(
            v8::String::New("Second argument must be a callback function")));
    }
    v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(args[1]);
    // TODO: validate this is a string
    v8::String::Utf8Value fontstack(args[0]->ToString());

    Tile *tile = node::ObjectWrap::Unwrap<Tile>(args.This());

    ShapeBaton* baton = new ShapeBaton();
    baton->callback = v8::Persistent<v8::Function>::New(callback);
    baton->tile = tile;
    baton->fontstack = *fontstack;

    uv_work_t *req = new uv_work_t();
    req->data = baton;

    int status = uv_queue_work(uv_default_loop(), req, AsyncShape, (uv_after_work_cb)ShapeAfter);
    assert(status == 0);

    return v8::Undefined();
}

void Tile::AsyncShape(uv_work_t* req) {
    ShapeBaton* baton = static_cast<ShapeBaton*>(req->data);
    // Maps char index (UTF-16) to width. If multiple glyphs map to the
    // same char the sum of all widths is used.
    // Note: this probably isn't the best solution. it would be better
    // to have an object for each cluster, but it needs to be
    // implemented with no overhead.
    std::map<unsigned, double> width_map_;
    fontserver::freetype_engine font_engine_;
    fontserver::face_manager_freetype font_manager(font_engine_);
    fontserver::text_itemizer itemizer;

    fontserver::font_set fset(baton->fontstack);
    fset.add_fontstack(baton->fontstack, ',');

    fontserver::face_set_ptr face_set = font_manager.get_face_set(fset);
    // std::cout << "FONTSTACK " << baton->fontstack << ' ' << face_set->size() << '\n';
    if (!face_set->size()) return;

    llmr::vector::tile& tile = baton->tile->tile;

    // for every label
    for (int i = 0; i < tile.layers_size(); i++) {
        const llmr::vector::layer& layer = tile.layers(i);

        typedef std::set<int> Strings;
        Strings strings;

        // Compile a set of all strings we need to shape.
        for (int j = 0; j < layer.features_size(); j++) {
            const llmr::vector::feature& feature = layer.features(j);

            for (int k = 1; k < feature.tags_size(); k += 2) {
                const std::string& key = layer.keys(feature.tags(k - 1));
                if (key == "name") {
                    // TODO: handle multiple fonts stacks
                    strings.insert(feature.tags(k));
                }
                // TODO: extract all keys we need to shape
            }
        }

        llmr::vector::layer* mutable_layer = tile.mutable_layers(i);
        fontserver::face_set_ptr layer_faces = font_manager.get_face_set();

        // Process strings per layer.
        for (auto const& key : strings) {
            const llmr::vector::value& value = layer.values(key);
            std::string text;
            if (value.has_string_value()) {
                text = value.string_value();
            }

            if (!text.empty()) {
                // Clear cluster widths.
                width_map_.clear();

                UnicodeString const& str = text.data();

                fontserver::text_line line(0, str.length() - 1);

                fontserver::text_format format(baton->fontstack, 24);
                fontserver::text_format_ptr format_ptr =
                    std::make_shared<fontserver::text_format>(format);

                itemizer.add_text(str, format_ptr);

                const double scale_factor = 1.0;

                // Shape the text.
                fontserver::harfbuzz_shaper shaper;
                shaper.shape_text(line,
                                  itemizer,
                                  width_map_,
                                  font_manager,
                                  scale_factor);

                llmr::vector::label *label = mutable_layer->add_labels();
                label->set_text(key);

                // TODO: support multiple font stacks
                label->set_stack(0);

                // Add all glyphs for this labels and add new font
                // faces as they appear.
                for (auto const& glyph : line) {
                    if (!glyph.face) {
                        std::cout << text << ' ' <<
                            line.size() << " glyphs\n" <<
                            " codepoint: " << glyph.glyph_index <<
                            " char_index: " << glyph.char_index <<
                            " face_ptr: " << glyph.face <<
                            '\n';
                        continue;
                    }

                    // fontserver::face_ptr const& face = std::make_shared<fontserver::font_face>(*glyph.face);

                    // Try to find whether this font has already been
                    // used in this tile.
                    fontserver::font_face_set::iterator face_itr = std::find(face_set->begin(), face_set->end(), glyph.face);
                    if (face_itr == face_set->end()) {
                        face_set->add(glyph.face);
                        std::cout << "Face not found: " <<
                            glyph.face->family_name() << ' ' <<
                            glyph.face->style_name() << '\n';
                    }

                    // Find out whether this font has been used in
                    // this tile before and get its position.
                    fontserver::font_face_set::iterator layer_itr = std::find(layer_faces->begin(), layer_faces->end(), glyph.face);
                    if (layer_itr == layer_faces->end()) {
                        layer_faces->add(glyph.face);
                        layer_itr = layer_faces->end() - 1;
                    }

                    int layer_face_id = layer_itr - layer_faces->begin();

                    /*
                    std::cout << " face: " << layer_face_id <<
                        " glyph: " << glyph.glyph_index <<
                        " x: " << width_map_[glyph.char_index] <<
                        " y: " << glyph.offset.y <<
                        '\n';
                    */

                    label->add_faces(layer_face_id);
                    label->add_glyphs(glyph.glyph_index);
                    label->add_x(width_map_[glyph.char_index]);
                    label->add_y(glyph.offset.y);
                }

                itemizer.clear();

                /*
                std::cout << "faces: " << label->faces_size() <<
                    " glyphs: " << label->glyphs_size() <<
                    " x: " << label->x_size() <<
                    " y: " << label->y_size() <<
                    " labels: " << mutable_layer->labels_size() <<
                    '\n';
                */
            }
        }

        // Add a textual representation of the font so that we can figure out
        // later what font we need to use.
        for (auto const& face : *layer_faces) {
            std::string name = face->family_name() + " " + face->style_name();
            mutable_layer->add_faces(name);
            // We don't delete the TileFace objects here because
            // they are 'owned' by the global faces map and deleted
            // later on.
        }

        // Insert FAKE stacks
        mutable_layer->add_stacks(baton->fontstack);

        // std::cout << " layer_faces: " << mutable_layer->faces_size() << '\n';
    }

    // Insert SDF glyphs + bitmaps
    for (auto const& face : *face_set) {
        llmr::vector::face *mutable_face = tile.add_faces();
        mutable_face->set_family(face->family_name());
        mutable_face->set_style(face->style_name());

        // Determine ASCII glyphs
        // std::set<uint32_t> omit;
        // FT_UInt glyph_index;
        // FT_ULong char_code = FT_Get_First_Char(ft_face, &glyph_index);
        // while (glyph_index != 0 && char_code < 256) {
        //     omit.insert(glyph_index);
        //     char_code = FT_Get_Next_Char(ft_face, char_code, &glyph_index);
        // }

        for (auto const& glyph : *face) {
            // Omit ASCII glyphs we determined earlier
            // if (omit.find(id) != omit.end()) {
            //     continue;
            // }

            /*
            std::cout << " Glyph: " << glyph.second.glyph_index <<
                " width: " << glyph.second.width <<
                " height: " << glyph.second.height() <<
                " left: " << glyph.second.left <<
                " top: " << glyph.second.top <<
                " advance: " << glyph.second.advance <<
                '\n';
            */

            llmr::vector::glyph *mutable_glyph = mutable_face->add_glyphs();
            mutable_glyph->set_id(glyph.second.glyph_index);
            mutable_glyph->set_width(glyph.second.width);
            mutable_glyph->set_height(glyph.second.height);
            mutable_glyph->set_left(glyph.second.left);
            mutable_glyph->set_top(glyph.second.top);
            mutable_glyph->set_advance(glyph.second.advance);
            if (glyph.second.width > 0) {
                mutable_glyph->set_bitmap(glyph.second.bitmap);
            }
        }
    }
}

void Tile::ShapeAfter(uv_work_t* req) {
    v8::HandleScope scope;
    ShapeBaton* baton = static_cast<ShapeBaton*>(req->data);

    const unsigned argc = 1;
    v8::Local<v8::Value> argv[argc] = { v8::Local<v8::Value>::New(v8::Null()) };

    v8::TryCatch try_catch;
    baton->callback->Call(v8::Context::GetCurrent()->Global(), argc, argv);
    if (try_catch.HasCaught()) {
        node::FatalException(try_catch);
    }

    baton->callback.Dispose();

    delete baton;
    delete req;
}
<commit_msg>create format outside of string loop<commit_after>#include "tile.hpp"
#include "font_face_set.hpp"
#include "harfbuzz_shaper.hpp"

// node
#include <node_buffer.h>

#include <set>
#include <algorithm>
#include <memory>

// freetype2
extern "C"
{
#include <ft2build.h>
#include FT_FREETYPE_H
// #include FT_STROKER_H
}

struct ShapeBaton {
    v8::Persistent<v8::Function> callback;
    Tile *tile;
    std::string fontstack;
};

v8::Persistent<v8::FunctionTemplate> Tile::constructor;

Tile::Tile(const char *data, size_t length) : node::ObjectWrap() {
    tile.ParseFromArray(data, length);
}

Tile::~Tile() {}

void Tile::Init(v8::Handle<v8::Object> target) {
    v8::HandleScope scope;

    v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(New);
    v8::Local<v8::String> name = v8::String::NewSymbol("Tile");

    constructor = v8::Persistent<v8::FunctionTemplate>::New(tpl);

    // node::ObjectWrap uses the first internal field to store the wrapped pointer.
    constructor->InstanceTemplate()->SetInternalFieldCount(1);
    constructor->SetClassName(name);

    // Add all prototype methods, getters and setters here.
    constructor->PrototypeTemplate()->SetAccessor(v8::String::NewSymbol("length"), Length);
    NODE_SET_PROTOTYPE_METHOD(constructor, "serialize", Serialize);
    NODE_SET_PROTOTYPE_METHOD(constructor, "shape", Shape);

    // This has to be last, otherwise the properties won't show up on the
    // object in JavaScript.
    target->Set(name, constructor->GetFunction());
}

v8::Handle<v8::Value> Tile::New(const v8::Arguments& args) {
    if (!args.IsConstructCall()) {
        return ThrowException(v8::Exception::TypeError(v8::String::New("Constructor must be called with new keyword")));
    }
    if (args.Length() < 1 || !node::Buffer::HasInstance(args[0])) {
        return ThrowException(v8::Exception::TypeError(v8::String::New("First argument must be a buffer")));
    }

    v8::Local<v8::Object> buffer = args[0]->ToObject();

    Tile* tile = new Tile(node::Buffer::Data(buffer), node::Buffer::Length(buffer));
    tile->Wrap(args.This());

    return args.This();
}

bool Tile::HasInstance(v8::Handle<v8::Value> val) {
    if (!val->IsObject()) return false;
    return constructor->HasInstance(val->ToObject());
}

v8::Handle<v8::Value> Tile::Length(v8::Local<v8::String> property, const v8::AccessorInfo &info) {
    v8::HandleScope scope;
    Tile* tile = node::ObjectWrap::Unwrap<Tile>(info.This());
    v8::Local<v8::Number> length = v8::Uint32::New(tile->tile.layers_size());
    return scope.Close(length);
}

v8::Handle<v8::Value> Tile::Serialize(const v8::Arguments& args) {
    v8::HandleScope scope;
    llmr::vector::tile& tile = node::ObjectWrap::Unwrap<Tile>(args.This())->tile;
    std::string serialized = tile.SerializeAsString();
    return scope.Close(node::Buffer::New(serialized.data(), serialized.length())->handle_);
}

v8::Handle<v8::Value> Tile::Shape(const v8::Arguments& args) {
    v8::HandleScope scope;

    if (args.Length() < 1) {
        return ThrowException(v8::Exception::TypeError(
            v8::String::New("First argument must be a font stack")));
    }

    if (args.Length() < 2 || !args[1]->IsFunction()) {
        return ThrowException(v8::Exception::TypeError(
            v8::String::New("Second argument must be a callback function")));
    }
    v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(args[1]);
    // TODO: validate this is a string
    v8::String::Utf8Value fontstack(args[0]->ToString());

    Tile *tile = node::ObjectWrap::Unwrap<Tile>(args.This());

    ShapeBaton* baton = new ShapeBaton();
    baton->callback = v8::Persistent<v8::Function>::New(callback);
    baton->tile = tile;
    baton->fontstack = *fontstack;

    uv_work_t *req = new uv_work_t();
    req->data = baton;

    int status = uv_queue_work(uv_default_loop(), req, AsyncShape, (uv_after_work_cb)ShapeAfter);
    assert(status == 0);

    return v8::Undefined();
}

void Tile::AsyncShape(uv_work_t* req) {
    ShapeBaton* baton = static_cast<ShapeBaton*>(req->data);
    // Maps char index (UTF-16) to width. If multiple glyphs map to the
    // same char the sum of all widths is used.
    // Note: this probably isn't the best solution. it would be better
    // to have an object for each cluster, but it needs to be
    // implemented with no overhead.
    std::map<unsigned, double> width_map_;
    fontserver::freetype_engine font_engine_;
    fontserver::face_manager_freetype font_manager(font_engine_);
    fontserver::text_itemizer itemizer;

    fontserver::font_set fset(baton->fontstack);
    fset.add_fontstack(baton->fontstack, ',');

    fontserver::face_set_ptr face_set = font_manager.get_face_set(fset);
    if (!face_set->size()) return;

    llmr::vector::tile& tile = baton->tile->tile;

    // for every label
    for (int i = 0; i < tile.layers_size(); i++) {
        const llmr::vector::layer& layer = tile.layers(i);

        typedef std::set<int> Strings;
        Strings strings;

        // Compile a set of all strings we need to shape.
        for (int j = 0; j < layer.features_size(); j++) {
            const llmr::vector::feature& feature = layer.features(j);

            for (int k = 1; k < feature.tags_size(); k += 2) {
                const std::string& key = layer.keys(feature.tags(k - 1));
                if (key == "name") {
                    // TODO: handle multiple fonts stacks
                    strings.insert(feature.tags(k));
                }
                // TODO: extract all keys we need to shape
            }
        }

        llmr::vector::layer* mutable_layer = tile.mutable_layers(i);
        fontserver::face_set_ptr layer_faces = font_manager.get_face_set();

        fontserver::text_format format(baton->fontstack, 24);
        fontserver::text_format_ptr format_ptr =
            std::make_shared<fontserver::text_format>(format);

        // Process strings per layer.
        for (auto const& key : strings) {
            const llmr::vector::value& value = layer.values(key);
            std::string text;
            if (value.has_string_value()) {
                text = value.string_value();
            }

            if (!text.empty()) {
                // Clear cluster widths.
                width_map_.clear();

                UnicodeString const& str = text.data();

                fontserver::text_line line(0, str.length() - 1);

                itemizer.add_text(str, format_ptr);

                const double scale_factor = 1.0;

                // Shape the text.
                fontserver::harfbuzz_shaper shaper;
                shaper.shape_text(line,
                                  itemizer,
                                  width_map_,
                                  font_manager,
                                  scale_factor);

                llmr::vector::label *label = mutable_layer->add_labels();
                label->set_text(key);

                // TODO: support multiple font stacks
                label->set_stack(0);

                // Add all glyphs for this labels and add new font
                // faces as they appear.
                for (auto const& glyph : line) {
                    if (!glyph.face) {
                        std::cout << text << ' ' <<
                            line.size() << " glyphs\n" <<
                            " codepoint: " << glyph.glyph_index <<
                            " char_index: " << glyph.char_index <<
                            " face_ptr: " << glyph.face <<
                            '\n';
                        continue;
                    }

                    // fontserver::face_ptr const& face = std::make_shared<fontserver::font_face>(*glyph.face);

                    // Try to find whether this font has already been
                    // used in this tile.
                    fontserver::font_face_set::iterator face_itr = std::find(face_set->begin(), face_set->end(), glyph.face);
                    if (face_itr == face_set->end()) {
                        face_set->add(glyph.face);
                        std::cout << "Face not found: " <<
                            glyph.face->family_name() << ' ' <<
                            glyph.face->style_name() << '\n';
                    }

                    // Find out whether this font has been used in
                    // this tile before and get its position.
                    fontserver::font_face_set::iterator layer_itr = std::find(layer_faces->begin(), layer_faces->end(), glyph.face);
                    if (layer_itr == layer_faces->end()) {
                        layer_faces->add(glyph.face);
                        layer_itr = layer_faces->end() - 1;
                    }

                    int layer_face_id = layer_itr - layer_faces->begin();

                    /*
                    std::cout << " face: " << layer_face_id <<
                        " glyph: " << glyph.glyph_index <<
                        " x: " << width_map_[glyph.char_index] <<
                        " y: " << glyph.offset.y <<
                        '\n';
                    */

                    label->add_faces(layer_face_id);
                    label->add_glyphs(glyph.glyph_index);
                    label->add_x(width_map_[glyph.char_index]);
                    label->add_y(glyph.offset.y);
                }

                itemizer.clear();

                /*
                std::cout << "faces: " << label->faces_size() <<
                    " glyphs: " << label->glyphs_size() <<
                    " x: " << label->x_size() <<
                    " y: " << label->y_size() <<
                    " labels: " << mutable_layer->labels_size() <<
                    '\n';
                */
            }
        }

        // Add a textual representation of the font so that we can figure out
        // later what font we need to use.
        for (auto const& face : *layer_faces) {
            std::string name = face->family_name() + " " + face->style_name();
            mutable_layer->add_faces(name);
            // We don't delete the TileFace objects here because
            // they are 'owned' by the global faces map and deleted
            // later on.
        }

        // Insert FAKE stacks
        mutable_layer->add_stacks(baton->fontstack);

        // std::cout << " layer_faces: " << mutable_layer->faces_size() << '\n';
    }

    // Insert SDF glyphs + bitmaps
    for (auto const& face : *face_set) {
        llmr::vector::face *mutable_face = tile.add_faces();
        mutable_face->set_family(face->family_name());
        mutable_face->set_style(face->style_name());

        // Determine ASCII glyphs
        // std::set<uint32_t> omit;
        // FT_UInt glyph_index;
        // FT_ULong char_code = FT_Get_First_Char(ft_face, &glyph_index);
        // while (glyph_index != 0 && char_code < 256) {
        //     omit.insert(glyph_index);
        //     char_code = FT_Get_Next_Char(ft_face, char_code, &glyph_index);
        // }

        for (auto const& glyph : *face) {
            // Omit ASCII glyphs we determined earlier
            // if (omit.find(id) != omit.end()) {
            //     continue;
            // }

            /*
            std::cout << " Glyph: " << glyph.second.glyph_index <<
                " width: " << glyph.second.width <<
                " height: " << glyph.second.height() <<
                " left: " << glyph.second.left <<
                " top: " << glyph.second.top <<
                " advance: " << glyph.second.advance <<
                '\n';
            */

            llmr::vector::glyph *mutable_glyph = mutable_face->add_glyphs();
            mutable_glyph->set_id(glyph.second.glyph_index);
            mutable_glyph->set_width(glyph.second.width);
            mutable_glyph->set_height(glyph.second.height);
            mutable_glyph->set_left(glyph.second.left);
            mutable_glyph->set_top(glyph.second.top);
            mutable_glyph->set_advance(glyph.second.advance);
            if (glyph.second.width > 0) {
                mutable_glyph->set_bitmap(glyph.second.bitmap);
            }
        }
    }
}

void Tile::ShapeAfter(uv_work_t* req) {
    v8::HandleScope scope;
    ShapeBaton* baton = static_cast<ShapeBaton*>(req->data);

    const unsigned argc = 1;
    v8::Local<v8::Value> argv[argc] = { v8::Local<v8::Value>::New(v8::Null()) };

    v8::TryCatch try_catch;
    baton->callback->Call(v8::Context::GetCurrent()->Global(), argc, argv);
    if (try_catch.HasCaught()) {
        node::FatalException(try_catch);
    }

    baton->callback.Dispose();

    delete baton;
    delete req;
}
<|endoftext|>


<commit_before>#include "gtest/gtest.h"
#include "jpp/Jpp.h"

using namespace jpp::literals;

//****************************
// Tests for Creating Objects
//****************************

TEST(CreateJppObject, String) {
      jpp::obj temp {
          "string0"_f = "stringy0",
          "string1"_f = "stringy1",
          "string2"_f = "stringy2"
      };

    ASSERT_EQ(0, 0);
}

TEST(CreateJppObject, Number) {
    jpp::obj temp{
        "double0"_f = 3.1415926500,
        "double1"_f = 3.1415926501,
        "double2"_f = 3.1415926502
    };

    ASSERT_EQ(0, 0);
}

TEST(CreateJppObject, Integer) {
    jpp::obj temp{
        "integer0"_f = 1234500,
        "integer1"_f = 1234501,
        "integer2"_f = 1234502
    };

    ASSERT_EQ(0, 0);
}

TEST(CreateJppObject, Boolean) {
    jpp::obj temp{
        "boolean0"_f = false,
        "boolean1"_f = true
    };

    ASSERT_EQ(0, 0);
}

TEST(CreateJppObject, Arbitrary) {

    struct dummy { int x; };

    jpp::obj temp{
        "struct0"_f = dummy{ 1 },
        "struct1"_f = dummy{ 2 },
        "struct2"_f = dummy{ 3 }
    };

    ASSERT_EQ(0, 0);
}

TEST(CreateJppObject, Null) {

    jpp::obj temp{
        "null0"_f = nullptr,
        "null1"_f = nullptr
    };

    ASSERT_EQ(0, 0);
}

TEST(CreateJppObject, All) {

    struct dummy { int x; };

    jpp::obj temp {
        "string"_f = "stringy",
        "double"_f = 3.1415,
        "integer"_f = 13245,
        "boolean"_f = false,
        "fuckall"_f = dummy{6666},
        "nothing"_f = nullptr
      };

    ASSERT_EQ(0, 0);
}

//*****************************
// Tests for Accessing Objects
//*****************************

TEST(AccessJppObject, String) {
   jpp::obj temp{
       "string0"_f = "stringy0",
       "string1"_f = "stringy1",
       "string2"_f = "stringy2"
   };

   auto a0 = jpp::Get<jpp::str>(temp["string0"]);
   auto a1 = jpp::Get<jpp::str>(temp["string1"]);
   auto a2 = jpp::Get<jpp::str>(temp["string2"]);

   EXPECT_STREQ(a0, "stringy0");
   EXPECT_STREQ(a1, "stringy1");
   EXPECT_STREQ(a2, "stringy2");
}

TEST(AccessJppObject, Number) {

   jpp::obj temp{
       "double0"_f = 3.1415926500,
       "double1"_f = 3.1415926501,
       "double2"_f = 3.1415926502
   };

   auto a0 = jpp::Get<double>(temp["double0"]);
   auto a1 = jpp::Get<double>(temp["double1"]);
   auto a2 = jpp::Get<double>(temp["double2"]);

   EXPECT_EQ(a0, 3.1415926500);
   EXPECT_EQ(a1, 3.1415926501);
   EXPECT_EQ(a2, 3.1415926502);
}

TEST(AccessJppObject, Integer) {

   jpp::obj temp{
        "integer0"_f = 1234500,
        "integer1"_f = 1234501,
        "integer2"_f = 1234502
    };

   auto a0 = jpp::Get<int>(temp["integer0"]);
   auto a1 = jpp::Get<int>(temp["integer1"]);
   auto a2 = jpp::Get<int>(temp["integer2"]);

   EXPECT_EQ(a0, 1234500);
   EXPECT_EQ(a1, 1234501);
   EXPECT_EQ(a2, 1234502);
}

TEST(AccessJppObject, Boolean) {

   jpp::obj temp{
        "boolean0"_f = false,
        "boolean1"_f = true
    };

   auto a0 = jpp::Get<bool>(temp["boolean0"]);
   auto a1 = jpp::Get<bool>(temp["boolean1"]);

   EXPECT_EQ(a0, false);
   EXPECT_EQ(a1, true);
}

TEST(AccessJppObject, Arbitrary) {

    struct dummy { int x; };

   jpp::obj temp{
        "struct0"_f = dummy{ 1 },
        "struct1"_f = dummy{ 2 },
        "struct2"_f = dummy{ 3 }
    };

   auto a0 = jpp::Get<dummy>(temp["struct0"]);
   auto a1 = jpp::Get<dummy>(temp["struct1"]);
   auto a2 = jpp::Get<dummy>(temp["struct2"]);

   EXPECT_EQ(a0->x, 1);
   EXPECT_EQ(a1->x, 2);
   EXPECT_EQ(a2->x, 3);
}

TEST(AccessJppObject, Null) {

   jpp::obj temp{
        "null0"_f = nullptr,
        "null1"_f = nullptr
    };

   auto a0 = jpp::Get<jpp::null>(temp["null0"]);
   auto a1 = jpp::Get<jpp::null>(temp["null1"]);

   EXPECT_EQ(a0, nullptr);
   EXPECT_EQ(a1, nullptr);
}

TEST(AccessJppObject, All) {
   struct dummy { int x; };

    jpp::obj temp {
        "string"_f = "stringy",
        "double"_f = 3.1415,
        "integer"_f = 13245,
        "boolean"_f = false,
        "fuckall"_f = dummy{6666},
        "nothing"_f = nullptr
      };

   auto a0 = jpp::Get<jpp::str>(temp["string"]);
   auto a1 = jpp::Get<double>(temp["double"]);
   auto a2 = jpp::Get<int>(temp["integer"]);
   auto a3 = jpp::Get<bool>(temp["boolean"]);
   auto a4 = jpp::Get<dummy>(temp["fuckall"]);
   auto a5 = jpp::Get<jpp::null>(temp["nothing"]);

   EXPECT_STREQ(a0, "stringy");
   EXPECT_EQ(a1, 3.1415);
   EXPECT_EQ(a2, 13245);
   EXPECT_EQ(a3, false);
   EXPECT_EQ(a4->x, 6666);
   EXPECT_EQ(a5, nullptr);
}

TEST(WrongTypeAccessJppObject, String) {

    struct dummy {};

    jpp::obj temp{
        "string0"_f = "stringy0"
    };

    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<int>(temp["string0"]), "integral");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<bool>(temp["string0"]), "boolean");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<float>(temp["string0"]), "floating");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<jpp::null>(temp["string0"]), "null");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<dummy>(temp["string0"]), "non-fundamental");
}

TEST(WrongTypeAccessJppObject, Number) {

    struct dummy {};

    jpp::obj temp{
        "double0"_f = 3.1415926500
    };

    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<int>(temp["double0"]), "integral");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<bool>(temp["double0"]), "boolean");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<jpp::str>(temp["double0"]), "string");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<jpp::null>(temp["double0"]), "null");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<dummy>(temp["double0"]), "non-fundamental");
}

TEST(WrongTypeAccessJppObject, Integer) {

    struct dummy {};

    jpp::obj temp{
        "integer0"_f = 123456
    };

    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<float>(temp["integer0"]), "floating");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<bool>(temp["integer0"]), "boolean");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<jpp::str>(temp["integer0"]), "string");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<jpp::null>(temp["integer0"]), "null");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<dummy>(temp["integer0"]), "non-fundamental");
}

TEST(WrongTypeAccessJppObject, Boolean) {

    struct dummy {};

    jpp::obj temp{
        "boolean0"_f = true
    };

    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<float>(temp["boolean0"]), "floating");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<int>(temp["boolean0"]), "integral");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<jpp::str>(temp["boolean0"]), "string");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<jpp::null>(temp["boolean0"]), "null");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<dummy>(temp["boolean0"]), "non-fundamental");
}

TEST(WrongTypeAccessJppObject, Arbitrary) {

    struct dummy {};

    jpp::obj temp{
        "struct0"_f = dummy{}
    };

    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<float>(temp["struct0"]), "floating");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<int>(temp["struct0"]), "integral");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<jpp::str>(temp["struct0"]), "string");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<jpp::null>(temp["struct0"]), "null");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<bool>(temp["struct0"]), "boolean");
}

TEST(WrongTypeAccessJppObject, Null) {

    struct dummy {};

    jpp::obj temp{
        "null0"_f = nullptr
    };

    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<float>(temp["null0"]), "floating");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<int>(temp["null0"]), "integral");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<jpp::str>(temp["null0"]), "string");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<dummy>(temp["null0"]), "non-fundamental");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<bool>(temp["null0"]), "boolean");
}

TEST(NullAccessJppObject, All) {

    struct dummy {};

    jpp::obj temp{
    };

    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<int>(temp["string0"]), "not.*found.*empty");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<bool>(temp["string0"]), "not.*found.*empty");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<float>(temp["string0"]), "not.*found.*empty");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<jpp::str>(temp["string0"]), "not.*found.*empty");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<jpp::null>(temp["string0"]), "not.*found.*empty");
    EXPECT_DEATH_IF_SUPPORTED(jpp::Get<dummy>(temp["string0"]), "not.*found.*empty");
}

TEST(ExampleTest, TestJSON) {

    jpp::obj temp {
        "string"_f = "this is a string",
        "double"_f = 3.14159265,
        "integer"_f  = 12345,
        "boolean"_f = true,
        "null"_f = nullptr
    };
}
<commit_msg>Switch to running death tests always, regardless of support<commit_after>#include "gtest/gtest.h"
#include "jpp/Jpp.h"

using namespace jpp::literals;

//****************************
// Tests for Creating Objects
//****************************

TEST(CreateJppObject, String) {
      jpp::obj temp {
          "string0"_f = "stringy0",
          "string1"_f = "stringy1",
          "string2"_f = "stringy2"
      };

    ASSERT_EQ(0, 0);
}

TEST(CreateJppObject, Number) {
    jpp::obj temp{
        "double0"_f = 3.1415926500,
        "double1"_f = 3.1415926501,
        "double2"_f = 3.1415926502
    };

    ASSERT_EQ(0, 0);
}

TEST(CreateJppObject, Integer) {
    jpp::obj temp{
        "integer0"_f = 1234500,
        "integer1"_f = 1234501,
        "integer2"_f = 1234502
    };

    ASSERT_EQ(0, 0);
}

TEST(CreateJppObject, Boolean) {
    jpp::obj temp{
        "boolean0"_f = false,
        "boolean1"_f = true
    };

    ASSERT_EQ(0, 0);
}

TEST(CreateJppObject, Arbitrary) {

    struct dummy { int x; };

    jpp::obj temp{
        "struct0"_f = dummy{ 1 },
        "struct1"_f = dummy{ 2 },
        "struct2"_f = dummy{ 3 }
    };

    ASSERT_EQ(0, 0);
}

TEST(CreateJppObject, Null) {

    jpp::obj temp{
        "null0"_f = nullptr,
        "null1"_f = nullptr
    };

    ASSERT_EQ(0, 0);
}

TEST(CreateJppObject, All) {

    struct dummy { int x; };

    jpp::obj temp {
        "string"_f = "stringy",
        "double"_f = 3.1415,
        "integer"_f = 13245,
        "boolean"_f = false,
        "fuckall"_f = dummy{6666},
        "nothing"_f = nullptr
      };

    ASSERT_EQ(0, 0);
}

//*****************************
// Tests for Accessing Objects
//*****************************

TEST(AccessJppObject, String) {
   jpp::obj temp{
       "string0"_f = "stringy0",
       "string1"_f = "stringy1",
       "string2"_f = "stringy2"
   };

   auto a0 = jpp::Get<jpp::str>(temp["string0"]);
   auto a1 = jpp::Get<jpp::str>(temp["string1"]);
   auto a2 = jpp::Get<jpp::str>(temp["string2"]);

   EXPECT_STREQ(a0, "stringy0");
   EXPECT_STREQ(a1, "stringy1");
   EXPECT_STREQ(a2, "stringy2");
}

TEST(AccessJppObject, Number) {

   jpp::obj temp{
       "double0"_f = 3.1415926500,
       "double1"_f = 3.1415926501,
       "double2"_f = 3.1415926502
   };

   auto a0 = jpp::Get<double>(temp["double0"]);
   auto a1 = jpp::Get<double>(temp["double1"]);
   auto a2 = jpp::Get<double>(temp["double2"]);

   EXPECT_EQ(a0, 3.1415926500);
   EXPECT_EQ(a1, 3.1415926501);
   EXPECT_EQ(a2, 3.1415926502);
}

TEST(AccessJppObject, Integer) {

   jpp::obj temp{
        "integer0"_f = 1234500,
        "integer1"_f = 1234501,
        "integer2"_f = 1234502
    };

   auto a0 = jpp::Get<int>(temp["integer0"]);
   auto a1 = jpp::Get<int>(temp["integer1"]);
   auto a2 = jpp::Get<int>(temp["integer2"]);

   EXPECT_EQ(a0, 1234500);
   EXPECT_EQ(a1, 1234501);
   EXPECT_EQ(a2, 1234502);
}

TEST(AccessJppObject, Boolean) {

   jpp::obj temp{
        "boolean0"_f = false,
        "boolean1"_f = true
    };

   auto a0 = jpp::Get<bool>(temp["boolean0"]);
   auto a1 = jpp::Get<bool>(temp["boolean1"]);

   EXPECT_EQ(a0, false);
   EXPECT_EQ(a1, true);
}

TEST(AccessJppObject, Arbitrary) {

    struct dummy { int x; };

   jpp::obj temp{
        "struct0"_f = dummy{ 1 },
        "struct1"_f = dummy{ 2 },
        "struct2"_f = dummy{ 3 }
    };

   auto a0 = jpp::Get<dummy>(temp["struct0"]);
   auto a1 = jpp::Get<dummy>(temp["struct1"]);
   auto a2 = jpp::Get<dummy>(temp["struct2"]);

   EXPECT_EQ(a0->x, 1);
   EXPECT_EQ(a1->x, 2);
   EXPECT_EQ(a2->x, 3);
}

TEST(AccessJppObject, Null) {

   jpp::obj temp{
        "null0"_f = nullptr,
        "null1"_f = nullptr
    };

   auto a0 = jpp::Get<jpp::null>(temp["null0"]);
   auto a1 = jpp::Get<jpp::null>(temp["null1"]);

   EXPECT_EQ(a0, nullptr);
   EXPECT_EQ(a1, nullptr);
}

TEST(AccessJppObject, All) {
   struct dummy { int x; };

    jpp::obj temp {
        "string"_f = "stringy",
        "double"_f = 3.1415,
        "integer"_f = 13245,
        "boolean"_f = false,
        "fuckall"_f = dummy{6666},
        "nothing"_f = nullptr
      };

   auto a0 = jpp::Get<jpp::str>(temp["string"]);
   auto a1 = jpp::Get<double>(temp["double"]);
   auto a2 = jpp::Get<int>(temp["integer"]);
   auto a3 = jpp::Get<bool>(temp["boolean"]);
   auto a4 = jpp::Get<dummy>(temp["fuckall"]);
   auto a5 = jpp::Get<jpp::null>(temp["nothing"]);

   EXPECT_STREQ(a0, "stringy");
   EXPECT_EQ(a1, 3.1415);
   EXPECT_EQ(a2, 13245);
   EXPECT_EQ(a3, false);
   EXPECT_EQ(a4->x, 6666);
   EXPECT_EQ(a5, nullptr);
}

TEST(WrongTypeAccessJppObject, String) {

    struct dummy {};

    jpp::obj temp{
        "string0"_f = "stringy0"
    };

    EXPECT_DEATH(jpp::Get<int>(temp["string0"]), "integral");
    EXPECT_DEATH(jpp::Get<bool>(temp["string0"]), "boolean");
    EXPECT_DEATH(jpp::Get<float>(temp["string0"]), "floating");
    EXPECT_DEATH(jpp::Get<jpp::null>(temp["string0"]), "null");
    EXPECT_DEATH(jpp::Get<dummy>(temp["string0"]), "non-fundamental");
}

TEST(WrongTypeAccessJppObject, Number) {

    struct dummy {};

    jpp::obj temp{
        "double0"_f = 3.1415926500
    };

    EXPECT_DEATH(jpp::Get<int>(temp["double0"]), "integral");
    EXPECT_DEATH(jpp::Get<bool>(temp["double0"]), "boolean");
    EXPECT_DEATH(jpp::Get<jpp::str>(temp["double0"]), "string");
    EXPECT_DEATH(jpp::Get<jpp::null>(temp["double0"]), "null");
    EXPECT_DEATH(jpp::Get<dummy>(temp["double0"]), "non-fundamental");
}

TEST(WrongTypeAccessJppObject, Integer) {

    struct dummy {};

    jpp::obj temp{
        "integer0"_f = 123456
    };

    EXPECT_DEATH(jpp::Get<float>(temp["integer0"]), "floating");
    EXPECT_DEATH(jpp::Get<bool>(temp["integer0"]), "boolean");
    EXPECT_DEATH(jpp::Get<jpp::str>(temp["integer0"]), "string");
    EXPECT_DEATH(jpp::Get<jpp::null>(temp["integer0"]), "null");
    EXPECT_DEATH(jpp::Get<dummy>(temp["integer0"]), "non-fundamental");
}

TEST(WrongTypeAccessJppObject, Boolean) {

    struct dummy {};

    jpp::obj temp{
        "boolean0"_f = true
    };

    EXPECT_DEATH(jpp::Get<float>(temp["boolean0"]), "floating");
    EXPECT_DEATH(jpp::Get<int>(temp["boolean0"]), "integral");
    EXPECT_DEATH(jpp::Get<jpp::str>(temp["boolean0"]), "string");
    EXPECT_DEATH(jpp::Get<jpp::null>(temp["boolean0"]), "null");
    EXPECT_DEATH(jpp::Get<dummy>(temp["boolean0"]), "non-fundamental");
}

TEST(WrongTypeAccessJppObject, Arbitrary) {

    struct dummy {};

    jpp::obj temp{
        "struct0"_f = dummy{}
    };

    EXPECT_DEATH(jpp::Get<float>(temp["struct0"]), "floating");
    EXPECT_DEATH(jpp::Get<int>(temp["struct0"]), "integral");
    EXPECT_DEATH(jpp::Get<jpp::str>(temp["struct0"]), "string");
    EXPECT_DEATH(jpp::Get<jpp::null>(temp["struct0"]), "null");
    EXPECT_DEATH(jpp::Get<bool>(temp["struct0"]), "boolean");
}

TEST(WrongTypeAccessJppObject, Null) {

    struct dummy {};

    jpp::obj temp{
        "null0"_f = nullptr
    };

    EXPECT_DEATH(jpp::Get<float>(temp["null0"]), "floating");
    EXPECT_DEATH(jpp::Get<int>(temp["null0"]), "integral");
    EXPECT_DEATH(jpp::Get<jpp::str>(temp["null0"]), "string");
    EXPECT_DEATH(jpp::Get<dummy>(temp["null0"]), "non-fundamental");
    EXPECT_DEATH(jpp::Get<bool>(temp["null0"]), "boolean");
}

TEST(NullAccessJppObject, All) {

    struct dummy {};

    jpp::obj temp{
    };

    EXPECT_DEATH(jpp::Get<int>(temp["string0"]), "not.*found.*empty");
    EXPECT_DEATH(jpp::Get<bool>(temp["string0"]), "not.*found.*empty");
    EXPECT_DEATH(jpp::Get<float>(temp["string0"]), "not.*found.*empty");
    EXPECT_DEATH(jpp::Get<jpp::str>(temp["string0"]), "not.*found.*empty");
    EXPECT_DEATH(jpp::Get<jpp::null>(temp["string0"]), "not.*found.*empty");
    EXPECT_DEATH(jpp::Get<dummy>(temp["string0"]), "not.*found.*empty");
}

TEST(ExampleTest, TestJSON) {

    jpp::obj temp {
        "string"_f = "this is a string",
        "double"_f = 3.14159265,
        "integer"_f  = 12345,
        "boolean"_f = true,
        "null"_f = nullptr
    };
}
<|endoftext|>


<commit_before>// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "writeOBJ.h"

#include <iostream>
#include <limits>
#include <iomanip>
#include <fstream>
#include <cstdio>
#include <cassert>

template <typename DerivedV, typename DerivedF, typename DerivedT>
IGL_INLINE bool igl::writeOBJ(
  const std::string str,
  const Eigen::PlainObjectBase<DerivedV>& V,
  const Eigen::PlainObjectBase<DerivedF>& F,
  const Eigen::PlainObjectBase<DerivedV>& CN,
  const Eigen::PlainObjectBase<DerivedF>& FN,
  const Eigen::PlainObjectBase<DerivedT>& TC,
  const Eigen::PlainObjectBase<DerivedF>& FTC)
{
  FILE * obj_file = fopen(str.c_str(),"w");
  if(NULL==obj_file)
  {
    printf("IOError: %s could not be opened for writing...",str.c_str());
    return false;
  }
  // Loop over V
  for(int i = 0;i<(int)V.rows();i++)
  {
    fprintf(obj_file,"v %0.15g %0.15g %0.15g\n",
      V(i,0),
      V(i,1),
      V(i,2)
      );
  }
  bool write_N = CN.rows() >0;

  if(write_N)
  {
    for(int i = 0;i<(int)CN.rows();i++)
    {
      fprintf(obj_file,"v %0.15g %0.15g %0.15g\n",
              CN(i,0),
              CN(i,1),
              CN(i,2)
              );
    }
    fprintf(obj_file,"\n");
  }

  bool write_texture_coords = TC.rows() >0;

  if(write_texture_coords)
  {
    for(int i = 0;i<(int)TC.rows();i++)
    {
      fprintf(obj_file, "vt %0.15g %0.15g\n",TC(i,0),TC(i,1));
    }
    fprintf(obj_file,"\n");
  }

  // loop over F
  for(int i = 0;i<(int)F.rows();++i)
  {
    fprintf(obj_file,"f");
    for(int j = 0; j<(int)F.cols();++j)
    {
      // OBJ is 1-indexed
      fprintf(obj_file," %u",F(i,j)+1);

      if(write_texture_coords)
        fprintf(obj_file,"/%u",FTC(i,j)+1);
      if(write_N)
      {
        if (write_texture_coords)
          fprintf(obj_file,"/%u",FN(i,j)+1);
        else
          fprintf(obj_file,"//%u",FN(i,j)+1);
      }
    }
    fprintf(obj_file,"\n");
  }
  fclose(obj_file);
  return true;
}

template <typename DerivedV, typename DerivedF>
IGL_INLINE bool igl::writeOBJ(
  const std::string str,
  const Eigen::PlainObjectBase<DerivedV>& V,
  const Eigen::PlainObjectBase<DerivedF>& F)
{
  using namespace std;
  using namespace Eigen;
  assert(V.cols() == 3 && "V should have 3 columns");
  ofstream s(str);
  if(!s.is_open())
  {
    fprintf(stderr,"IOError: writeOBJ() could not open %s\n",str.c_str());
    return false;
  }
  s<<
    V.format(IOFormat(FullPrecision,DontAlignCols," ","\n","v ","","",""))<<
    (F.array()+1).format(IOFormat(FullPrecision,DontAlignCols," ","\n","f ","","",""));
  return true;
}

#ifdef IGL_STATIC_LIBRARY
// Explicit template specialization
// generated by autoexplicit.sh
template bool igl::writeOBJ<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&);
template bool igl::writeOBJ<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1>, Eigen::Matrix<double, -1, 2, 1, -1, 2> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 2, 1, -1, 2> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1> > const&);
template bool igl::writeOBJ<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1>, Eigen::Matrix<float, -1, 2, 1, -1, 2> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 2, 1, -1, 2> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1> > const&);
template bool igl::writeOBJ<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&);
template bool igl::writeOBJ<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 2, 0, -1, 2> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 2, 0, -1, 2> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&);
template bool igl::writeOBJ<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&);
template bool igl::writeOBJ<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&);
#endif
<commit_msg>write object bug: need newline between end of v and start of f<commit_after>// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "writeOBJ.h"

#include <iostream>
#include <limits>
#include <iomanip>
#include <fstream>
#include <cstdio>
#include <cassert>

template <typename DerivedV, typename DerivedF, typename DerivedT>
IGL_INLINE bool igl::writeOBJ(
  const std::string str,
  const Eigen::PlainObjectBase<DerivedV>& V,
  const Eigen::PlainObjectBase<DerivedF>& F,
  const Eigen::PlainObjectBase<DerivedV>& CN,
  const Eigen::PlainObjectBase<DerivedF>& FN,
  const Eigen::PlainObjectBase<DerivedT>& TC,
  const Eigen::PlainObjectBase<DerivedF>& FTC)
{
  FILE * obj_file = fopen(str.c_str(),"w");
  if(NULL==obj_file)
  {
    printf("IOError: %s could not be opened for writing...",str.c_str());
    return false;
  }
  // Loop over V
  for(int i = 0;i<(int)V.rows();i++)
  {
    fprintf(obj_file,"v %0.15g %0.15g %0.15g\n",
      V(i,0),
      V(i,1),
      V(i,2)
      );
  }
  bool write_N = CN.rows() >0;

  if(write_N)
  {
    for(int i = 0;i<(int)CN.rows();i++)
    {
      fprintf(obj_file,"v %0.15g %0.15g %0.15g\n",
              CN(i,0),
              CN(i,1),
              CN(i,2)
              );
    }
    fprintf(obj_file,"\n");
  }

  bool write_texture_coords = TC.rows() >0;

  if(write_texture_coords)
  {
    for(int i = 0;i<(int)TC.rows();i++)
    {
      fprintf(obj_file, "vt %0.15g %0.15g\n",TC(i,0),TC(i,1));
    }
    fprintf(obj_file,"\n");
  }

  // loop over F
  for(int i = 0;i<(int)F.rows();++i)
  {
    fprintf(obj_file,"f");
    for(int j = 0; j<(int)F.cols();++j)
    {
      // OBJ is 1-indexed
      fprintf(obj_file," %u",F(i,j)+1);

      if(write_texture_coords)
        fprintf(obj_file,"/%u",FTC(i,j)+1);
      if(write_N)
      {
        if (write_texture_coords)
          fprintf(obj_file,"/%u",FN(i,j)+1);
        else
          fprintf(obj_file,"//%u",FN(i,j)+1);
      }
    }
    fprintf(obj_file,"\n");
  }
  fclose(obj_file);
  return true;
}

template <typename DerivedV, typename DerivedF>
IGL_INLINE bool igl::writeOBJ(
  const std::string str,
  const Eigen::PlainObjectBase<DerivedV>& V,
  const Eigen::PlainObjectBase<DerivedF>& F)
{
  using namespace std;
  using namespace Eigen;
  assert(V.cols() == 3 && "V should have 3 columns");
  ofstream s(str);
  if(!s.is_open())
  {
    fprintf(stderr,"IOError: writeOBJ() could not open %s\n",str.c_str());
    return false;
  }
  s<<
    V.format(IOFormat(FullPrecision,DontAlignCols," ","\n","v ","","","\n"))<<
    (F.array()+1).format(IOFormat(FullPrecision,DontAlignCols," ","\n","f ","","","\n"));
  return true;
}

#ifdef IGL_STATIC_LIBRARY
// Explicit template specialization
// generated by autoexplicit.sh
template bool igl::writeOBJ<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&);
template bool igl::writeOBJ<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1>, Eigen::Matrix<double, -1, 2, 1, -1, 2> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 2, 1, -1, 2> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1> > const&);
template bool igl::writeOBJ<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1>, Eigen::Matrix<float, -1, 2, 1, -1, 2> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 2, 1, -1, 2> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, -1, 1, -1, -1> > const&);
template bool igl::writeOBJ<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&);
template bool igl::writeOBJ<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 2, 0, -1, 2> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 2, 0, -1, 2> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&);
template bool igl::writeOBJ<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&);
template bool igl::writeOBJ<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&);
#endif
<|endoftext|>


<commit_before>#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "highlevel_feature_extractor.h"
#include <rcsc/common/server_param.h>

using namespace rcsc;

HighLevelFeatureExtractor::HighLevelFeatureExtractor(int num_teammates,
                                                     int num_opponents,
                                                     bool playing_offense) :
    FeatureExtractor(num_teammates, num_opponents, playing_offense)
{
  assert(numTeammates >= 0);
  assert(numOpponents >= 0);
  numFeatures = num_basic_features + features_per_teammate * numTeammates;
  if (numOpponents > 0) {
    // One extra basic feature and one feature per teammate
    numFeatures += 1 + numTeammates;
  }
  feature_vec.resize(numFeatures);
}

HighLevelFeatureExtractor::~HighLevelFeatureExtractor() {}

const std::vector<float>& HighLevelFeatureExtractor::ExtractFeatures(
    const WorldModel& wm) {
  featIndx = 0;
  const ServerParam& SP = ServerParam::i();
  const SelfObject& self = wm.self();
  const Vector2D& self_pos = self.pos();
  const float self_ang = self.body().radian();
  const PlayerCont& teammates = wm.teammates();
  float maxR = sqrtf(SP.pitchHalfLength() * SP.pitchHalfLength()
                     + SP.pitchHalfWidth() * SP.pitchHalfWidth());
  // features about self pos
  // Allow the agent to go 10% over the playfield in any direction
  float tolerance_x = .1 * SP.pitchHalfLength();
  float tolerance_y = .1 * SP.pitchHalfWidth();
  // Feature[0]: X-postion
  if (playingOffense) {
    addNormFeature(self_pos.x, -tolerance_x, SP.pitchHalfLength() + tolerance_x);
  } else {
    addNormFeature(self_pos.x, -SP.pitchHalfLength()-tolerance_x, tolerance_x);
  }
  // addFeature(self_pos.x);

  // Feature[1]: Y-Position
  addNormFeature(self_pos.y, -SP.pitchHalfWidth() - tolerance_y,
                 SP.pitchHalfWidth() + tolerance_y);
  // addFeature(self_pos.y);

  // Feature[2]: Self Angle
  addNormFeature(self_ang, -2*M_PI, 2*M_PI); // addFeature(self_ang);

  float r;
  float th;
  // features about the ball
  Vector2D ball_pos = wm.ball().pos();
  angleDistToPoint(self_pos, ball_pos, th, r);
  // Feature[3]: Dist to ball
  addNormFeature(r, 0, maxR); // addFeature(r);
  // Feature[4]: Ang to ball
  addNormFeature(th, -2*M_PI, 2*M_PI); // addFeature(th);
  // Feature[5]: Able to kick
  addNormFeature(self.isKickable(), false, true); // addFeature(self.isKickable());

  // features about distance to goal center
  Vector2D goalCenter(SP.pitchHalfLength(), 0);
  if (!playingOffense) {
    goalCenter.assign(-SP.pitchHalfLength(), 0);
  }
  angleDistToPoint(self_pos, goalCenter, th, r);
  // Feature[6]: Goal Center Distance
  addNormFeature(r, 0, maxR); // addFeature(r);
  // Feature[7]: Angle to goal center
  addNormFeature(th, -2*M_PI, 2*M_PI); // addFeature(th);
  // Feature[8]: largest open goal angle
  addNormFeature(calcLargestGoalAngle(wm, self_pos), 0, M_PI);
  // addFeature(calcLargestGoalAngle(wm, self_pos));

  // Features [9+T - 9 + 2T]: teammate's open angle to goal
  int detected_teammates = 0;
  for (PlayerCont::const_iterator it=teammates.begin(); it != teammates.end(); ++it) {
    const PlayerObject& teammate = *it;
    if (valid(teammate) && detected_teammates < numTeammates) {
      addNormFeature(calcLargestGoalAngle(wm, teammate.pos()), 0, M_PI);
      // addFeature(calcLargestGoalAngle(wm, teammate.pos()));
      detected_teammates++;
    }
  }
  // Add zero features for any missing teammates
  for (int i=detected_teammates; i<numTeammates; ++i) {
    addFeature(FEAT_INVALID);
  }

  // dist to our closest opp
  if (numOpponents > 0) {
    calcClosestOpp(wm, self_pos, th, r);
    addNormFeature(r, 0, maxR); // addFeature(r);

    // teammates dists to closest opps
    detected_teammates = 0;
    for (PlayerCont::const_iterator it=teammates.begin(); it != teammates.end(); ++it) {
      const PlayerObject& teammate = *it;
      if (valid(teammate) && detected_teammates < numTeammates) {
        calcClosestOpp(wm, teammate.pos(), th, r);
        addNormFeature(r, 0, maxR); // addFeature(r);
        detected_teammates++;
      }
    }
    // Add zero features for any missing teammates
    for (int i=detected_teammates; i<numTeammates; ++i) {
      addFeature(FEAT_INVALID);
    }
  }

  // Features [9+2T - 9+3T]: open angle to teammates
  detected_teammates = 0;
  for (PlayerCont::const_iterator it=teammates.begin(); it != teammates.end(); ++it) {
    const PlayerObject& teammate = *it;
    if (valid(teammate) && detected_teammates < numTeammates) {
      addNormFeature(calcLargestTeammateAngle(wm, self_pos, teammate.pos()),0,M_PI);
      // addFeature(calcLargestTeammateAngle(wm, self_pos, teammate.pos()));
      detected_teammates++;
    }
  }
  // Add zero features for any missing teammates
  for (int i=detected_teammates; i<numTeammates; ++i) {
    addFeature(FEAT_INVALID);
  }

  // Features [9+3T - end]: dist, angle, unum of teammates
  detected_teammates = 0;
  for (PlayerCont::const_iterator it=teammates.begin(); it != teammates.end(); ++it) {
    const PlayerObject& teammate = *it;
    if (valid(teammate) && detected_teammates < numTeammates) {
      angleDistToPoint(self_pos, teammate.pos(), th, r);
      addNormFeature(r,0,maxR); // addFeature(r);
      addNormFeature(th,-M_PI,M_PI); // addFeature(th);
      addFeature(teammate.unum());
      detected_teammates++;
    }
  }
  // Add zero features for any missing teammates
  for (int i=detected_teammates; i<numTeammates; ++i) {
    addFeature(FEAT_INVALID);
    addFeature(FEAT_INVALID);
    addFeature(FEAT_INVALID);
  }

  assert(featIndx == numFeatures);
  // checkFeatures();
  //std::cout << "features: " << features.rows(0,ind-1).t();
  return feature_vec;
}
<commit_msg>Corrected bug in high level feature set.<commit_after>#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "highlevel_feature_extractor.h"
#include <rcsc/common/server_param.h>

using namespace rcsc;

HighLevelFeatureExtractor::HighLevelFeatureExtractor(int num_teammates,
                                                     int num_opponents,
                                                     bool playing_offense) :
    FeatureExtractor(num_teammates, num_opponents, playing_offense)
{
  assert(numTeammates >= 0);
  assert(numOpponents >= 0);
  numFeatures = num_basic_features + features_per_teammate * numTeammates;
  if (numOpponents > 0) {
    // One extra basic feature and one feature per teammate
    numFeatures += 1 + numTeammates;
  }
  feature_vec.resize(numFeatures);
}

HighLevelFeatureExtractor::~HighLevelFeatureExtractor() {}

const std::vector<float>& HighLevelFeatureExtractor::ExtractFeatures(
    const WorldModel& wm) {
  featIndx = 0;
  const ServerParam& SP = ServerParam::i();
  const SelfObject& self = wm.self();
  const Vector2D& self_pos = self.pos();
  const float self_ang = self.body().radian();
  const PlayerCont& teammates = wm.teammates();
  float maxR = sqrtf(SP.pitchHalfLength() * SP.pitchHalfLength()
                     + SP.pitchHalfWidth() * SP.pitchHalfWidth());
  // features about self pos
  // Allow the agent to go 10% over the playfield in any direction
  float tolerance_x = .1 * SP.pitchHalfLength();
  float tolerance_y = .1 * SP.pitchHalfWidth();
  // Feature[0]: X-postion
  if (playingOffense) {
    addNormFeature(self_pos.x, -tolerance_x, SP.pitchHalfLength() + tolerance_x);
  } else {
    addNormFeature(self_pos.x, -SP.pitchHalfLength()-tolerance_x, tolerance_x);
  }
  // addFeature(self_pos.x);

  // Feature[1]: Y-Position
  addNormFeature(self_pos.y, -SP.pitchHalfWidth() - tolerance_y,
                 SP.pitchHalfWidth() + tolerance_y);
  // addFeature(self_pos.y);

  // Feature[2]: Self Angle
  addNormFeature(self_ang, -M_PI, M_PI); // addFeature(self_ang);

  float r;
  float th;
  // features about the ball
  Vector2D ball_pos = wm.ball().pos();
  angleDistToPoint(self_pos, ball_pos, th, r);
  // Feature[3]: Dist to ball
  addNormFeature(r, 0, maxR); // addFeature(r);
  // Feature[4]: Ang to ball
  addNormFeature(th, -M_PI, M_PI); // addFeature(th);
  // Feature[5]: Able to kick
  addNormFeature(self.isKickable(), false, true); // addFeature(self.isKickable());

  // features about distance to goal center
  Vector2D goalCenter(SP.pitchHalfLength(), 0);
  if (!playingOffense) {
    goalCenter.assign(-SP.pitchHalfLength(), 0);
  }
  angleDistToPoint(self_pos, goalCenter, th, r);
  // Feature[6]: Goal Center Distance
  addNormFeature(r, 0, maxR); // addFeature(r);
  // Feature[7]: Angle to goal center
  addNormFeature(th, -M_PI, M_PI); // addFeature(th);
  // Feature[8]: largest open goal angle
  addNormFeature(calcLargestGoalAngle(wm, self_pos), 0, M_PI);
  // addFeature(calcLargestGoalAngle(wm, self_pos));

  // Features [9+T - 9 + 2T]: teammate's open angle to goal
  int detected_teammates = 0;
  for (PlayerCont::const_iterator it=teammates.begin(); it != teammates.end(); ++it) {
    const PlayerObject& teammate = *it;
    if (valid(teammate) && detected_teammates < numTeammates) {
      addNormFeature(calcLargestGoalAngle(wm, teammate.pos()), 0, M_PI);
      // addFeature(calcLargestGoalAngle(wm, teammate.pos()));
      detected_teammates++;
    }
  }
  // Add zero features for any missing teammates
  for (int i=detected_teammates; i<numTeammates; ++i) {
    addFeature(FEAT_INVALID);
  }

  // dist to our closest opp
  if (numOpponents > 0) {
    calcClosestOpp(wm, self_pos, th, r);
    addNormFeature(r, 0, maxR); // addFeature(r);

    // teammates dists to closest opps
    detected_teammates = 0;
    for (PlayerCont::const_iterator it=teammates.begin(); it != teammates.end(); ++it) {
      const PlayerObject& teammate = *it;
      if (valid(teammate) && detected_teammates < numTeammates) {
        calcClosestOpp(wm, teammate.pos(), th, r);
        addNormFeature(r, 0, maxR); // addFeature(r);
        detected_teammates++;
      }
    }
    // Add zero features for any missing teammates
    for (int i=detected_teammates; i<numTeammates; ++i) {
      addFeature(FEAT_INVALID);
    }
  }

  // Features [9+2T - 9+3T]: open angle to teammates
  detected_teammates = 0;
  for (PlayerCont::const_iterator it=teammates.begin(); it != teammates.end(); ++it) {
    const PlayerObject& teammate = *it;
    if (valid(teammate) && detected_teammates < numTeammates) {
      addNormFeature(calcLargestTeammateAngle(wm, self_pos, teammate.pos()),0,M_PI);
      // addFeature(calcLargestTeammateAngle(wm, self_pos, teammate.pos()));
      detected_teammates++;
    }
  }
  // Add zero features for any missing teammates
  for (int i=detected_teammates; i<numTeammates; ++i) {
    addFeature(FEAT_INVALID);
  }

  // Features [9+3T - end]: dist, angle, unum of teammates
  detected_teammates = 0;
  for (PlayerCont::const_iterator it=teammates.begin(); it != teammates.end(); ++it) {
    const PlayerObject& teammate = *it;
    if (valid(teammate) && detected_teammates < numTeammates) {
      angleDistToPoint(self_pos, teammate.pos(), th, r);
      addNormFeature(r,0,maxR); // addFeature(r);
      addNormFeature(th,-M_PI,M_PI); // addFeature(th);
      addFeature(teammate.unum());
      detected_teammates++;
    }
  }
  // Add zero features for any missing teammates
  for (int i=detected_teammates; i<numTeammates; ++i) {
    addFeature(FEAT_INVALID);
    addFeature(FEAT_INVALID);
    addFeature(FEAT_INVALID);
  }

  assert(featIndx == numFeatures);
  // checkFeatures();
  //std::cout << "features: " << features.rows(0,ind-1).t();
  return feature_vec;
}
<|endoftext|>


<commit_before>#include <cstdlib>
#include <cstring>
#include <iostream>
#include <getopt.h>
#include <signal.h>
#include <map>

#include <sys/inotify.h>
#include <unistd.h>

#include "debug-func.hpp"
#include "main.hpp"
#include "wayfire/nonstd/safe-list.hpp"
#include <wayfire/config/file.hpp>

#include <wayland-server.h>

#include "core/core-impl.hpp"
#include "wayfire/output.hpp"

wf_runtime_config runtime_config;

#define INOT_BUF_SIZE (1024 * sizeof(inotify_event))
static char buf[INOT_BUF_SIZE];

static std::string config_dir, config_file;

static void reload_config(int fd)
{
    wf::config::load_configuration_options_from_file(
        wf::get_core().config, config_file);
    inotify_add_watch(fd, config_dir.c_str(), IN_CREATE);
    inotify_add_watch(fd, config_file.c_str(), IN_MODIFY);
}

static int handle_config_updated(int fd, uint32_t mask, void *data)
{
    LOGD("Reloading configuration file");

    /* read, but don't use */
    read(fd, buf, INOT_BUF_SIZE);
    reload_config(fd);

    wf::get_core().emit_signal("reload-config", nullptr);

    return 0;
}

static void print_version()
{
    std::cout << WAYFIRE_VERSION << std::endl;
    exit(0);
}

static void print_help()
{
    std::cout << "Wayfire " << WAYFIRE_VERSION << std::endl;
    std::cout << "Usage: wayfire [OPTION]...\n" << std::endl;
    std::cout << " -c,  --config            specify config file to use" << std::endl;
    std::cout << " -h,  --help              print this help" << std::endl;
    std::cout << " -d,  --debug             enable debug logging" << std::endl;
    std::cout <<
        " -D,  --damage-debug      enable additional debug for damaged regions" <<
        std::endl;
    std::cout << " -R,  --damage-rerender   rerender damaged regions" << std::endl;
    std::cout << " -v,  --version           print version and exit" << std::endl;
    exit(0);
}

std::map<EGLint, EGLint> default_attribs = {
    {EGL_RED_SIZE, 1},
    {EGL_GREEN_SIZE, 1},
    {EGL_BLUE_SIZE, 1},
    {EGL_DEPTH_SIZE, 1},
};

std::map<wlr_renderer*, wlr_egl*> egl_for_renderer;

/* Merge the default config and the config we need */
static std::vector<EGLint> generate_config_attribs(EGLint *renderer_attribs)
{
    std::vector<EGLint> attribs;

    /* See what we have in the default config */
    for (auto i = renderer_attribs; i != NULL && *i != EGL_NONE; i++)
    {
        /* We will override this value later */
        if (default_attribs.count(*i))
        {
            ++i;
            continue;
        }

        attribs.push_back(*i);
        i++;
        attribs.push_back(*i);
    }

    /* Then pack all values we want */
    for (auto & p : default_attribs)
    {
        attribs.push_back(p.first);
        attribs.push_back(p.second);
    }

    attribs.push_back(EGL_NONE);

    return attribs;
}

wlr_renderer *add_egl_depth_renderer(wlr_egl *egl, EGLenum platform,
    void *remote, EGLint *_r_attr, EGLint visual)
{
    bool r;
    auto attribs = generate_config_attribs(_r_attr);
    r = wlr_egl_init(egl, platform, remote, attribs.data(), visual);

    if (!r)
    {
        LOGE("Failed to initialize EGL");

        return NULL;
    }

    auto renderer = wlr_gles2_renderer_create(egl);
    if (!renderer)
    {
        LOGE("Failed to create GLES2 renderer");
        wlr_egl_finish(egl);

        return NULL;
    }

    egl_for_renderer[renderer] = egl;

    return renderer;
}

namespace wf
{
namespace _safe_list_detail
{
wl_event_loop *event_loop;
void idle_cleanup_func(void *data)
{
    auto priv = reinterpret_cast<std::function<void()>*>(data);
    (*priv)();
}
}
}

static bool drop_permissions(void)
{
    if ((getuid() != geteuid()) || (getgid() != getegid()))
    {
        // Set the gid and uid in the correct order.
        if ((setgid(getgid()) != 0) || (setuid(getuid()) != 0))
        {
            LOGE("Unable to drop root, refusing to start");

            return false;
        }
    }

    if ((setgid(0) != -1) || (setuid(0) != -1))
    {
        LOGE("Unable to drop root (we shouldn't be able to "
             "restore it after setuid), refusing to start");

        return false;
    }

    return true;
}

static wf::log::color_mode_t detect_color_mode()
{
    return isatty(STDOUT_FILENO) ?
           wf::log::LOG_COLOR_MODE_ON : wf::log::LOG_COLOR_MODE_OFF;
}

static void wlr_log_handler(wlr_log_importance level,
    const char *fmt, va_list args)
{
    const int bufsize = 4 * 1024;
    char buffer[bufsize];
    vsnprintf(buffer, bufsize, fmt, args);

    wf::log::log_level_t wlevel;
    switch (level)
    {
      case WLR_ERROR:
        wlevel = wf::log::LOG_LEVEL_ERROR;
        break;

      case WLR_INFO:
        wlevel = wf::log::LOG_LEVEL_INFO;
        break;

      case WLR_DEBUG:
        wlevel = wf::log::LOG_LEVEL_DEBUG;
        break;

      default:
        return;
    }

    wf::log::log_plain(wlevel, buffer);
}

#ifndef ASAN_ENABLED
static void signal_handler(int signal)
{
    std::string error;
    switch (signal)
    {
      case SIGSEGV:
        error = "Segmentation fault";
        break;

      case SIGFPE:
        error = "Floating-point exception";
        break;

      case SIGABRT:
        error = "Fatal error(SIGABRT)";
        break;

      default:
        error = "Unknown";
    }

    LOGE("Fatal error: ", error);
    wf::print_trace(false);
    std::exit(0);
}

#endif

static std::optional<std::string> choose_socket(wl_display *display)
{
    for (int i = 1; i <= 32; i++)
    {
        auto name = "wayland-" + std::to_string(i);
        if (wl_display_add_socket(display, name.c_str()) >= 0)
        {
            return name;
        }
    }

    return {};
}

int main(int argc, char *argv[])
{
    config_dir = nonull(getenv("XDG_CONFIG_HOME"));
    if (!config_dir.compare("nil"))
    {
        config_dir = std::string(nonull(getenv("HOME"))) + "/.config";
    }

    config_file = config_dir + "/wayfire.ini";

    wf::log::log_level_t log_level = wf::log::LOG_LEVEL_INFO;
    struct option opts[] = {
        {
            "config", required_argument, NULL, 'c'
        },
        {"debug", no_argument, NULL, 'd'},
        {"damage-debug", no_argument, NULL, 'D'},
        {"damage-rerender", no_argument, NULL, 'R'},
        {"help", no_argument, NULL, 'h'},
        {"version", no_argument, NULL, 'v'},
        {0, 0, NULL, 0}
    };

    int c, i;
    while ((c = getopt_long(argc, argv, "c:dDhRv", opts, &i)) != -1)
    {
        switch (c)
        {
          case 'c':
            config_file = optarg;
            break;

          case 'D':
            runtime_config.damage_debug = true;
            break;

          case 'R':
            runtime_config.no_damage_track = true;
            break;

          case 'h':
            print_help();
            break;

          case 'd':
            log_level = wf::log::LOG_LEVEL_DEBUG;
            break;

          case 'v':
            print_version();
            break;

          default:
            std::cerr << "Unrecognized command line argument " << optarg << "\n" <<
                std::endl;
        }
    }

    auto wlr_log_level =
        (log_level == wf::log::LOG_LEVEL_DEBUG ? WLR_DEBUG : WLR_ERROR);
    wlr_log_init(wlr_log_level, wlr_log_handler);
    wf::log::initialize_logging(std::cout, log_level, detect_color_mode());

#ifndef ASAN_ENABLED
    /* In case of crash, print the stacktrace for debugging.
     * However, if ASAN is enabled, we'll get better stacktrace from there. */
    signal(SIGSEGV, signal_handler);
    signal(SIGFPE, signal_handler);
    signal(SIGABRT, signal_handler);
#endif

    LOGI("Starting wayfire version ", WAYFIRE_VERSION);
    /* First create display and initialize safe-list's event loop, so that
     * wf objects (which depend on safe-list) can work */
    auto display = wl_display_create();
    wf::_safe_list_detail::event_loop = wl_display_get_event_loop(display);

    auto& core = wf::get_core_impl();

    /** TODO: move this to core_impl constructor */
    core.display  = display;
    core.ev_loop  = wl_display_get_event_loop(core.display);
    core.backend  = wlr_backend_autocreate(core.display, add_egl_depth_renderer);
    core.renderer = wlr_backend_get_renderer(core.backend);
    core.egl = egl_for_renderer[core.renderer];
    assert(core.egl);

    if (!drop_permissions())
    {
        wl_display_destroy_clients(core.display);
        wl_display_destroy(core.display);

        return EXIT_FAILURE;
    }

    std::vector<std::string> xmldirs;
    if (char *plugin_xml_path = getenv("WAYFIRE_PLUGIN_XML_PATH"))
    {
        std::stringstream ss(plugin_xml_path);
        std::string entry;
        while (std::getline(ss, entry, ':'))
        {
            xmldirs.push_back(entry);
        }
    }

    xmldirs.push_back(PLUGIN_XML_DIR);

    LOGI("using config file: ", config_file.c_str());
    core.config = wf::config::build_configuration(
        xmldirs, SYSCONFDIR "/wayfire/defaults.ini", config_file);

    int inotify_fd = inotify_init1(IN_CLOEXEC);
    reload_config(inotify_fd);

    wl_event_loop_add_fd(core.ev_loop, inotify_fd, WL_EVENT_READABLE,
        handle_config_updated, NULL);
    core.init();

    auto socket = choose_socket(core.display);
    if (!socket)
    {
        LOGE("Failed to create wayland socket, exiting.");

        return -1;
    }

    core.wayland_display = socket.value();
    LOGI("Using socket name ", core.wayland_display);
    if (!wlr_backend_start(core.backend))
    {
        LOGE("Failed to initialize backend, exiting");
        wlr_backend_destroy(core.backend);
        wl_display_destroy(core.display);

        return -1;
    }

    setenv("WAYLAND_DISPLAY", core.wayland_display.c_str(), 1);
    wl_display_run(core.display);

    /* Teardown */
    wl_display_destroy_clients(core.display);
    wl_display_destroy(core.display);

    return EXIT_SUCCESS;
}
<commit_msg>main: use _Exit() in signal handlers<commit_after>#include <cstdlib>
#include <cstring>
#include <iostream>
#include <getopt.h>
#include <signal.h>
#include <map>

#include <sys/inotify.h>
#include <unistd.h>

#include "debug-func.hpp"
#include "main.hpp"
#include "wayfire/nonstd/safe-list.hpp"
#include <wayfire/config/file.hpp>

#include <wayland-server.h>

#include "core/core-impl.hpp"
#include "wayfire/output.hpp"

wf_runtime_config runtime_config;

#define INOT_BUF_SIZE (1024 * sizeof(inotify_event))
static char buf[INOT_BUF_SIZE];

static std::string config_dir, config_file;

static void reload_config(int fd)
{
    wf::config::load_configuration_options_from_file(
        wf::get_core().config, config_file);
    inotify_add_watch(fd, config_dir.c_str(), IN_CREATE);
    inotify_add_watch(fd, config_file.c_str(), IN_MODIFY);
}

static int handle_config_updated(int fd, uint32_t mask, void *data)
{
    LOGD("Reloading configuration file");

    /* read, but don't use */
    read(fd, buf, INOT_BUF_SIZE);
    reload_config(fd);

    wf::get_core().emit_signal("reload-config", nullptr);

    return 0;
}

static void print_version()
{
    std::cout << WAYFIRE_VERSION << std::endl;
    exit(0);
}

static void print_help()
{
    std::cout << "Wayfire " << WAYFIRE_VERSION << std::endl;
    std::cout << "Usage: wayfire [OPTION]...\n" << std::endl;
    std::cout << " -c,  --config            specify config file to use" << std::endl;
    std::cout << " -h,  --help              print this help" << std::endl;
    std::cout << " -d,  --debug             enable debug logging" << std::endl;
    std::cout <<
        " -D,  --damage-debug      enable additional debug for damaged regions" <<
        std::endl;
    std::cout << " -R,  --damage-rerender   rerender damaged regions" << std::endl;
    std::cout << " -v,  --version           print version and exit" << std::endl;
    exit(0);
}

std::map<EGLint, EGLint> default_attribs = {
    {EGL_RED_SIZE, 1},
    {EGL_GREEN_SIZE, 1},
    {EGL_BLUE_SIZE, 1},
    {EGL_DEPTH_SIZE, 1},
};

std::map<wlr_renderer*, wlr_egl*> egl_for_renderer;

/* Merge the default config and the config we need */
static std::vector<EGLint> generate_config_attribs(EGLint *renderer_attribs)
{
    std::vector<EGLint> attribs;

    /* See what we have in the default config */
    for (auto i = renderer_attribs; i != NULL && *i != EGL_NONE; i++)
    {
        /* We will override this value later */
        if (default_attribs.count(*i))
        {
            ++i;
            continue;
        }

        attribs.push_back(*i);
        i++;
        attribs.push_back(*i);
    }

    /* Then pack all values we want */
    for (auto & p : default_attribs)
    {
        attribs.push_back(p.first);
        attribs.push_back(p.second);
    }

    attribs.push_back(EGL_NONE);

    return attribs;
}

wlr_renderer *add_egl_depth_renderer(wlr_egl *egl, EGLenum platform,
    void *remote, EGLint *_r_attr, EGLint visual)
{
    bool r;
    auto attribs = generate_config_attribs(_r_attr);
    r = wlr_egl_init(egl, platform, remote, attribs.data(), visual);

    if (!r)
    {
        LOGE("Failed to initialize EGL");

        return NULL;
    }

    auto renderer = wlr_gles2_renderer_create(egl);
    if (!renderer)
    {
        LOGE("Failed to create GLES2 renderer");
        wlr_egl_finish(egl);

        return NULL;
    }

    egl_for_renderer[renderer] = egl;

    return renderer;
}

namespace wf
{
namespace _safe_list_detail
{
wl_event_loop *event_loop;
void idle_cleanup_func(void *data)
{
    auto priv = reinterpret_cast<std::function<void()>*>(data);
    (*priv)();
}
}
}

static bool drop_permissions(void)
{
    if ((getuid() != geteuid()) || (getgid() != getegid()))
    {
        // Set the gid and uid in the correct order.
        if ((setgid(getgid()) != 0) || (setuid(getuid()) != 0))
        {
            LOGE("Unable to drop root, refusing to start");

            return false;
        }
    }

    if ((setgid(0) != -1) || (setuid(0) != -1))
    {
        LOGE("Unable to drop root (we shouldn't be able to "
             "restore it after setuid), refusing to start");

        return false;
    }

    return true;
}

static wf::log::color_mode_t detect_color_mode()
{
    return isatty(STDOUT_FILENO) ?
           wf::log::LOG_COLOR_MODE_ON : wf::log::LOG_COLOR_MODE_OFF;
}

static void wlr_log_handler(wlr_log_importance level,
    const char *fmt, va_list args)
{
    const int bufsize = 4 * 1024;
    char buffer[bufsize];
    vsnprintf(buffer, bufsize, fmt, args);

    wf::log::log_level_t wlevel;
    switch (level)
    {
      case WLR_ERROR:
        wlevel = wf::log::LOG_LEVEL_ERROR;
        break;

      case WLR_INFO:
        wlevel = wf::log::LOG_LEVEL_INFO;
        break;

      case WLR_DEBUG:
        wlevel = wf::log::LOG_LEVEL_DEBUG;
        break;

      default:
        return;
    }

    wf::log::log_plain(wlevel, buffer);
}

#ifndef ASAN_ENABLED
static void signal_handler(int signal)
{
    std::string error;
    switch (signal)
    {
      case SIGSEGV:
        error = "Segmentation fault";
        break;

      case SIGFPE:
        error = "Floating-point exception";
        break;

      case SIGABRT:
        error = "Fatal error(SIGABRT)";
        break;

      default:
        error = "Unknown";
    }

    LOGE("Fatal error: ", error);
    wf::print_trace(false);
    std::_Exit(-1);
}

#endif

static std::optional<std::string> choose_socket(wl_display *display)
{
    for (int i = 1; i <= 32; i++)
    {
        auto name = "wayland-" + std::to_string(i);
        if (wl_display_add_socket(display, name.c_str()) >= 0)
        {
            return name;
        }
    }

    return {};
}

int main(int argc, char *argv[])
{
    config_dir = nonull(getenv("XDG_CONFIG_HOME"));
    if (!config_dir.compare("nil"))
    {
        config_dir = std::string(nonull(getenv("HOME"))) + "/.config";
    }

    config_file = config_dir + "/wayfire.ini";

    wf::log::log_level_t log_level = wf::log::LOG_LEVEL_INFO;
    struct option opts[] = {
        {
            "config", required_argument, NULL, 'c'
        },
        {"debug", no_argument, NULL, 'd'},
        {"damage-debug", no_argument, NULL, 'D'},
        {"damage-rerender", no_argument, NULL, 'R'},
        {"help", no_argument, NULL, 'h'},
        {"version", no_argument, NULL, 'v'},
        {0, 0, NULL, 0}
    };

    int c, i;
    while ((c = getopt_long(argc, argv, "c:dDhRv", opts, &i)) != -1)
    {
        switch (c)
        {
          case 'c':
            config_file = optarg;
            break;

          case 'D':
            runtime_config.damage_debug = true;
            break;

          case 'R':
            runtime_config.no_damage_track = true;
            break;

          case 'h':
            print_help();
            break;

          case 'd':
            log_level = wf::log::LOG_LEVEL_DEBUG;
            break;

          case 'v':
            print_version();
            break;

          default:
            std::cerr << "Unrecognized command line argument " << optarg << "\n" <<
                std::endl;
        }
    }

    auto wlr_log_level =
        (log_level == wf::log::LOG_LEVEL_DEBUG ? WLR_DEBUG : WLR_ERROR);
    wlr_log_init(wlr_log_level, wlr_log_handler);
    wf::log::initialize_logging(std::cout, log_level, detect_color_mode());

#ifndef ASAN_ENABLED
    /* In case of crash, print the stacktrace for debugging.
     * However, if ASAN is enabled, we'll get better stacktrace from there. */
    signal(SIGSEGV, signal_handler);
    signal(SIGFPE, signal_handler);
    signal(SIGABRT, signal_handler);
#endif

    LOGI("Starting wayfire version ", WAYFIRE_VERSION);
    /* First create display and initialize safe-list's event loop, so that
     * wf objects (which depend on safe-list) can work */
    auto display = wl_display_create();
    wf::_safe_list_detail::event_loop = wl_display_get_event_loop(display);

    auto& core = wf::get_core_impl();

    /** TODO: move this to core_impl constructor */
    core.display  = display;
    core.ev_loop  = wl_display_get_event_loop(core.display);
    core.backend  = wlr_backend_autocreate(core.display, add_egl_depth_renderer);
    core.renderer = wlr_backend_get_renderer(core.backend);
    core.egl = egl_for_renderer[core.renderer];
    assert(core.egl);

    if (!drop_permissions())
    {
        wl_display_destroy_clients(core.display);
        wl_display_destroy(core.display);

        return EXIT_FAILURE;
    }

    std::vector<std::string> xmldirs;
    if (char *plugin_xml_path = getenv("WAYFIRE_PLUGIN_XML_PATH"))
    {
        std::stringstream ss(plugin_xml_path);
        std::string entry;
        while (std::getline(ss, entry, ':'))
        {
            xmldirs.push_back(entry);
        }
    }

    xmldirs.push_back(PLUGIN_XML_DIR);

    LOGI("using config file: ", config_file.c_str());
    core.config = wf::config::build_configuration(
        xmldirs, SYSCONFDIR "/wayfire/defaults.ini", config_file);

    int inotify_fd = inotify_init1(IN_CLOEXEC);
    reload_config(inotify_fd);

    wl_event_loop_add_fd(core.ev_loop, inotify_fd, WL_EVENT_READABLE,
        handle_config_updated, NULL);
    core.init();

    auto socket = choose_socket(core.display);
    if (!socket)
    {
        LOGE("Failed to create wayland socket, exiting.");

        return -1;
    }

    core.wayland_display = socket.value();
    LOGI("Using socket name ", core.wayland_display);
    if (!wlr_backend_start(core.backend))
    {
        LOGE("Failed to initialize backend, exiting");
        wlr_backend_destroy(core.backend);
        wl_display_destroy(core.display);

        return -1;
    }

    setenv("WAYLAND_DISPLAY", core.wayland_display.c_str(), 1);
    wl_display_run(core.display);

    /* Teardown */
    wl_display_destroy_clients(core.display);
    wl_display_destroy(core.display);

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#include <QApplication>
#include <QDir>
#include <QDesktopServices>
#include <QSettings>
#include <QDate>
#include <QIcon>
#include <QPointer>

#include "window.h"
#include "tracker.h"

QPointer<Window> window;

void redirectLogMessages(QtMsgType type, const char *msg)
{
  if(window) {
    window->addLogEntry(type, msg);
  }
}

int main(int argc, char **argv)
{
  qInstallMsgHandler(redirectLogMessages);

  // Basic setup
  QApplication app(argc, argv);
  QIcon icon = QIcon(":/icons/paw.svg");
  app.setApplicationName(APP_NAME); // for proper DataLocation handling
  app.setOrganizationName("spidy.ch");
  app.setOrganizationDomain("spidy.ch");
  app.setWindowIcon(icon);

  // Logging
  // Non-generic DataLocation includes the organization name, which we don't want
  QString dataLocation = QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QDir::separator() + app.applicationName();
  if(!QFile::exists(dataLocation)) {
    QDir dir;
    dir.mkpath(dataLocation);
  }
  string logFilePath = (dataLocation + QDir::separator() + app.applicationName() + ".log").toStdString();
  Logger::Instance()->SetLogPath(logFilePath);

  // Start
  LOG("--> Launched On %s", QDate::currentDate().toString(Qt::ISODate).toStdString().c_str());

  // Initalize Windows n stuff
  window = new Window();

  // Main Loop
  int exitCode = app.exec();

  // Tear down
  LOG("<-- Shutdown");

  delete window;

  return exitCode;
}
<commit_msg>fix log path<commit_after>#include <QApplication>
#include <QDir>
#include <QDesktopServices>
#include <QSettings>
#include <QDate>
#include <QIcon>
#include <QPointer>

#include "window.h"
#include "tracker.h"

QPointer<Window> window;

void redirectLogMessages(QtMsgType type, const char *msg)
{
  if(window) {
    window->addLogEntry(type, msg);
  }
}

int main(int argc, char **argv)
{
  qInstallMsgHandler(redirectLogMessages);

  // Basic setup
  QApplication app(argc, argv);
  QIcon icon = QIcon(":/icons/paw.svg");
  app.setApplicationName(APP_NAME); // for proper DataLocation handling
  app.setOrganizationName("spidy.ch");
  app.setOrganizationDomain("spidy.ch");
  app.setWindowIcon(icon);

  // Logging
  QString dataLocation = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
  if(!QFile::exists(dataLocation)) {
    QDir dir;
    dir.mkpath(dataLocation);
  }
  string logFilePath = (dataLocation + QDir::separator() + app.applicationName() + ".log").toStdString();
  Logger::Instance()->SetLogPath(logFilePath);

  // Start
  LOG("--> Launched On %s", QDate::currentDate().toString(Qt::ISODate).toStdString().c_str());

  // Initalize Windows n stuff
  window = new Window();

  // Main Loop
  int exitCode = app.exec();

  // Tear down
  LOG("<-- Shutdown");

  delete window;

  return exitCode;
}
<|endoftext|>