<commit_before>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#include "BaseTestBlock.hpp"

#include <iterator>
#include <boost/math/constants/constants.hpp>

#include <nix/hydra/multiArray.hpp>

#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/CompilerOutputter.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TestRunner.h>
#include <cppunit/BriefTestProgressListener.h>

using namespace nix;
using namespace valid;

void BaseTestBlock::testValidate() {
    valid::Result result = validate(block);
    CPPUNIT_ASSERT(result.getErrors().size() == 0);
    CPPUNIT_ASSERT(result.getWarnings().size() == 0);
}

void BaseTestBlock::testId() {
    CPPUNIT_ASSERT(block.id().size() == 36);
    CPPUNIT_ASSERT(block_fs.id().size() == 36);
}

void BaseTestBlock::testName() {
    CPPUNIT_ASSERT(block.name() == "block_one");
    CPPUNIT_ASSERT(block_fs.name() == "block_one");
}

void BaseTestBlock::testType() {
    CPPUNIT_ASSERT(block.type() == "dataset");
    std::string typ = util::createId();
    block.type(typ);
    CPPUNIT_ASSERT(block.type() == typ);

    CPPUNIT_ASSERT(block_fs.type() == "dataset");
    block_fs.type(typ);
    CPPUNIT_ASSERT(block_fs.type() == typ);
}

void BaseTestBlock::testDefinition() {
    std::string def = util::createId();
    block.definition(def);
    CPPUNIT_ASSERT(*block.definition() == def);

    block_fs.definition(def);
    CPPUNIT_ASSERT(*block_fs.definition() == def);
}

void BaseTestBlock::testMetadataAccess() {
    CPPUNIT_ASSERT(!block.metadata());
    block.metadata(section);
    CPPUNIT_ASSERT(block.metadata());
    // test none-unsetter
    block.metadata(none);
    CPPUNIT_ASSERT(!block.metadata());
    // test deleter removing link too
    block.metadata(section);
    file.deleteSection(section.id());
    CPPUNIT_ASSERT(!block.metadata());
    // re-create section
    section = file.createSection("foo_section", "metadata");
}

void BaseTestBlock::testSourceAccess() {
    std::vector<std::string> names = { "source_a", "source_b", "source_c", "source_d", "source_e" };
    Source s;
    CPPUNIT_ASSERT(!block.hasSource(s));
    CPPUNIT_ASSERT(block.sourceCount() == 0);
    CPPUNIT_ASSERT(block.sources().size() == 0);
    CPPUNIT_ASSERT(block.getSource("invalid_id") == false);
    CPPUNIT_ASSERT(!block.hasSource("invalid_id"));
    CPPUNIT_ASSERT(block.getSource(block.sourceCount() + 10) == nullptr);

    std::vector<std::string> ids;
    for (const auto &name : names) {
        Source src = block.createSource(name, "channel");
        CPPUNIT_ASSERT(src.name() == name);
        CPPUNIT_ASSERT(block.hasSource(name));
        CPPUNIT_ASSERT(block.hasSource(src));

        ids.push_back(src.id());
    }
    CPPUNIT_ASSERT_THROW(block.createSource(names[0], "channel"),
                         DuplicateName);
    CPPUNIT_ASSERT_THROW(block.createSource("", "test"), nix::EmptyString);

    CPPUNIT_ASSERT(block.sourceCount() == names.size());
    CPPUNIT_ASSERT(block.sources().size() == names.size());
    CPPUNIT_ASSERT_THROW(block.getSource(block.sourceCount() + 10), OutOfBounds);

    for (const auto &id : ids) {
        Source src = block.getSource(id);
        CPPUNIT_ASSERT(block.hasSource(id) == true);
        CPPUNIT_ASSERT(src.id() == id);
        block.deleteSource(id);
    }

    s = b.createSource("test", "test");
    CPPUNIT_ASSERT(block.sourceCount() == 1);
    CPPUNIT_ASSERT_NO_THROW(block.deleteSource(s));

    CPPUNIT_ASSERT(block.sourceCount() == 0);
    CPPUNIT_ASSERT(block.sources().size() == 0);
    CPPUNIT_ASSERT(block.getSource("invalid_id") == false);
}

void BaseTestBlock::testDataArrayAccess() {
    std::vector<std::string> names = { "data_array_a", "data_array_b", "data_array_c",
                             "data_array_d", "data_array_e" };
    DataArray data_array, a;

    CPPUNIT_ASSERT(block.dataArrayCount() == 0);
    CPPUNIT_ASSERT(block.dataArrays().size() == 0);
    CPPUNIT_ASSERT(block.getDataArray("invalid_id") == false);

    std::vector<std::string> ids;
    for (const auto &name : names) {
        data_array = block.createDataArray(name, "channel", DataType::Double, nix::NDSize({ 0 }));
        CPPUNIT_ASSERT(data_array.name() == name);
        CPPUNIT_ASSERT(data_array.type() == "channel");
        ids.push_back(data_array.id());
    }

    CPPUNIT_ASSERT_THROW(block.createDataArray(names[0], "channel", DataType::Double, nix::NDSize({ 0 })), DuplicateName);
    CPPUNIT_ASSERT_THROW(block.createDataArray("", "channel", DataType::Double, nix::NDSize({ 0 })), EmptyString);

    CPPUNIT_ASSERT(block.hasDataArray(data_array));
    CPPUNIT_ASSERT(!block.hasDataArray(a));
    CPPUNIT_ASSERT(block.dataArrayCount() == names.size());
    CPPUNIT_ASSERT(block.dataArrays().size() == names.size());

    for (const auto &name : names) {
        DataArray da_name = block.getDataArray(name);
        CPPUNIT_ASSERT(da_name);
        DataArray da_id = block.getDataArray(da_name.id());
        CPPUNIT_ASSERT(da_id);
        CPPUNIT_ASSERT_EQUAL(da_name.name(), da_id.name());
    }

    std::vector<DataArray> filteredArrays = block.dataArrays(util::TypeFilter<DataArray>("channel"));
    CPPUNIT_ASSERT_EQUAL(names.size(), filteredArrays.size());

    filteredArrays = block.dataArrays(util::NameFilter<DataArray>("data_array_c"));
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), filteredArrays.size());
    if (filteredArrays.size() > 0) {
        boost::optional<std::string> name = filteredArrays[0].name();
        CPPUNIT_ASSERT(name && *name == "data_array_c");
    }

    for (auto it = ids.begin(); it != ids.end(); it++) {
        DataArray data_array = block.getDataArray(*it);
        CPPUNIT_ASSERT(block.hasDataArray(*it) == true);
        CPPUNIT_ASSERT(data_array.id() == *it);

        block.deleteDataArray(*it);
    }
    CPPUNIT_ASSERT(!block.deleteDataArray(a));
    CPPUNIT_ASSERT(block.dataArrayCount() == 0);
    CPPUNIT_ASSERT(block.dataArrays().size() == 0);
    CPPUNIT_ASSERT(block.getDataArray("invalid_id") == false);
}

void BaseTestBlock::testTagAccess() {
    std::vector<std::string> names = { "tag_a", "tag_b", "tag_c", "tag_d", "tag_e" };
    std::vector<std::string> array_names = { "data_array_a", "data_array_b", "data_array_c",
                                   "data_array_d", "data_array_e" };
    std::vector<DataArray> refs;
    Tag tag, t;
    for (const auto &name : array_names) {
        refs.push_back(block.createDataArray(name,
                                         "reference",
                                         DataType::Double,
                                         nix::NDSize({ 0 })));
    }

    CPPUNIT_ASSERT(block.tagCount() == 0);
    CPPUNIT_ASSERT(block.tags().size() == 0);
    CPPUNIT_ASSERT(block.getTag("invalid_id") == false);
    CPPUNIT_ASSERT(!block.hasTag(t));

    std::vector<std::string> ids;
    for (auto it = names.begin(); it != names.end(); ++it) {
        tag = block.createTag(*it, "segment", {0.0, 2.0, 3.4});
        tag.references(refs);
        CPPUNIT_ASSERT(tag.name() == *it);
        CPPUNIT_ASSERT(block.hasTag(tag));
        ids.push_back(tag.id());
    }
    CPPUNIT_ASSERT_THROW(block.createTag(names[0], "segment", {0.0, 2.0, 3.4}),
                         DuplicateName);

    CPPUNIT_ASSERT(block.tagCount() == names.size());
    CPPUNIT_ASSERT(block.tags().size() == names.size());

    for (auto it = ids.begin(); it != ids.end(); ++it) {
        tag = block.getTag(*it);
        CPPUNIT_ASSERT(block.hasTag(*it) == true);
        CPPUNIT_ASSERT(tag.id() == *it);

        block.deleteTag(*it);
    }

    tag = block.createTag("test", "test", {0.0});
    CPPUNIT_ASSERT(block.hasTag(tag));
    CPPUNIT_ASSERT(block.deleteTag(tag));
    CPPUNIT_ASSERT(!block.deleteTag(t));

    CPPUNIT_ASSERT(block.tagCount() == 0);
    CPPUNIT_ASSERT(block.tags().size() == 0);
    CPPUNIT_ASSERT(block.getTag("invalid_id") == false);
}

void BaseTestBlock::testMultiTagAccess() {
    std::vector<std::string> names = { "tag_a", "tag_b", "tag_c", "tag_d", "tag_e" };
    MultiTag mtag, m;
    // create a valid positions data array below
    typedef boost::multi_array<double, 3>::index index;
    DataArray positions = b.createDataArray("array_one",
                                            "testdata",
                                            DataType::Double,
                                            nix::NDSize({ 3, 4, 2 }));

    boost::multi_array<double, 3> A(boost::extents[3][4][2]);
    int values = 0;
    for(index i = 0; i != 3; ++i)
        for(index j = 0; j != 4; ++j)
            for(index k = 0; k != 2; ++k)
                A[i][j][k] = values++;

    positions.setData(A);

    CPPUNIT_ASSERT(block.multiTagCount() == 0);
    CPPUNIT_ASSERT(block.multiTags().size() == 0);
    CPPUNIT_ASSERT(block.getMultiTag("invalid_id") == false);
    CPPUNIT_ASSERT(!block.hasMultiTag(m));
    std::vector<std::string> ids;
    for (auto it = names.begin(); it != names.end(); it++) {
        mtag = block.createMultiTag(*it, "segment", positions);
        CPPUNIT_ASSERT(mtag.name() == *it);
        CPPUNIT_ASSERT(block.hasMultiTag(mtag));
        ids.push_back(mtag.id());
    }
    CPPUNIT_ASSERT_THROW(block.createMultiTag(names[0], "segment", positions),
                         DuplicateName);

    CPPUNIT_ASSERT(block.multiTagCount() == names.size());
    CPPUNIT_ASSERT(block.multiTags().size() == names.size());

    for (auto it = ids.begin(); it != ids.end(); it++) {
        mtag = block.getMultiTag(*it);
        CPPUNIT_ASSERT(block.hasMultiTag(*it) == true);
        CPPUNIT_ASSERT(mtag.id() == *it);

        block.deleteMultiTag(*it);
    }
    mtag = block.createMultiTag("test", "test", positions);
    CPPUNIT_ASSERT(block.hasMultiTag(mtag));
    CPPUNIT_ASSERT(!block.deleteMultiTag(m));
    CPPUNIT_ASSERT(block.deleteMultiTag(mtag));
    CPPUNIT_ASSERT(block.multiTagCount() == 0);
    CPPUNIT_ASSERT(block.multiTags().size() == 0);
    CPPUNIT_ASSERT(block.getMultiTag("invalid_id") == false);
}

void BaseTestBlock::testGroupAccess() {
    std::vector<std::string> names = { "group_a", "group_b", "group_c", "group_d", "group_e" };
    Group g;
    CPPUNIT_ASSERT(!block.hasGroup(g));
    CPPUNIT_ASSERT(block.groupCount() == 0);
    CPPUNIT_ASSERT(block.groups().size() == 0);
    CPPUNIT_ASSERT(block.getGroup("invalid_id") == false);
    CPPUNIT_ASSERT(!block.hasGroup("invalid_id"));

    std::vector<std::string> ids;
    for (const auto &name : names) {
        Group gr = block.createGroup(name, "group");
        CPPUNIT_ASSERT(gr.name() == name);
        CPPUNIT_ASSERT(block.hasGroup(name));
        CPPUNIT_ASSERT(block.hasGroup(gr));

        ids.push_back(gr.id());
    }
    CPPUNIT_ASSERT_THROW(block.createGroup(names[0], "group"),
                         DuplicateName);

    CPPUNIT_ASSERT(block.groupCount() == names.size());
    CPPUNIT_ASSERT(block.groups().size() == names.size());

    for (const auto &id : ids) {
        Group gr = block.getGroup(id);
        CPPUNIT_ASSERT(block.hasGroup(id) == true);
        CPPUNIT_ASSERT(gr.id() == id);
        block.deleteGroup(id);
    }

    g = block.createGroup("test", "test");
    CPPUNIT_ASSERT(block.groupCount() == 1);
    CPPUNIT_ASSERT(block.deleteGroup(g));

    CPPUNIT_ASSERT(block.groupCount() == 0);
    CPPUNIT_ASSERT(block.groups().size() == 0);
    CPPUNIT_ASSERT(block.getGroup("invalid_id") == false);
}

void BaseTestBlock::testOperators() {
    CPPUNIT_ASSERT(block_null == false);
    CPPUNIT_ASSERT(block_null == none);

    CPPUNIT_ASSERT(block != false);
    CPPUNIT_ASSERT(block != none);

    CPPUNIT_ASSERT(block == block);
    CPPUNIT_ASSERT(block != block_other);

    block_other = block;
    CPPUNIT_ASSERT(block == block_other);

    block_other = none;
    CPPUNIT_ASSERT(block_null == false);
    CPPUNIT_ASSERT(block_null == none);

    std::stringstream str;
    str << block;
}

void BaseTestBlock::testCreatedAt() {
    CPPUNIT_ASSERT(block.createdAt() >= startup_time);
    time_t past_time = time(NULL) - 10000000;
    block.forceCreatedAt(past_time);
    CPPUNIT_ASSERT(block.createdAt() == past_time);
}

void BaseTestBlock::testUpdatedAt() {
    CPPUNIT_ASSERT(block.updatedAt() >= startup_time);
}

void BaseTestBlock::testCompare() {
    std::string other_name = block_other.name();
    std::string block_name = block.name();

    CPPUNIT_ASSERT(block.compare(block) == 0);
    CPPUNIT_ASSERT(block.compare(block_other) ==  block_name.compare(other_name));
}
<commit_msg>[testBlock] remove data part from testMultiTag, wrong place and not needed<commit_after>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#include "BaseTestBlock.hpp"

#include <iterator>
#include <boost/math/constants/constants.hpp>

#include <nix/hydra/multiArray.hpp>

#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/CompilerOutputter.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TestRunner.h>
#include <cppunit/BriefTestProgressListener.h>

using namespace nix;
using namespace valid;

void BaseTestBlock::testValidate() {
    valid::Result result = validate(block);
    CPPUNIT_ASSERT(result.getErrors().size() == 0);
    CPPUNIT_ASSERT(result.getWarnings().size() == 0);
}

void BaseTestBlock::testId() {
    CPPUNIT_ASSERT(block.id().size() == 36);
    CPPUNIT_ASSERT(block_fs.id().size() == 36);
}

void BaseTestBlock::testName() {
    CPPUNIT_ASSERT(block.name() == "block_one");
    CPPUNIT_ASSERT(block_fs.name() == "block_one");
}

void BaseTestBlock::testType() {
    CPPUNIT_ASSERT(block.type() == "dataset");
    std::string typ = util::createId();
    block.type(typ);
    CPPUNIT_ASSERT(block.type() == typ);

    CPPUNIT_ASSERT(block_fs.type() == "dataset");
    block_fs.type(typ);
    CPPUNIT_ASSERT(block_fs.type() == typ);
}

void BaseTestBlock::testDefinition() {
    std::string def = util::createId();
    block.definition(def);
    CPPUNIT_ASSERT(*block.definition() == def);

    block_fs.definition(def);
    CPPUNIT_ASSERT(*block_fs.definition() == def);
}

void BaseTestBlock::testMetadataAccess() {
    CPPUNIT_ASSERT(!block.metadata());
    block.metadata(section);
    CPPUNIT_ASSERT(block.metadata());
    // test none-unsetter
    block.metadata(none);
    CPPUNIT_ASSERT(!block.metadata());
    // test deleter removing link too
    block.metadata(section);
    file.deleteSection(section.id());
    CPPUNIT_ASSERT(!block.metadata());
    // re-create section
    section = file.createSection("foo_section", "metadata");
}

void BaseTestBlock::testSourceAccess() {
    std::vector<std::string> names = { "source_a", "source_b", "source_c", "source_d", "source_e" };
    Source s;
    CPPUNIT_ASSERT(!block.hasSource(s));
    CPPUNIT_ASSERT(block.sourceCount() == 0);
    CPPUNIT_ASSERT(block.sources().size() == 0);
    CPPUNIT_ASSERT(block.getSource("invalid_id") == false);
    CPPUNIT_ASSERT(!block.hasSource("invalid_id"));
    CPPUNIT_ASSERT(block.getSource(block.sourceCount() + 10) == nullptr);

    std::vector<std::string> ids;
    for (const auto &name : names) {
        Source src = block.createSource(name, "channel");
        CPPUNIT_ASSERT(src.name() == name);
        CPPUNIT_ASSERT(block.hasSource(name));
        CPPUNIT_ASSERT(block.hasSource(src));

        ids.push_back(src.id());
    }
    CPPUNIT_ASSERT_THROW(block.createSource(names[0], "channel"),
                         DuplicateName);
    CPPUNIT_ASSERT_THROW(block.createSource("", "test"), nix::EmptyString);

    CPPUNIT_ASSERT(block.sourceCount() == names.size());
    CPPUNIT_ASSERT(block.sources().size() == names.size());
    CPPUNIT_ASSERT_THROW(block.getSource(block.sourceCount() + 10), OutOfBounds);

    for (const auto &id : ids) {
        Source src = block.getSource(id);
        CPPUNIT_ASSERT(block.hasSource(id) == true);
        CPPUNIT_ASSERT(src.id() == id);
        block.deleteSource(id);
    }

    s = b.createSource("test", "test");
    CPPUNIT_ASSERT(block.sourceCount() == 1);
    CPPUNIT_ASSERT_NO_THROW(block.deleteSource(s));

    CPPUNIT_ASSERT(block.sourceCount() == 0);
    CPPUNIT_ASSERT(block.sources().size() == 0);
    CPPUNIT_ASSERT(block.getSource("invalid_id") == false);
}

void BaseTestBlock::testDataArrayAccess() {
    std::vector<std::string> names = { "data_array_a", "data_array_b", "data_array_c",
                             "data_array_d", "data_array_e" };
    DataArray data_array, a;

    CPPUNIT_ASSERT(block.dataArrayCount() == 0);
    CPPUNIT_ASSERT(block.dataArrays().size() == 0);
    CPPUNIT_ASSERT(block.getDataArray("invalid_id") == false);

    std::vector<std::string> ids;
    for (const auto &name : names) {
        data_array = block.createDataArray(name, "channel", DataType::Double, nix::NDSize({ 0 }));
        CPPUNIT_ASSERT(data_array.name() == name);
        CPPUNIT_ASSERT(data_array.type() == "channel");
        ids.push_back(data_array.id());
    }

    CPPUNIT_ASSERT_THROW(block.createDataArray(names[0], "channel", DataType::Double, nix::NDSize({ 0 })), DuplicateName);
    CPPUNIT_ASSERT_THROW(block.createDataArray("", "channel", DataType::Double, nix::NDSize({ 0 })), EmptyString);

    CPPUNIT_ASSERT(block.hasDataArray(data_array));
    CPPUNIT_ASSERT(!block.hasDataArray(a));
    CPPUNIT_ASSERT(block.dataArrayCount() == names.size());
    CPPUNIT_ASSERT(block.dataArrays().size() == names.size());

    for (const auto &name : names) {
        DataArray da_name = block.getDataArray(name);
        CPPUNIT_ASSERT(da_name);
        DataArray da_id = block.getDataArray(da_name.id());
        CPPUNIT_ASSERT(da_id);
        CPPUNIT_ASSERT_EQUAL(da_name.name(), da_id.name());
    }

    std::vector<DataArray> filteredArrays = block.dataArrays(util::TypeFilter<DataArray>("channel"));
    CPPUNIT_ASSERT_EQUAL(names.size(), filteredArrays.size());

    filteredArrays = block.dataArrays(util::NameFilter<DataArray>("data_array_c"));
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), filteredArrays.size());
    if (filteredArrays.size() > 0) {
        boost::optional<std::string> name = filteredArrays[0].name();
        CPPUNIT_ASSERT(name && *name == "data_array_c");
    }

    for (auto it = ids.begin(); it != ids.end(); it++) {
        DataArray data_array = block.getDataArray(*it);
        CPPUNIT_ASSERT(block.hasDataArray(*it) == true);
        CPPUNIT_ASSERT(data_array.id() == *it);

        block.deleteDataArray(*it);
    }
    CPPUNIT_ASSERT(!block.deleteDataArray(a));
    CPPUNIT_ASSERT(block.dataArrayCount() == 0);
    CPPUNIT_ASSERT(block.dataArrays().size() == 0);
    CPPUNIT_ASSERT(block.getDataArray("invalid_id") == false);
}

void BaseTestBlock::testTagAccess() {
    std::vector<std::string> names = { "tag_a", "tag_b", "tag_c", "tag_d", "tag_e" };
    std::vector<std::string> array_names = { "data_array_a", "data_array_b", "data_array_c",
                                   "data_array_d", "data_array_e" };
    std::vector<DataArray> refs;
    Tag tag, t;
    for (const auto &name : array_names) {
        refs.push_back(block.createDataArray(name,
                                         "reference",
                                         DataType::Double,
                                         nix::NDSize({ 0 })));
    }

    CPPUNIT_ASSERT(block.tagCount() == 0);
    CPPUNIT_ASSERT(block.tags().size() == 0);
    CPPUNIT_ASSERT(block.getTag("invalid_id") == false);
    CPPUNIT_ASSERT(!block.hasTag(t));

    std::vector<std::string> ids;
    for (auto it = names.begin(); it != names.end(); ++it) {
        tag = block.createTag(*it, "segment", {0.0, 2.0, 3.4});
        tag.references(refs);
        CPPUNIT_ASSERT(tag.name() == *it);
        CPPUNIT_ASSERT(block.hasTag(tag));
        ids.push_back(tag.id());
    }
    CPPUNIT_ASSERT_THROW(block.createTag(names[0], "segment", {0.0, 2.0, 3.4}),
                         DuplicateName);

    CPPUNIT_ASSERT(block.tagCount() == names.size());
    CPPUNIT_ASSERT(block.tags().size() == names.size());

    for (auto it = ids.begin(); it != ids.end(); ++it) {
        tag = block.getTag(*it);
        CPPUNIT_ASSERT(block.hasTag(*it) == true);
        CPPUNIT_ASSERT(tag.id() == *it);

        block.deleteTag(*it);
    }

    tag = block.createTag("test", "test", {0.0});
    CPPUNIT_ASSERT(block.hasTag(tag));
    CPPUNIT_ASSERT(block.deleteTag(tag));
    CPPUNIT_ASSERT(!block.deleteTag(t));

    CPPUNIT_ASSERT(block.tagCount() == 0);
    CPPUNIT_ASSERT(block.tags().size() == 0);
    CPPUNIT_ASSERT(block.getTag("invalid_id") == false);
}

void BaseTestBlock::testMultiTagAccess() {
    std::vector<std::string> names = { "tag_a", "tag_b", "tag_c", "tag_d", "tag_e" };
    MultiTag mtag, m;
    // create a valid positions data array below
    DataArray positions = b.createDataArray("array_one",
                                            "testdata",
                                            DataType::Double,
                                            nix::NDSize({ 3, 4, 2 }));

    CPPUNIT_ASSERT(block.multiTagCount() == 0);
    CPPUNIT_ASSERT(block.multiTags().size() == 0);
    CPPUNIT_ASSERT(block.getMultiTag("invalid_id") == false);
    CPPUNIT_ASSERT(!block.hasMultiTag(m));
    std::vector<std::string> ids;
    for (auto it = names.begin(); it != names.end(); it++) {
        mtag = block.createMultiTag(*it, "segment", positions);
        CPPUNIT_ASSERT(mtag.name() == *it);
        CPPUNIT_ASSERT(block.hasMultiTag(mtag));
        ids.push_back(mtag.id());
    }
    CPPUNIT_ASSERT_THROW(block.createMultiTag(names[0], "segment", positions),
                         DuplicateName);

    CPPUNIT_ASSERT(block.multiTagCount() == names.size());
    CPPUNIT_ASSERT(block.multiTags().size() == names.size());

    for (auto it = ids.begin(); it != ids.end(); it++) {
        mtag = block.getMultiTag(*it);
        CPPUNIT_ASSERT(block.hasMultiTag(*it) == true);
        CPPUNIT_ASSERT(mtag.id() == *it);

        block.deleteMultiTag(*it);
    }
    mtag = block.createMultiTag("test", "test", positions);
    CPPUNIT_ASSERT(block.hasMultiTag(mtag));
    CPPUNIT_ASSERT(!block.deleteMultiTag(m));
    CPPUNIT_ASSERT(block.deleteMultiTag(mtag));
    CPPUNIT_ASSERT(block.multiTagCount() == 0);
    CPPUNIT_ASSERT(block.multiTags().size() == 0);
    CPPUNIT_ASSERT(block.getMultiTag("invalid_id") == false);
}

void BaseTestBlock::testGroupAccess() {
    std::vector<std::string> names = { "group_a", "group_b", "group_c", "group_d", "group_e" };
    Group g;
    CPPUNIT_ASSERT(!block.hasGroup(g));
    CPPUNIT_ASSERT(block.groupCount() == 0);
    CPPUNIT_ASSERT(block.groups().size() == 0);
    CPPUNIT_ASSERT(block.getGroup("invalid_id") == false);
    CPPUNIT_ASSERT(!block.hasGroup("invalid_id"));

    std::vector<std::string> ids;
    for (const auto &name : names) {
        Group gr = block.createGroup(name, "group");
        CPPUNIT_ASSERT(gr.name() == name);
        CPPUNIT_ASSERT(block.hasGroup(name));
        CPPUNIT_ASSERT(block.hasGroup(gr));

        ids.push_back(gr.id());
    }
    CPPUNIT_ASSERT_THROW(block.createGroup(names[0], "group"),
                         DuplicateName);

    CPPUNIT_ASSERT(block.groupCount() == names.size());
    CPPUNIT_ASSERT(block.groups().size() == names.size());

    for (const auto &id : ids) {
        Group gr = block.getGroup(id);
        CPPUNIT_ASSERT(block.hasGroup(id) == true);
        CPPUNIT_ASSERT(gr.id() == id);
        block.deleteGroup(id);
    }

    g = block.createGroup("test", "test");
    CPPUNIT_ASSERT(block.groupCount() == 1);
    CPPUNIT_ASSERT(block.deleteGroup(g));

    CPPUNIT_ASSERT(block.groupCount() == 0);
    CPPUNIT_ASSERT(block.groups().size() == 0);
    CPPUNIT_ASSERT(block.getGroup("invalid_id") == false);
}

void BaseTestBlock::testOperators() {
    CPPUNIT_ASSERT(block_null == false);
    CPPUNIT_ASSERT(block_null == none);

    CPPUNIT_ASSERT(block != false);
    CPPUNIT_ASSERT(block != none);

    CPPUNIT_ASSERT(block == block);
    CPPUNIT_ASSERT(block != block_other);

    block_other = block;
    CPPUNIT_ASSERT(block == block_other);

    block_other = none;
    CPPUNIT_ASSERT(block_null == false);
    CPPUNIT_ASSERT(block_null == none);

    std::stringstream str;
    str << block;
}

void BaseTestBlock::testCreatedAt() {
    CPPUNIT_ASSERT(block.createdAt() >= startup_time);
    time_t past_time = time(NULL) - 10000000;
    block.forceCreatedAt(past_time);
    CPPUNIT_ASSERT(block.createdAt() == past_time);
}

void BaseTestBlock::testUpdatedAt() {
    CPPUNIT_ASSERT(block.updatedAt() >= startup_time);
}

void BaseTestBlock::testCompare() {
    std::string other_name = block_other.name();
    std::string block_name = block.name();

    CPPUNIT_ASSERT(block.compare(block) == 0);
    CPPUNIT_ASSERT(block.compare(block_other) ==  block_name.compare(other_name));
}
<|endoftext|>


<commit_before>/******************************************************************************
* Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in
*       the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
*       names of its contributors may be used to endorse or promote
*       products derived from this software without specific prior
*       written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/

#ifndef INCLUDED_STAGEBASE_HPP
#define INCLUDED_STAGEBASE_HPP

#include <boost/shared_ptr.hpp>

#include <pdal/pdal_internal.hpp>
#include <pdal/Log.hpp>
#include <pdal/Options.hpp>

#include <string>
#include <vector>
#include <iosfwd>

namespace pdal
{

class Stage;

//
// supported options:
//   <uint32>id
//   <bool>debug
//   <uint32>verbose
//

// both Stages and Writers have a few common properties, so
class PDAL_DLL StageBase
{
public:

    /// Constructor.
    ///
    /// @param  inputs  The input stages.
    /// @param  options Options for controlling the pipeline stage.
    StageBase(const std::vector<StageBase*>& inputs, const Options& options);

    /// Destructor.
    virtual ~StageBase();

    /// Initializes this object.
    ///
    /// This function is for derived stages to perform "static" validation, e.g. bad Option arguments.
    /// It will recursively call initialize() on all previous stages.
    /// Users must call this after the last stage in the pipeline has been consturcted.
    /// It is illegal to call this twice for a stage.
    /// Derived stages should feel free to provide their own implementations.  Remember to call initialize() on
    ///   the parent class before your own class-specific code.
    /// This function will throw when errors are found.
    virtual void initialize();

    /// Query if this object is initialized.
    ///
    /// @return true if initialized, false if not.
    bool isInitialized() const;

    /// Gets the Options set for the stage.
    ///
    /// @return The options.
    const Options& getOptions() const;

    /// Returns the serialized pipeline.
    ///
    /// This is used to generate pipeline xml files.  It will
    /// recursively visit all child stages to populate the tree.
    ///
    /// @return the ptree for the stage
    virtual boost::property_tree::ptree serializePipeline() const = 0;

    /// Put data to the log
    /// @param input a string to put into the Stage's log
    // virtual void log(std::ostringstream& input, boost::uint32_t nVerbosity = 1) const;

    virtual LogPtr log(void) const { return m_log; }

    /// Put data to the log
    /// @param input a string to put into the Stage's log
    // virtual void log(std::string const& input, boost::uint32_t nVerbosity = 1) const;

    /// Query if this object is debug.
    ///
    /// This is set by the "debug" option, which is a boolean.
    ///
    /// This is intended to be used for adding debug code to stages, e.g. more than just the
    /// extra logging that "verbose" implies.
    ///
    /// @return true if debug, false if not.
    bool isDebug() const;

    /// Query if this object is verbose.
    ///
    /// This is set by the "verbose" option
    ///    0 - no verbosity at all
    ///    >0 - meaning is left to the implementors of the individual stages
    ///
    /// "Verbose" is intended to only add logging/tracing/output functionality; to add or enable
    /// extra validation checks and such (code which is potentially side-effecting) you want to use
    /// the "debug" option.
    ///
    /// @return true if verbosity>0, false if not.

    bool isVerbose() const;

    /// Gets the verbose level.
    ///
    /// @return The verbose level.
    boost::uint32_t getVerboseLevel() const;

    /// Gets the default options.
    ///
    /// Everyone must implement this.  If you want to access the list of
    /// options "statically", you are free to construct the stage with no
    /// arguments and cal getDefaultOptions() on it -- there is no need
    /// to call initialize(), so it should be a fast/safe operation.
    ///
    /// @return The default options.
    virtual const Options getDefaultOptions() const = 0;

    /// Gets the name.
    ///
    /// Use a dotted, XPath-style name for your
    /// stage.  For example, 'drivers.las.reader' or 'filters.crop'.  This
    /// XPath-style name will also correspond to an entry in the pdal::Options
    /// tree for the given stage.
    ///
    /// @return The name.
    virtual std::string getName() const = 0;

    /// Gets the description.
    ///
    /// @return The description.
    virtual std::string getDescription() const = 0;

    /// For getName() and getDescription(), each stage provides a static and
    /// a dynamic version of the function.  Each (concrete) stage should call
    /// the following macro to create the functions for you.

#define SET_STAGE_NAME(name, description)  \
    static std::string s_getName() { return name; }  \
    std::string getName() const { return name; }  \
    static std::string s_getDescription() { return description; }  \
    std::string getDescription() const { return description; }

    /// Converts this object to a ptree.
    ///
    /// @return This object as a boost::property_tree::ptree.
    virtual boost::property_tree::ptree toPTree() const;

    /// Dumps this object.
    virtual void dump() const;

    /// Gets the stage's id.
    ///
    /// @return The id.
    boost::uint32_t getId() const { return m_id; }

    /// Gets the list of input stages
    ///
    /// @return vector of input stages (may be empty)
    const std::vector<StageBase*>& getInputs() const;

    /// Gets the list of output stages
    ///
    /// @return vector of output stages (may be empty)
    const std::vector<StageBase*>& getOutputs() const;

    /// Gets the previous stage.
    ///
    /// convenience function: returns the first input stage, as a Stage&
    /// (don't call this unless you know it is safe to do so, e.g Readers
    /// should not use this since they have no input stages)
    ///
    /// @return The previous stage.
    Stage& getPrevStage() const;

    /// Gets the previous stages.
    ///
    /// convenience function: returns the input stages, as a Stage* vector
    /// (don't call this unless you know it is safe to do so, e.g Readers
    /// should not use this since they have no input stages, and Filters
    /// probably won't want to use this either since they only have one
    /// prev stage)
    ///
    /// @return null if it fails, else the previous stages.
    std::vector<Stage*> getPrevStages() const;

protected:

    /// Gets the options.
    ///
    /// @return The options.
    Options& getOptions();

    inline StageOperationType getDimensionOperationType() const
        { return m_dimensionsType; }
    /// Makes an "empty" vector of StageBase pointers.
    ///
    /// This is used by the ctors of the derived classes, so they can call
    /// the ctor of StageBase.  This function goes along with the other
    /// makeVector() functions.
    ///
    /// @return an empty vector
    static std::vector<StageBase*> makeVector();

    /// Makes a vector of one StageBase pointer from a single Stage reference
    ///
    /// This is used by the ctors of the derived classes, so they can call
    /// the ctor of StageBase.  C++ doesn't support polymorphic arrays or
    /// inlined initialization of vectors, so we do it this way.
    ///
    /// @param src the Stage reference
    ///
    /// @return a vector of one StageBase pointer
    static std::vector<StageBase*> makeVector(Stage& src);

    /// Makes a vector of StageBase pointers from a vector of Stages
    ///
    /// This is used by the ctors of the derived classes, so they can call
    /// the ctor of StageBase.  C++ doesn't support polymorphic arrays or
    /// inlined initialization of vectors, so we do it this way.
    ///
    /// @param src the vector of Stages
    ///
    /// @return a vector of StageBase pointers
    static std::vector<StageBase*> makeVector(const std::vector<Stage*>& src);

private:
    bool m_initialized;
    Options m_options;
    bool m_debug;
    boost::uint32_t m_verbose;
    const boost::uint32_t m_id;

    std::vector<StageBase*> m_inputs;
    std::vector<StageBase*> m_outputs;
    StageOperationType m_dimensionsType;
    LogPtr m_log;

    StageBase& operator=(const StageBase& rhs); // not implemented
    StageBase(const StageBase&); // not implemented
};

/// Output operator for serialization
///
/// @param ostr    The output stream to write to
/// @param src     The StageBase to be serialized out
///
/// @return The output stream

PDAL_DLL std::ostream& operator<<(std::ostream& ostr, const StageBase& src);

} // namespace pdal

#endif
<commit_msg>allow fetching a copy of the stage's current options with getCurrentOptions method<commit_after>/******************************************************************************
* Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in
*       the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
*       names of its contributors may be used to endorse or promote
*       products derived from this software without specific prior
*       written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/

#ifndef INCLUDED_STAGEBASE_HPP
#define INCLUDED_STAGEBASE_HPP

#include <boost/shared_ptr.hpp>

#include <pdal/pdal_internal.hpp>
#include <pdal/Log.hpp>
#include <pdal/Options.hpp>

#include <string>
#include <vector>
#include <iosfwd>

namespace pdal
{

class Stage;

//
// supported options:
//   <uint32>id
//   <bool>debug
//   <uint32>verbose
//

// both Stages and Writers have a few common properties, so
class PDAL_DLL StageBase
{
public:

    /// Constructor.
    ///
    /// @param  inputs  The input stages.
    /// @param  options Options for controlling the pipeline stage.
    StageBase(const std::vector<StageBase*>& inputs, const Options& options);

    /// Destructor.
    virtual ~StageBase();

    /// Initializes this object.
    ///
    /// This function is for derived stages to perform "static" validation, e.g. bad Option arguments.
    /// It will recursively call initialize() on all previous stages.
    /// Users must call this after the last stage in the pipeline has been consturcted.
    /// It is illegal to call this twice for a stage.
    /// Derived stages should feel free to provide their own implementations.  Remember to call initialize() on
    ///   the parent class before your own class-specific code.
    /// This function will throw when errors are found.
    virtual void initialize();

    /// Query if this object is initialized.
    ///
    /// @return true if initialized, false if not.
    bool isInitialized() const;

    /// Gets the Options set for the stage.
    ///
    /// @return The options.
    const Options& getOptions() const;

    /// Returns the serialized pipeline.
    ///
    /// This is used to generate pipeline xml files.  It will
    /// recursively visit all child stages to populate the tree.
    ///
    /// @return the ptree for the stage
    virtual boost::property_tree::ptree serializePipeline() const = 0;

    /// Put data to the log
    /// @param input a string to put into the Stage's log
    // virtual void log(std::ostringstream& input, boost::uint32_t nVerbosity = 1) const;

    virtual LogPtr log(void) const { return m_log; }

    /// Put data to the log
    /// @param input a string to put into the Stage's log
    // virtual void log(std::string const& input, boost::uint32_t nVerbosity = 1) const;

    /// Query if this object is debug.
    ///
    /// This is set by the "debug" option, which is a boolean.
    ///
    /// This is intended to be used for adding debug code to stages, e.g. more than just the
    /// extra logging that "verbose" implies.
    ///
    /// @return true if debug, false if not.
    bool isDebug() const;

    /// Query if this object is verbose.
    ///
    /// This is set by the "verbose" option
    ///    0 - no verbosity at all
    ///    >0 - meaning is left to the implementors of the individual stages
    ///
    /// "Verbose" is intended to only add logging/tracing/output functionality; to add or enable
    /// extra validation checks and such (code which is potentially side-effecting) you want to use
    /// the "debug" option.
    ///
    /// @return true if verbosity>0, false if not.

    bool isVerbose() const;

    /// Gets the verbose level.
    ///
    /// @return The verbose level.
    boost::uint32_t getVerboseLevel() const;

    /// Gets the default options.
    ///
    /// Everyone must implement this.  If you want to access the list of
    /// options "statically", you are free to construct the stage with no
    /// arguments and cal getDefaultOptions() on it -- there is no need
    /// to call initialize(), so it should be a fast/safe operation.
    ///
    /// @return The default options.
    virtual const Options getDefaultOptions() const = 0;

    /// Gets the name.
    ///
    /// Use a dotted, XPath-style name for your
    /// stage.  For example, 'drivers.las.reader' or 'filters.crop'.  This
    /// XPath-style name will also correspond to an entry in the pdal::Options
    /// tree for the given stage.
    ///
    /// @return The name.
    virtual std::string getName() const = 0;

    /// Gets the description.
    ///
    /// @return The description.
    virtual std::string getDescription() const = 0;

    /// For getName() and getDescription(), each stage provides a static and
    /// a dynamic version of the function.  Each (concrete) stage should call
    /// the following macro to create the functions for you.

#define SET_STAGE_NAME(name, description)  \
    static std::string s_getName() { return name; }  \
    std::string getName() const { return name; }  \
    static std::string s_getDescription() { return description; }  \
    std::string getDescription() const { return description; }

    /// Converts this object to a ptree.
    ///
    /// @return This object as a boost::property_tree::ptree.
    virtual boost::property_tree::ptree toPTree() const;

    /// Dumps this object.
    virtual void dump() const;

    /// Gets the stage's id.
    ///
    /// @return The id.
    boost::uint32_t getId() const { return m_id; }

    /// Gets the list of input stages
    ///
    /// @return vector of input stages (may be empty)
    const std::vector<StageBase*>& getInputs() const;

    /// Gets the list of output stages
    ///
    /// @return vector of output stages (may be empty)
    const std::vector<StageBase*>& getOutputs() const;

    /// Gets the previous stage.
    ///
    /// convenience function: returns the first input stage, as a Stage&
    /// (don't call this unless you know it is safe to do so, e.g Readers
    /// should not use this since they have no input stages)
    ///
    /// @return The previous stage.
    Stage& getPrevStage() const;

    /// Gets the previous stages.
    ///
    /// convenience function: returns the input stages, as a Stage* vector
    /// (don't call this unless you know it is safe to do so, e.g Readers
    /// should not use this since they have no input stages, and Filters
    /// probably won't want to use this either since they only have one
    /// prev stage)
    ///
    /// @return null if it fails, else the previous stages.
    std::vector<Stage*> getPrevStages() const;

    /// Gets the options.
    ///
    /// @return The options.
    inline Options getCurrentOptions() { return m_options; }

protected:

    /// Gets the options.
    ///
    /// @return The options.
    Options& getOptions();

    inline StageOperationType getDimensionOperationType() const
        { return m_dimensionsType; }
    /// Makes an "empty" vector of StageBase pointers.
    ///
    /// This is used by the ctors of the derived classes, so they can call
    /// the ctor of StageBase.  This function goes along with the other
    /// makeVector() functions.
    ///
    /// @return an empty vector
    static std::vector<StageBase*> makeVector();

    /// Makes a vector of one StageBase pointer from a single Stage reference
    ///
    /// This is used by the ctors of the derived classes, so they can call
    /// the ctor of StageBase.  C++ doesn't support polymorphic arrays or
    /// inlined initialization of vectors, so we do it this way.
    ///
    /// @param src the Stage reference
    ///
    /// @return a vector of one StageBase pointer
    static std::vector<StageBase*> makeVector(Stage& src);

    /// Makes a vector of StageBase pointers from a vector of Stages
    ///
    /// This is used by the ctors of the derived classes, so they can call
    /// the ctor of StageBase.  C++ doesn't support polymorphic arrays or
    /// inlined initialization of vectors, so we do it this way.
    ///
    /// @param src the vector of Stages
    ///
    /// @return a vector of StageBase pointers
    static std::vector<StageBase*> makeVector(const std::vector<Stage*>& src);

private:
    bool m_initialized;
    Options m_options;
    bool m_debug;
    boost::uint32_t m_verbose;
    const boost::uint32_t m_id;

    std::vector<StageBase*> m_inputs;
    std::vector<StageBase*> m_outputs;
    StageOperationType m_dimensionsType;
    LogPtr m_log;

    StageBase& operator=(const StageBase& rhs); // not implemented
    StageBase(const StageBase&); // not implemented
};

/// Output operator for serialization
///
/// @param ostr    The output stream to write to
/// @param src     The StageBase to be serialized out
///
/// @return The output stream

PDAL_DLL std::ostream& operator<<(std::ostream& ostr, const StageBase& src);

} // namespace pdal

#endif
<|endoftext|>


<commit_before>/* Definition of the pqxx::stream_to class.
 *
 * pqxx::stream_to enables optimized batch updates to a database table.
 *
 * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/stream_to.hxx instead.
 *
 * Copyright (c) 2000-2020, Jeroen T. Vermeulen.
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this
 * mistake, or contact the author.
 */
#ifndef PQXX_H_STREAM_TO
#define PQXX_H_STREAM_TO

#include "pqxx/compiler-public.hxx"
#include "pqxx/internal/compiler-internal-pre.hxx"

#include "pqxx/separated_list.hxx"
#include "pqxx/transaction_base.hxx"

#include <iostream>
namespace pqxx
{
/// Efficiently write data directly to a database table.
/** If you wish to insert rows of data into a table, you can compose INSERT
 * statements and execute them.  But it's slow and tedious, and you need to
 * worry about quoting and escaping the data.
 *
 * If you're just inserting a single row, it probably won't matter much.  You
 * can use prepared or parameterised statements to take care of the escaping
 * for you.  But if you're inserting large numbers of rows you will want
 * something better.
 *
 * Inserting rows one by one involves a lot of pointless overhead, especially
 * when you are working with a remote database server over the network.  You
 * may end up sending each row over the network as a separate query, and
 * waiting for a reply.  Do it "in bulk" using @c stream_to, and you may find
 * that it goes many times faster.  Sometimes you gain orders of magnitude in
 * speed.
 *
 * Here's how it works: you create a @c stream_to stream to start writing to
 * your table.  You will probably want to specify the columns.  Then, you
 * feed your data into the stream one row at a time.  And finally, you call the
 * stream's @c complete() to tell it to finalise the operation, wait for
 * completion, and check for errors.
 *
 * You insert data using the @c << ("shift-left") operator.  Each row must be
 * something that can be iterated in order to get its constituent fields: a
 * @c std::tuple, a @c std::vector, or anything else with a @c begin and
 * @c end.  It could be a class of your own.  Of course the fields have to
 * match the columns you specified when creating the stream.
 *
 * To insert a null value, pass a @c nullptr, or an empty @c std::optional,
 * @c std::shared_ptr, or @c std::unique_ptr.
 *
 * There is also a matching stream_from for reading data in bulk.
 */
class PQXX_LIBEXPORT stream_to : internal::transactionfocus
{
public:
  /// Create a stream, without specifying columns.
  /** Fields will be inserted in whatever order the columns have in the
   * database.
   *
   * You'll probably want to specify the columns, so that the mapping between
   * your data fields and the table is explicit in your code, and not hidden
   * in an "implicit contract" between your code and your schema.
   */
  stream_to(transaction_base &, std::string_view table_name);

  /// Create a stream, specifying column names as a container of strings.
  template<typename Columns>
  stream_to(
    transaction_base &, std::string_view table_name, Columns const &columns);

  /// Create a stream, specifying column names as a sequence of strings.
  template<typename Iter>
  stream_to(
    transaction_base &, std::string_view table_name, Iter columns_begin,
    Iter columns_end);

  ~stream_to() noexcept;

  [[nodiscard]] operator bool() const noexcept { return not m_finished; }
  [[nodiscard]] bool operator!() const noexcept { return m_finished; }

  /// Complete the operation, and check for errors.
  /** Always call this to close the stream in an orderly fashion, even after
   * an error.  (In the case of an error, abort the transaction afterwards.)
   *
   * The only circumstance where it's safe to skip this is after an error, if
   * you're discarding the entire connection.
   */
  void complete();

  /// Insert a row of data.
  /** Returns a reference to the stream, so you can chain the calls.
   *
   * The @c row can be a tuple, or any type that can be iterated.  Each
   * item becomes a field in the row, in the same order as the columns you
   * specified when creating the stream.
   */
  template<typename Row> stream_to &operator<<(Row const &row)
  {
    write_row(row);
    return *this;
  }

  /// Stream a `stream_from` straight into a `stream_to`.
  /** This can be useful when copying between different databases.  If the
   * source and the destination are on the same database, you'll get better
   * performance doing it all in a regular query.
   */
  stream_to &operator<<(stream_from &);

  /// Insert a row of data.
  /** The @c row can be a tuple, or any type that can be iterated.  Each
   * item becomes a field in the row, in the same order as the columns you
   * specified when creating the stream.
   */
  template<typename Row> void write_row(Row const &row)
  {
    fill_buffer(row);
    write_buffer();
  }

private:
  bool m_finished = false;

  /// Reusable buffer for a row.  Saves doing an allocation for each row.
  std::string m_buffer;

  /// Reusable buffer for converting/escaping a field.
  std::string m_field_buf;

  /// Write a row of raw text-format data into the destination table.
  void write_raw_line(std::string_view);

  /// Write a row of data from @c m_buffer into the destination table.
  /** Resets the buffer for the next row.
   */
  void write_buffer();

  /// COPY encoding for a null field, plus subsequent separator.
  static constexpr std::string_view null_field{"\\N\t"};

  /// Estimate buffer space needed for a field which is always null.
  template<typename T>
  static std::enable_if_t<nullness<T>::always_null, std::size_t>
  estimate_buffer(T const &)
  {
    return null_field.size();
  }

  /// Estimate buffer space needed for field f.
  /** The estimate is not very precise.  We don't actually know how much space
   * we'll need once the escaping comes in.
   */
  template<typename T>
  static std::enable_if_t<not nullness<T>::always_null, std::size_t>
  estimate_buffer(T const &field)
  {
    return is_null(field) ? null_field.size() :
                            string_traits<T>::size_buffer(field);
  }

  /// Append escaped version of @c m_field_buf to @c m_buffer, plus a tab.
  void escape_field_to_buffer(std::string_view);

  /// Append string representation for @c f to @c m_buffer.
  /** This is for the general case, where the field may contain a value.
   *
   * Also appends a tab.  The tab is meant to be a separator, not a terminator,
   * so if you write any fields at all, you'll end up with one tab too many
   * at the end of the buffer.
   */
  template<typename Field>
  std::enable_if_t<not nullness<Field>::always_null>
  append_to_buffer(Field const &f)
  {
    // We append each field, terminated by a tab.  That will leave us with
    // one tab too many, assuming we write any fields at all; we remove that
    // at the end.
    if (is_null(f))
    {
      // Easy.  Append null and tab in one go.
      m_buffer.append(null_field);
    }
    else
    {
      // Convert f into m_buffer.

      using traits = string_traits<Field>;
      auto const budget{estimate_buffer(f)};
      auto const offset{m_buffer.size()};

      if constexpr (std::is_arithmetic_v<Field>)
      {
        // Specially optimised for "safe" types, which never need any
        // escaping.  Convert straight into m_buffer.

        // The budget we get from size_buffer() includes room for the trailing
        // zero, which we must remove.  But we're also inserting tabs between
        // fields, so we re-purpose the extra byte for that.
        auto const total{offset + budget};
        m_buffer.resize(total);
        char *const end{traits::into_buf(
          m_buffer.data() + offset, m_buffer.data() + total, f)};
        *(end - 1) = '\t';
        // Shrink to fit.  Keep the tab though.
        m_buffer.resize(static_cast<std::size_t>(end - m_buffer.data()));
      }
      else
      {
        // TODO: Specialise string/string_view/zview to skip to_buf()!
        // This field may need escaping.  First convert the value into
        // m_field_buffer, then escape into its final place.
        m_field_buf.resize(budget);
        escape_field_to_buffer(traits::to_buf(
          m_field_buf.data(), m_field_buf.data() + m_field_buf.size(), f));
      }
    }
  }

  /// Append string representation for a null field to @c m_buffer.
  /** This special case is for types which are always null.
   *
   * Also appends a tab.  The tab is meant to be a separator, not a terminator,
   * so if you write any fields at all, you'll end up with one tab too many
   * at the end of the buffer.
   */
  template<typename Field>
  std::enable_if_t<nullness<Field>::always_null>
  append_to_buffer(Field const &)
  {
    m_buffer.append(null_field);
  }

  /// Write raw COPY line into @c m_buffer, based on a container of fields.
  template<typename Container> void fill_buffer(Container const &c)
  {
    // To avoid unnecessary allocations and deallocations, we run through c
    // twice: once to determine how much buffer space we may need, and once to
    // actually write it into the buffer.
    std::size_t budget{0};
    for (auto const &f : c) budget += estimate_buffer(f);
    m_buffer.reserve(budget);
    for (auto const &f : c) append_to_buffer(f);
  }

  template<typename Tuple, std::size_t... indexes>
  static std::size_t
  budget_tuple(Tuple const &t, std::index_sequence<indexes...>)
  {
    return (estimate_buffer(std::get<indexes>(t)) + ...);
  }

  template<typename Tuple, std::size_t... indexes>
  void append_tuple(Tuple const &t, std::index_sequence<indexes...>)
  {
    (append_to_buffer(std::get<indexes>(t)), ...);
  }

  /// Write raw COPY line into @c m_buffer, based on a tuple of fields.
  template<typename... Elts> void fill_buffer(std::tuple<Elts...> const &t)
  {
    using indexes = std::make_index_sequence<sizeof...(Elts)>;

    m_buffer.reserve(budget_tuple(t, indexes{}));
    append_tuple(t, indexes{});
  }

  void set_up(transaction_base &, std::string_view table_name);
  void set_up(
    transaction_base &, std::string_view table_name,
    std::string const &columns);
};

template<typename Columns>
inline stream_to::stream_to(
  transaction_base &tb, std::string_view table_name, Columns const &columns) :
        stream_to{tb, table_name, std::begin(columns), std::end(columns)}
{}

template<typename Iter>
inline stream_to::stream_to(
  transaction_base &tb, std::string_view table_name, Iter columns_begin,
  Iter columns_end) :
        namedclass{"stream_to", table_name},
        internal::transactionfocus{tb}
{
  set_up(tb, table_name, separated_list(",", columns_begin, columns_end));
}
} // namespace pqxx

#include "pqxx/internal/compiler-internal-post.hxx"
#endif
<commit_msg>Clean up a debug include.<commit_after>/* Definition of the pqxx::stream_to class.
 *
 * pqxx::stream_to enables optimized batch updates to a database table.
 *
 * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/stream_to.hxx instead.
 *
 * Copyright (c) 2000-2020, Jeroen T. Vermeulen.
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this
 * mistake, or contact the author.
 */
#ifndef PQXX_H_STREAM_TO
#define PQXX_H_STREAM_TO

#include "pqxx/compiler-public.hxx"
#include "pqxx/internal/compiler-internal-pre.hxx"

#include "pqxx/separated_list.hxx"
#include "pqxx/transaction_base.hxx"

namespace pqxx
{
/// Efficiently write data directly to a database table.
/** If you wish to insert rows of data into a table, you can compose INSERT
 * statements and execute them.  But it's slow and tedious, and you need to
 * worry about quoting and escaping the data.
 *
 * If you're just inserting a single row, it probably won't matter much.  You
 * can use prepared or parameterised statements to take care of the escaping
 * for you.  But if you're inserting large numbers of rows you will want
 * something better.
 *
 * Inserting rows one by one involves a lot of pointless overhead, especially
 * when you are working with a remote database server over the network.  You
 * may end up sending each row over the network as a separate query, and
 * waiting for a reply.  Do it "in bulk" using @c stream_to, and you may find
 * that it goes many times faster.  Sometimes you gain orders of magnitude in
 * speed.
 *
 * Here's how it works: you create a @c stream_to stream to start writing to
 * your table.  You will probably want to specify the columns.  Then, you
 * feed your data into the stream one row at a time.  And finally, you call the
 * stream's @c complete() to tell it to finalise the operation, wait for
 * completion, and check for errors.
 *
 * You insert data using the @c << ("shift-left") operator.  Each row must be
 * something that can be iterated in order to get its constituent fields: a
 * @c std::tuple, a @c std::vector, or anything else with a @c begin and
 * @c end.  It could be a class of your own.  Of course the fields have to
 * match the columns you specified when creating the stream.
 *
 * To insert a null value, pass a @c nullptr, or an empty @c std::optional,
 * @c std::shared_ptr, or @c std::unique_ptr.
 *
 * There is also a matching stream_from for reading data in bulk.
 */
class PQXX_LIBEXPORT stream_to : internal::transactionfocus
{
public:
  /// Create a stream, without specifying columns.
  /** Fields will be inserted in whatever order the columns have in the
   * database.
   *
   * You'll probably want to specify the columns, so that the mapping between
   * your data fields and the table is explicit in your code, and not hidden
   * in an "implicit contract" between your code and your schema.
   */
  stream_to(transaction_base &, std::string_view table_name);

  /// Create a stream, specifying column names as a container of strings.
  template<typename Columns>
  stream_to(
    transaction_base &, std::string_view table_name, Columns const &columns);

  /// Create a stream, specifying column names as a sequence of strings.
  template<typename Iter>
  stream_to(
    transaction_base &, std::string_view table_name, Iter columns_begin,
    Iter columns_end);

  ~stream_to() noexcept;

  [[nodiscard]] operator bool() const noexcept { return not m_finished; }
  [[nodiscard]] bool operator!() const noexcept { return m_finished; }

  /// Complete the operation, and check for errors.
  /** Always call this to close the stream in an orderly fashion, even after
   * an error.  (In the case of an error, abort the transaction afterwards.)
   *
   * The only circumstance where it's safe to skip this is after an error, if
   * you're discarding the entire connection.
   */
  void complete();

  /// Insert a row of data.
  /** Returns a reference to the stream, so you can chain the calls.
   *
   * The @c row can be a tuple, or any type that can be iterated.  Each
   * item becomes a field in the row, in the same order as the columns you
   * specified when creating the stream.
   */
  template<typename Row> stream_to &operator<<(Row const &row)
  {
    write_row(row);
    return *this;
  }

  /// Stream a `stream_from` straight into a `stream_to`.
  /** This can be useful when copying between different databases.  If the
   * source and the destination are on the same database, you'll get better
   * performance doing it all in a regular query.
   */
  stream_to &operator<<(stream_from &);

  /// Insert a row of data.
  /** The @c row can be a tuple, or any type that can be iterated.  Each
   * item becomes a field in the row, in the same order as the columns you
   * specified when creating the stream.
   */
  template<typename Row> void write_row(Row const &row)
  {
    fill_buffer(row);
    write_buffer();
  }

private:
  bool m_finished = false;

  /// Reusable buffer for a row.  Saves doing an allocation for each row.
  std::string m_buffer;

  /// Reusable buffer for converting/escaping a field.
  std::string m_field_buf;

  /// Write a row of raw text-format data into the destination table.
  void write_raw_line(std::string_view);

  /// Write a row of data from @c m_buffer into the destination table.
  /** Resets the buffer for the next row.
   */
  void write_buffer();

  /// COPY encoding for a null field, plus subsequent separator.
  static constexpr std::string_view null_field{"\\N\t"};

  /// Estimate buffer space needed for a field which is always null.
  template<typename T>
  static std::enable_if_t<nullness<T>::always_null, std::size_t>
  estimate_buffer(T const &)
  {
    return null_field.size();
  }

  /// Estimate buffer space needed for field f.
  /** The estimate is not very precise.  We don't actually know how much space
   * we'll need once the escaping comes in.
   */
  template<typename T>
  static std::enable_if_t<not nullness<T>::always_null, std::size_t>
  estimate_buffer(T const &field)
  {
    return is_null(field) ? null_field.size() :
                            string_traits<T>::size_buffer(field);
  }

  /// Append escaped version of @c m_field_buf to @c m_buffer, plus a tab.
  void escape_field_to_buffer(std::string_view);

  /// Append string representation for @c f to @c m_buffer.
  /** This is for the general case, where the field may contain a value.
   *
   * Also appends a tab.  The tab is meant to be a separator, not a terminator,
   * so if you write any fields at all, you'll end up with one tab too many
   * at the end of the buffer.
   */
  template<typename Field>
  std::enable_if_t<not nullness<Field>::always_null>
  append_to_buffer(Field const &f)
  {
    // We append each field, terminated by a tab.  That will leave us with
    // one tab too many, assuming we write any fields at all; we remove that
    // at the end.
    if (is_null(f))
    {
      // Easy.  Append null and tab in one go.
      m_buffer.append(null_field);
    }
    else
    {
      // Convert f into m_buffer.

      using traits = string_traits<Field>;
      auto const budget{estimate_buffer(f)};
      auto const offset{m_buffer.size()};

      if constexpr (std::is_arithmetic_v<Field>)
      {
        // Specially optimised for "safe" types, which never need any
        // escaping.  Convert straight into m_buffer.

        // The budget we get from size_buffer() includes room for the trailing
        // zero, which we must remove.  But we're also inserting tabs between
        // fields, so we re-purpose the extra byte for that.
        auto const total{offset + budget};
        m_buffer.resize(total);
        char *const end{traits::into_buf(
          m_buffer.data() + offset, m_buffer.data() + total, f)};
        *(end - 1) = '\t';
        // Shrink to fit.  Keep the tab though.
        m_buffer.resize(static_cast<std::size_t>(end - m_buffer.data()));
      }
      else
      {
        // TODO: Specialise string/string_view/zview to skip to_buf()!
        // This field may need escaping.  First convert the value into
        // m_field_buffer, then escape into its final place.
        m_field_buf.resize(budget);
        escape_field_to_buffer(traits::to_buf(
          m_field_buf.data(), m_field_buf.data() + m_field_buf.size(), f));
      }
    }
  }

  /// Append string representation for a null field to @c m_buffer.
  /** This special case is for types which are always null.
   *
   * Also appends a tab.  The tab is meant to be a separator, not a terminator,
   * so if you write any fields at all, you'll end up with one tab too many
   * at the end of the buffer.
   */
  template<typename Field>
  std::enable_if_t<nullness<Field>::always_null>
  append_to_buffer(Field const &)
  {
    m_buffer.append(null_field);
  }

  /// Write raw COPY line into @c m_buffer, based on a container of fields.
  template<typename Container> void fill_buffer(Container const &c)
  {
    // To avoid unnecessary allocations and deallocations, we run through c
    // twice: once to determine how much buffer space we may need, and once to
    // actually write it into the buffer.
    std::size_t budget{0};
    for (auto const &f : c) budget += estimate_buffer(f);
    m_buffer.reserve(budget);
    for (auto const &f : c) append_to_buffer(f);
  }

  template<typename Tuple, std::size_t... indexes>
  static std::size_t
  budget_tuple(Tuple const &t, std::index_sequence<indexes...>)
  {
    return (estimate_buffer(std::get<indexes>(t)) + ...);
  }

  template<typename Tuple, std::size_t... indexes>
  void append_tuple(Tuple const &t, std::index_sequence<indexes...>)
  {
    (append_to_buffer(std::get<indexes>(t)), ...);
  }

  /// Write raw COPY line into @c m_buffer, based on a tuple of fields.
  template<typename... Elts> void fill_buffer(std::tuple<Elts...> const &t)
  {
    using indexes = std::make_index_sequence<sizeof...(Elts)>;

    m_buffer.reserve(budget_tuple(t, indexes{}));
    append_tuple(t, indexes{});
  }

  void set_up(transaction_base &, std::string_view table_name);
  void set_up(
    transaction_base &, std::string_view table_name,
    std::string const &columns);
};

template<typename Columns>
inline stream_to::stream_to(
  transaction_base &tb, std::string_view table_name, Columns const &columns) :
        stream_to{tb, table_name, std::begin(columns), std::end(columns)}
{}

template<typename Iter>
inline stream_to::stream_to(
  transaction_base &tb, std::string_view table_name, Iter columns_begin,
  Iter columns_end) :
        namedclass{"stream_to", table_name},
        internal::transactionfocus{tb}
{
  set_up(tb, table_name, separated_list(",", columns_begin, columns_end));
}
} // namespace pqxx

#include "pqxx/internal/compiler-internal-post.hxx"
#endif
<|endoftext|>


<commit_before>#ifndef __TRIANGULATION_TEMPLATES_HPP_INCLUDED
#define __TRIANGULATION_TEMPLATES_HPP_INCLUDED

#include "code/ylikuutio/common/globals.hpp"

// Include GLEW
#ifndef __GL_GLEW_H_INCLUDED
#define __GL_GLEW_H_INCLUDED
#include <GL/glew.h> // GLfloat, GLuint etc.
#endif

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp> // glm
#endif

// Include standard headers
#include <stdint.h> // uint32_t etc.
#include <vector>   // std::vector

namespace geometry
{
    template<class T1>
        T1 get_y(
                const T1* const vertex_data,
                const int32_t x,
                const int32_t z,
                const int32_t image_width)
        {
            // This function returns the altitude value based on x & z coordinates.
            // This works only for a raw heightmap data (for a 2D array of altitudes).
            const T1* const vertex_pointer = vertex_data + z * image_width + x;
            return static_cast<T1>(*vertex_pointer);
        }

    // for bilinear interpolation.
    template<class T1>
        float southwest_y(const int32_t x, const int32_t z, const T1* const input_vertex_pointer, const int32_t image_width, const int32_t x_step, const int32_t z_step)
        {
            return static_cast<float>(geometry::get_y(input_vertex_pointer, x - x_step, z - z_step, image_width));
        }
    template<class T1>
        float southeast_y(const int32_t x, const int32_t z, const T1* const input_vertex_pointer, const int32_t image_width, const int32_t x_step, const int32_t z_step)
        {
            return static_cast<float>(geometry::get_y(input_vertex_pointer, x, z - z_step, image_width));
        }
    template<class T1>
        float northwest_y(const int32_t x, const int32_t z, const T1* const input_vertex_pointer, const int32_t image_width, const int32_t x_step, const int32_t z_step)
        {
            return static_cast<float>(geometry::get_y(input_vertex_pointer, x - x_step, z, image_width));
        }
    template<class T1>
        float northeast_y(const int32_t x, const int32_t z, const T1* const input_vertex_pointer, const int32_t image_width, const int32_t x_step, const int32_t z_step)
        {
            return static_cast<float>(geometry::get_y(input_vertex_pointer, x, z, image_width));
        }
    template<class T1>
        float center_y(const int32_t x, const int32_t z, const T1* const input_vertex_pointer, const int32_t image_width, const int32_t x_step, const int32_t z_step)
        {
            return static_cast<float>(southwest_y(x, z, input_vertex_pointer, image_width, x_step, z_step) +
                    southeast_y(x, z, input_vertex_pointer, image_width, x_step, z_step) +
                    northwest_y(x, z, input_vertex_pointer, image_width, x_step, z_step) +
                    northeast_y(x, z, input_vertex_pointer, image_width, x_step, z_step)) / 4.0f;
        }

    template<class T1>
        bool define_vertices(
                const T1* const input_vertex_pointer,
                const int32_t image_width,
                const int32_t image_height,
                const int32_t x_step,
                const int32_t z_step,
                const bool should_ylikuutio_use_real_texture_coordinates,
                std::vector<glm::vec3>& temp_vertices,
                std::vector<glm::vec2>& temp_UVs)
        {
            if (image_width < 0 || image_height < 0)
            {
                // Can not define vertices if image width < 0 or image height < 0.
                return false;
            }

            const int32_t actual_image_width = image_width / x_step;
            const int32_t actual_image_height = actual_image_height / x_step;
            int32_t number_of_vertices = actual_image_width * actual_image_height;
            temp_vertices.reserve(number_of_vertices);
            temp_UVs.reserve(number_of_vertices);

            // Define the temporary vertices in a double loop.
            int32_t texture_y = 0;

            for (int32_t z = 0; z < image_height; z += z_step)
            {
                int32_t texture_x = 0;

                for (int32_t x = 0; x < image_width; x += x_step)
                {
                    // current x,z coordinates).
                    float y = static_cast<float>(geometry::get_y(input_vertex_pointer, x, z, image_width));

                    // This corresponds to "v": specify one vertex.
                    glm::vec3 vertex;
                    vertex.x = static_cast<GLfloat>(x);
                    vertex.y = static_cast<GLfloat>(y);
                    vertex.z = static_cast<GLfloat>(z);
                    temp_vertices.push_back(vertex);

                    // This corresponds to "vt": specify texture coordinates of one vertex.
                    glm::vec2 uv;

                    if (should_ylikuutio_use_real_texture_coordinates)
                    {
                        uv.x = round(static_cast<GLfloat>(texture_x));
                        uv.y = round(static_cast<GLfloat>(texture_y));
                    }
                    else
                    {
                        uv.x = static_cast<GLfloat>(y) / 256.0f;
                        uv.y = 0.0f;
                    }

                    temp_UVs.push_back(uv);

                    texture_x ^= 1;
                }
                texture_y ^= 1;
            }

            return true;
        }

    template<class T1>
        const bool interpolate_and_define_vertices_using_bilinear_interpolation(
                const T1* const input_vertex_pointer,
                const int32_t image_width,
                const int32_t image_height,
                const int32_t x_step,
                const int32_t z_step,
                const bool should_ylikuutio_use_real_texture_coordinates,
                std::vector<glm::vec3>& temp_vertices,
                std::vector<glm::vec2>& temp_UVs)
        {
            std::cout << "interpolating center vertices.\n";

            if (image_width < 2 || image_height < 2)
            {
                // Can not interpolate center vertices if image width < 2 or image height < 2.
                return false;
            }

            if (x_step <= 0 || z_step <= 0)
            {
                // Can not interpolate center vertices if x_step <= 0 or z_step <= 0.
                return false;
            }

            // Then, define the faces in a double loop.
            // Begin from index `z_step`.
            for (int32_t z = z_step; z < image_height; z += z_step)
            {
                // Begin from index `x_step`.
                for (int32_t x = x_step; x < image_width; x += x_step)
                {
                    // This corresponds to "f": specify a face (but here we specify 2 faces instead!).

                    // Interpolate y coordinate (altitude).
                    const float y = center_y(x, z, input_vertex_pointer, image_width, x_step, z_step);

                    // Create a new vertex using bilinear interpolation.
                    // This corresponds to "v": specify one vertex.
                    glm::vec3 vertex;
                    vertex.x = static_cast<GLfloat>(x) - 0.5f * x_step;
                    vertex.y = static_cast<GLfloat>(y);
                    vertex.z = static_cast<GLfloat>(z) - 0.5f * z_step;
                    temp_vertices.push_back(vertex);

                    // This corresponds to "vt": specify texture coordinates of one vertex.
                    glm::vec2 uv;

                    if (should_ylikuutio_use_real_texture_coordinates)
                    {
                        uv.x = 0.5f;
                        uv.y = 0.5f;
                    }
                    else
                    {
                        uv.x = y / 256.0f;
                        uv.y = 0.0f;
                    }

                    temp_UVs.push_back(uv);
                }
            }

            return true;
        }
}

#endif
<commit_msg>Bugfix: `define_vertices`: use of an uninitialized value.<commit_after>#ifndef __TRIANGULATION_TEMPLATES_HPP_INCLUDED
#define __TRIANGULATION_TEMPLATES_HPP_INCLUDED

#include "code/ylikuutio/common/globals.hpp"

// Include GLEW
#ifndef __GL_GLEW_H_INCLUDED
#define __GL_GLEW_H_INCLUDED
#include <GL/glew.h> // GLfloat, GLuint etc.
#endif

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp> // glm
#endif

// Include standard headers
#include <stdint.h> // uint32_t etc.
#include <vector>   // std::vector

namespace geometry
{
    template<class T1>
        T1 get_y(
                const T1* const vertex_data,
                const int32_t x,
                const int32_t z,
                const int32_t image_width)
        {
            // This function returns the altitude value based on x & z coordinates.
            // This works only for a raw heightmap data (for a 2D array of altitudes).
            const T1* const vertex_pointer = vertex_data + z * image_width + x;
            return static_cast<T1>(*vertex_pointer);
        }

    // for bilinear interpolation.
    template<class T1>
        float southwest_y(const int32_t x, const int32_t z, const T1* const input_vertex_pointer, const int32_t image_width, const int32_t x_step, const int32_t z_step)
        {
            return static_cast<float>(geometry::get_y(input_vertex_pointer, x - x_step, z - z_step, image_width));
        }
    template<class T1>
        float southeast_y(const int32_t x, const int32_t z, const T1* const input_vertex_pointer, const int32_t image_width, const int32_t x_step, const int32_t z_step)
        {
            return static_cast<float>(geometry::get_y(input_vertex_pointer, x, z - z_step, image_width));
        }
    template<class T1>
        float northwest_y(const int32_t x, const int32_t z, const T1* const input_vertex_pointer, const int32_t image_width, const int32_t x_step, const int32_t z_step)
        {
            return static_cast<float>(geometry::get_y(input_vertex_pointer, x - x_step, z, image_width));
        }
    template<class T1>
        float northeast_y(const int32_t x, const int32_t z, const T1* const input_vertex_pointer, const int32_t image_width, const int32_t x_step, const int32_t z_step)
        {
            return static_cast<float>(geometry::get_y(input_vertex_pointer, x, z, image_width));
        }
    template<class T1>
        float center_y(const int32_t x, const int32_t z, const T1* const input_vertex_pointer, const int32_t image_width, const int32_t x_step, const int32_t z_step)
        {
            return static_cast<float>(southwest_y(x, z, input_vertex_pointer, image_width, x_step, z_step) +
                    southeast_y(x, z, input_vertex_pointer, image_width, x_step, z_step) +
                    northwest_y(x, z, input_vertex_pointer, image_width, x_step, z_step) +
                    northeast_y(x, z, input_vertex_pointer, image_width, x_step, z_step)) / 4.0f;
        }

    template<class T1>
        bool define_vertices(
                const T1* const input_vertex_pointer,
                const int32_t image_width,
                const int32_t image_height,
                const int32_t x_step,
                const int32_t z_step,
                const bool should_ylikuutio_use_real_texture_coordinates,
                std::vector<glm::vec3>& temp_vertices,
                std::vector<glm::vec2>& temp_UVs)
        {
            if (image_width < 0 || image_height < 0)
            {
                // Can not define vertices if image width < 0 or image height < 0.
                return false;
            }

            const int32_t actual_image_width = image_width / x_step;
            const int32_t actual_image_height = image_height / z_step;
            int32_t number_of_vertices = actual_image_width * actual_image_height;
            temp_vertices.reserve(number_of_vertices);
            temp_UVs.reserve(number_of_vertices);

            // Define the temporary vertices in a double loop.
            int32_t texture_y = 0;

            for (int32_t z = 0; z < image_height; z += z_step)
            {
                int32_t texture_x = 0;

                for (int32_t x = 0; x < image_width; x += x_step)
                {
                    // current x,z coordinates).
                    float y = static_cast<float>(geometry::get_y(input_vertex_pointer, x, z, image_width));

                    // This corresponds to "v": specify one vertex.
                    glm::vec3 vertex;
                    vertex.x = static_cast<GLfloat>(x);
                    vertex.y = static_cast<GLfloat>(y);
                    vertex.z = static_cast<GLfloat>(z);
                    temp_vertices.push_back(vertex);

                    // This corresponds to "vt": specify texture coordinates of one vertex.
                    glm::vec2 uv;

                    if (should_ylikuutio_use_real_texture_coordinates)
                    {
                        uv.x = round(static_cast<GLfloat>(texture_x));
                        uv.y = round(static_cast<GLfloat>(texture_y));
                    }
                    else
                    {
                        uv.x = static_cast<GLfloat>(y) / 256.0f;
                        uv.y = 0.0f;
                    }

                    temp_UVs.push_back(uv);

                    texture_x ^= 1;
                }
                texture_y ^= 1;
            }

            return true;
        }

    template<class T1>
        const bool interpolate_and_define_vertices_using_bilinear_interpolation(
                const T1* const input_vertex_pointer,
                const int32_t image_width,
                const int32_t image_height,
                const int32_t x_step,
                const int32_t z_step,
                const bool should_ylikuutio_use_real_texture_coordinates,
                std::vector<glm::vec3>& temp_vertices,
                std::vector<glm::vec2>& temp_UVs)
        {
            std::cout << "interpolating center vertices.\n";

            if (image_width < 2 || image_height < 2)
            {
                // Can not interpolate center vertices if image width < 2 or image height < 2.
                return false;
            }

            if (x_step <= 0 || z_step <= 0)
            {
                // Can not interpolate center vertices if x_step <= 0 or z_step <= 0.
                return false;
            }

            // Then, define the faces in a double loop.
            // Begin from index `z_step`.
            for (int32_t z = z_step; z < image_height; z += z_step)
            {
                // Begin from index `x_step`.
                for (int32_t x = x_step; x < image_width; x += x_step)
                {
                    // This corresponds to "f": specify a face (but here we specify 2 faces instead!).

                    // Interpolate y coordinate (altitude).
                    const float y = center_y(x, z, input_vertex_pointer, image_width, x_step, z_step);

                    // Create a new vertex using bilinear interpolation.
                    // This corresponds to "v": specify one vertex.
                    glm::vec3 vertex;
                    vertex.x = static_cast<GLfloat>(x) - 0.5f * x_step;
                    vertex.y = static_cast<GLfloat>(y);
                    vertex.z = static_cast<GLfloat>(z) - 0.5f * z_step;
                    temp_vertices.push_back(vertex);

                    // This corresponds to "vt": specify texture coordinates of one vertex.
                    glm::vec2 uv;

                    if (should_ylikuutio_use_real_texture_coordinates)
                    {
                        uv.x = 0.5f;
                        uv.y = 0.5f;
                    }
                    else
                    {
                        uv.x = y / 256.0f;
                        uv.y = 0.0f;
                    }

                    temp_UVs.push_back(uv);
                }
            }

            return true;
        }
}

#endif
<|endoftext|>


<commit_before>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once
#ifndef FS_COMMON_HPP
#define FS_COMMON_HPP

#include <memory>
#include <string>

namespace fs {

  /**
   * @brief Type used as a building block to represent buffers
   * within the filesystem subsystem
   */
  using buffer_t = std::shared_ptr<uint8_t>;

  struct error_t
  {
    enum token_t {
      NO_ERR = 0,
      E_IO, // general I/O error
      E_MNT,

      E_NOENT,
      E_NOTDIR,
      E_NOTFILE
    }; //< enum token_t

    /**
     * @brief Constructor
     *
     * @param tk:  An error token
     * @param rsn: The reason for the error
     */
    error_t(const token_t tk, const std::string& rsn)
      : token_{tk}
      , reason_{rsn}
    {}

    /**
     * @brief Get a human-readable description of the token
     *
     * @return Description of the token as a {std::string}
     */
    const std::string& token() const noexcept;

    /**
     * @brief Get an explanation for error
     */
    const std::string& reason() const noexcept
    { return reason_; }

    /**
     * @brief Get a {std::string} representation of this type
     *
     * Format "description: reason"
     *
     * @return {std::string} representation of this type
     */
    std::string to_string() const
    { return token() + ": " + reason(); }

    /**
     * @brief Check if the object of this type represents
     * an error
     *
     * @return true if its an error, false otherwise
     */
    operator bool () const noexcept
    { return token_ not_eq NO_ERR; }

  private:
    const token_t     token_;
    const std::string reason_;
  }; //< struct error_t

  /**
   * @brief Type used for buffers within the filesystem
   * subsystem
   */
  struct Buffer
  {
    Buffer(const error_t& e, buffer_t b, const uint64_t l)
      : err_    {e}
      , buffer_ {b}
      , len_    {l}
    {}

    /**
     * @brief Check if an object of this type is in a valid
     * state
     *
     * @return true if valid, false otherwise
     */
    bool is_valid() const noexcept
    { return (buffer_ not_eq nullptr) and (not err_); }

    /**
     * @brief Coerce an object of this type to a bool
     */
    operator bool () const noexcept
    { return is_valid(); }

    /**
     * @brief Get the starting address of the underlying data buffer
     *
     * @return The starting address of the underlying data buffer
     */
    uint8_t* data() noexcept
    { return buffer_.get(); }

    /**
     * @brief Get the size/length of the buffer
     *
     * @return The size/length of the buffer
     */
    size_t   size() const noexcept
    { return len_; }

    /**
     * @brief Get a {std::string} representation of this type
     *
     * @return A {std::string} representation of this type
     */
    std::string to_string() const noexcept
    { return std::string{static_cast<char*>(data()), size()}; }

  private:
    const error_t  err_;
    buffer_t       buffer_;
    const uint64_t len_;
  }; //< struct Buffer

  /** @var no_error: Always returns boolean false when used in expressions */
  extern error_t no_error;

} //< namespace fs

#endif //< FS_ERROR_HPP
<commit_msg>Fixed error from converting between types<commit_after>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once
#ifndef FS_COMMON_HPP
#define FS_COMMON_HPP

#include <memory>
#include <string>

namespace fs {

  /**
   * @brief Type used as a building block to represent buffers
   * within the filesystem subsystem
   */
  using buffer_t = std::shared_ptr<uint8_t>;

  struct error_t
  {
    enum token_t {
      NO_ERR = 0,
      E_IO, // general I/O error
      E_MNT,

      E_NOENT,
      E_NOTDIR,
      E_NOTFILE
    }; //< enum token_t

    /**
     * @brief Constructor
     *
     * @param tk:  An error token
     * @param rsn: The reason for the error
     */
    error_t(const token_t tk, const std::string& rsn)
      : token_{tk}
      , reason_{rsn}
    {}

    /**
     * @brief Get a human-readable description of the token
     *
     * @return Description of the token as a {std::string}
     */
    const std::string& token() const noexcept;

    /**
     * @brief Get an explanation for error
     */
    const std::string& reason() const noexcept
    { return reason_; }

    /**
     * @brief Get a {std::string} representation of this type
     *
     * Format "description: reason"
     *
     * @return {std::string} representation of this type
     */
    std::string to_string() const
    { return token() + ": " + reason(); }

    /**
     * @brief Check if the object of this type represents
     * an error
     *
     * @return true if its an error, false otherwise
     */
    operator bool () const noexcept
    { return token_ not_eq NO_ERR; }

  private:
    const token_t     token_;
    const std::string reason_;
  }; //< struct error_t

  /**
   * @brief Type used for buffers within the filesystem
   * subsystem
   */
  struct Buffer
  {
    Buffer(const error_t& e, buffer_t b, const uint64_t l)
      : err_    {e}
      , buffer_ {b}
      , len_    {l}
    {}

    /**
     * @brief Check if an object of this type is in a valid
     * state
     *
     * @return true if valid, false otherwise
     */
    bool is_valid() const noexcept
    { return (buffer_ not_eq nullptr) and (not err_); }

    /**
     * @brief Coerce an object of this type to a bool
     */
    operator bool () const noexcept
    { return is_valid(); }

    /**
     * @brief Get the starting address of the underlying data buffer
     *
     * @return The starting address of the underlying data buffer
     */
    uint8_t* data() noexcept
    { return buffer_.get(); }

    /**
     * @brief Get the size/length of the buffer
     *
     * @return The size/length of the buffer
     */
    size_t   size() const noexcept
    { return len_; }

    /**
     * @brief Get a {std::string} representation of this type
     *
     * @return A {std::string} representation of this type
     */
    std::string to_string() const noexcept
    { return std::string{reinterpret_cast<char*>(data()), size()}; }

  private:
    const error_t  err_;
    buffer_t       buffer_;
    const uint64_t len_;
  }; //< struct Buffer

  /** @var no_error: Always returns boolean false when used in expressions */
  extern error_t no_error;

} //< namespace fs

#endif //< FS_ERROR_HPP
<|endoftext|>