<commit_before>#include "labels.h"

#include "tangram.h"
#include "platform.h"
#include "gl/shaderProgram.h"
#include "gl/primitives.h"
#include "view/view.h"
#include "style/style.h"
#include "tile/tile.h"
#include "tile/tileCache.h"

#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtx/rotate_vector.hpp"
#include "glm/gtx/norm.hpp"

namespace Tangram {

Labels::Labels()
    : m_needUpdate(false),
      m_lastZoom(0.0f)
{}

Labels::~Labels() {}

int Labels::LODDiscardFunc(float _maxZoom, float _zoom) {
    return (int) MIN(floor(((log(-_zoom + (_maxZoom + 2)) / log(_maxZoom + 2) * (_maxZoom )) * 0.5)), MAX_LOD);
}

bool Labels::updateLabels(const std::vector<std::unique_ptr<Style>>& _styles,
                          const std::vector<std::shared_ptr<Tile>>& _tiles,
                          float _dt, float _dz, const View& _view)
{
    bool animate = false;

    glm::vec2 screenSize = glm::vec2(_view.getWidth(), _view.getHeight());

    // int lodDiscard = LODDiscardFunc(View::s_maxZoom, _view.getZoom());

    for (const auto& tile : _tiles) {

        // discard based on level of detail
        // if ((zoom - tile->getID().z) > lodDiscard) {
        //     continue;
        // }

        bool proxyTile = tile->isProxy();

        glm::mat4 mvp = _view.getViewProjectionMatrix() * tile->getModelMatrix();

        for (const auto& style : _styles) {
            const auto& mesh = tile->getMesh(*style);
            if (!mesh) { continue; }

            const LabelMesh* labelMesh = dynamic_cast<const LabelMesh*>(mesh.get());
            if (!labelMesh) { continue; }

            for (auto& label : labelMesh->getLabels()) {
                animate |= label->update(mvp, screenSize, _dt, _dz);

                label->setProxy(proxyTile);

                if (label->canOcclude()) {
                    m_aabbs.push_back(label->aabb());
                    m_aabbs.back().m_userData = (void*)label.get();
                }
                m_labels.push_back(label.get());
            }
        }
    }

    return animate;
}

std::set<std::pair<Label*, Label*>> Labels::narrowPhase(const CollisionPairs& _pairs) const {
    std::set<std::pair<Label*, Label*>> occlusions;

    for (auto pair : _pairs) {
        const auto& aabb1 = m_aabbs[pair.first];
        const auto& aabb2 = m_aabbs[pair.second];

        auto l1 = static_cast<Label*>(aabb1.m_userData);
        auto l2 = static_cast<Label*>(aabb2.m_userData);

        if (intersect(l1->obb(), l2->obb())) {
            occlusions.insert({l1, l2});
        }
    }

    return occlusions;
}

void Labels::applyPriorities(const std::set<std::pair<Label*, Label*>> _occlusions) const {
    for (auto& pair : _occlusions) {
        if (!pair.first->occludedLastFrame() || !pair.second->occludedLastFrame()) {
            // check first is the label belongs to a proxy tile
            if (pair.first->isProxy() && !pair.second->isProxy()) {
                pair.first->occlude(Label::OcclusionType::collision);
            } else if (!pair.first->isProxy() && pair.second->isProxy()) {
                pair.second->occlude(Label::OcclusionType::collision);
            } else {
                // lower numeric priority means higher priority
                if (pair.first->options().priority < pair.second->options().priority) {
                    pair.second->occlude(Label::OcclusionType::collision);
                } else {
                    pair.first->occlude(Label::OcclusionType::collision);
                }
            }
        }
    }
}

void Labels::skipTransitions(const std::vector<std::unique_ptr<Style>>& _styles,
                             const std::vector<std::shared_ptr<Tile>>& _tiles,
                             std::unique_ptr<TileCache>& _cache, float _currentZoom) const
{
    for (const auto& t0 : _tiles) {
        TileID tileID = t0->getID();
        std::vector<std::shared_ptr<Tile>> tiles;

        if (m_lastZoom < _currentZoom) {
            // zooming in, add the one cached parent tile
            tiles.push_back(_cache->contains(t0->sourceID(), tileID.getParent()));
        } else {
            // zooming out, add the 4 cached children tiles
            tiles.push_back(_cache->contains(t0->sourceID(), tileID.getChild(0)));
            tiles.push_back(_cache->contains(t0->sourceID(), tileID.getChild(1)));
            tiles.push_back(_cache->contains(t0->sourceID(), tileID.getChild(2)));
            tiles.push_back(_cache->contains(t0->sourceID(), tileID.getChild(3)));
        }

        for (const auto& t1 : tiles) {
            if (!t1) { continue; }
            for (const auto& style : _styles) {
                const auto& m0 = t0->getMesh(*style);
                if (!m0) { continue; }
                const LabelMesh* mesh0 = dynamic_cast<const LabelMesh*>(m0.get());
                if (!mesh0) { continue; }
                const auto& m1 = t1->getMesh(*style);
                if (!m1) { continue; }
                const LabelMesh* mesh1 = static_cast<const LabelMesh*>(m1.get());

                for (auto& l0 : mesh0->getLabels()) {
                    if (!l0->canOcclude()) { continue; }

                    for (auto& l1 : mesh1->getLabels()) {
                        if (!l1 || !l1->canOcclude() || l0->hash() != l1->hash()) {
                            continue;
                        }
                        float d2 = glm::distance2(l0->transform().state.screenPos,
                                l1->transform().state.screenPos);

                        // The new label lies within the circle defined by the bbox of l0
                        if (sqrt(d2) < std::max(l0->dimension().x, l0->dimension().y)) {
                            l0->skipTransitions();
                        }
                    }
                }
            }
        }
    }
}

void Labels::checkRepeatGroups(std::vector<TextLabel*>& _visibleSet) const {
    struct GroupElement {
        glm::vec2 position;
        float threshold;
        const std::string* group;

        bool operator==(const GroupElement& _ge) {
            return _ge.position == position
                && _ge.threshold == threshold
                && *_ge.group == *group;
        };
    };

    std::map<size_t, std::vector<GroupElement>> repeatGroups;

    for (TextLabel* textLabel : _visibleSet) {

        size_t hash = std::hash<std::string>()(textLabel->options().repeatGroup);
        GroupElement element;

        element.position = textLabel->center();
        element.threshold = textLabel->options().repeatDistance;
        element.group = &textLabel->options().repeatGroup;

        auto it = repeatGroups.find(hash);
        if (it == repeatGroups.end()) {
            repeatGroups[hash].push_back(element);
            continue;
        }

        std::vector<GroupElement>& group = it->second;
        if (std::find(group.begin(), group.end(), element) != group.end()) {
            // TBD: when is this case possible?
            // -> When two tiles contain the same label?
            continue;
        }

        bool add = true;
        float threshold2 = pow(element.threshold, 2);

        for (const GroupElement& ge : group) {
            float d2 = distance2(ge.position, element.position);
            if (d2 < threshold2) {
                textLabel->occlude(Label::OcclusionType::repeat_group);<commit_msg>skip labels with repeatDistance 0<commit_after>#include "labels.h"

#include "tangram.h"
#include "platform.h"
#include "gl/shaderProgram.h"
#include "gl/primitives.h"
#include "view/view.h"
#include "style/style.h"
#include "tile/tile.h"
#include "tile/tileCache.h"

#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtx/rotate_vector.hpp"
#include "glm/gtx/norm.hpp"

namespace Tangram {

Labels::Labels()
    : m_needUpdate(false),
      m_lastZoom(0.0f)
{}

Labels::~Labels() {}

int Labels::LODDiscardFunc(float _maxZoom, float _zoom) {
    return (int) MIN(floor(((log(-_zoom + (_maxZoom + 2)) / log(_maxZoom + 2) * (_maxZoom )) * 0.5)), MAX_LOD);
}

bool Labels::updateLabels(const std::vector<std::unique_ptr<Style>>& _styles,
                          const std::vector<std::shared_ptr<Tile>>& _tiles,
                          float _dt, float _dz, const View& _view)
{
    bool animate = false;

    glm::vec2 screenSize = glm::vec2(_view.getWidth(), _view.getHeight());

    // int lodDiscard = LODDiscardFunc(View::s_maxZoom, _view.getZoom());

    for (const auto& tile : _tiles) {

        // discard based on level of detail
        // if ((zoom - tile->getID().z) > lodDiscard) {
        //     continue;
        // }

        bool proxyTile = tile->isProxy();

        glm::mat4 mvp = _view.getViewProjectionMatrix() * tile->getModelMatrix();

        for (const auto& style : _styles) {
            const auto& mesh = tile->getMesh(*style);
            if (!mesh) { continue; }

            const LabelMesh* labelMesh = dynamic_cast<const LabelMesh*>(mesh.get());
            if (!labelMesh) { continue; }

            for (auto& label : labelMesh->getLabels()) {
                animate |= label->update(mvp, screenSize, _dt, _dz);

                label->setProxy(proxyTile);

                if (label->canOcclude()) {
                    m_aabbs.push_back(label->aabb());
                    m_aabbs.back().m_userData = (void*)label.get();
                }
                m_labels.push_back(label.get());
            }
        }
    }

    return animate;
}

std::set<std::pair<Label*, Label*>> Labels::narrowPhase(const CollisionPairs& _pairs) const {
    std::set<std::pair<Label*, Label*>> occlusions;

    for (auto pair : _pairs) {
        const auto& aabb1 = m_aabbs[pair.first];
        const auto& aabb2 = m_aabbs[pair.second];

        auto l1 = static_cast<Label*>(aabb1.m_userData);
        auto l2 = static_cast<Label*>(aabb2.m_userData);

        if (intersect(l1->obb(), l2->obb())) {
            occlusions.insert({l1, l2});
        }
    }

    return occlusions;
}

void Labels::applyPriorities(const std::set<std::pair<Label*, Label*>> _occlusions) const {
    for (auto& pair : _occlusions) {
        if (!pair.first->occludedLastFrame() || !pair.second->occludedLastFrame()) {
            // check first is the label belongs to a proxy tile
            if (pair.first->isProxy() && !pair.second->isProxy()) {
                pair.first->occlude(Label::OcclusionType::collision);
            } else if (!pair.first->isProxy() && pair.second->isProxy()) {
                pair.second->occlude(Label::OcclusionType::collision);
            } else {
                // lower numeric priority means higher priority
                if (pair.first->options().priority < pair.second->options().priority) {
                    pair.second->occlude(Label::OcclusionType::collision);
                } else {
                    pair.first->occlude(Label::OcclusionType::collision);
                }
            }
        }
    }
}

void Labels::skipTransitions(const std::vector<std::unique_ptr<Style>>& _styles,
                             const std::vector<std::shared_ptr<Tile>>& _tiles,
                             std::unique_ptr<TileCache>& _cache, float _currentZoom) const
{
    for (const auto& t0 : _tiles) {
        TileID tileID = t0->getID();
        std::vector<std::shared_ptr<Tile>> tiles;

        if (m_lastZoom < _currentZoom) {
            // zooming in, add the one cached parent tile
            tiles.push_back(_cache->contains(t0->sourceID(), tileID.getParent()));
        } else {
            // zooming out, add the 4 cached children tiles
            tiles.push_back(_cache->contains(t0->sourceID(), tileID.getChild(0)));
            tiles.push_back(_cache->contains(t0->sourceID(), tileID.getChild(1)));
            tiles.push_back(_cache->contains(t0->sourceID(), tileID.getChild(2)));
            tiles.push_back(_cache->contains(t0->sourceID(), tileID.getChild(3)));
        }

        for (const auto& t1 : tiles) {
            if (!t1) { continue; }
            for (const auto& style : _styles) {
                const auto& m0 = t0->getMesh(*style);
                if (!m0) { continue; }
                const LabelMesh* mesh0 = dynamic_cast<const LabelMesh*>(m0.get());
                if (!mesh0) { continue; }
                const auto& m1 = t1->getMesh(*style);
                if (!m1) { continue; }
                const LabelMesh* mesh1 = static_cast<const LabelMesh*>(m1.get());

                for (auto& l0 : mesh0->getLabels()) {
                    if (!l0->canOcclude()) { continue; }

                    for (auto& l1 : mesh1->getLabels()) {
                        if (!l1 || !l1->canOcclude() || l0->hash() != l1->hash()) {
                            continue;
                        }
                        float d2 = glm::distance2(l0->transform().state.screenPos,
                                l1->transform().state.screenPos);

                        // The new label lies within the circle defined by the bbox of l0
                        if (sqrt(d2) < std::max(l0->dimension().x, l0->dimension().y)) {
                            l0->skipTransitions();
                        }
                    }
                }
            }
        }
    }
}

void Labels::checkRepeatGroups(std::vector<TextLabel*>& _visibleSet) const {
    struct GroupElement {
        glm::vec2 position;
        float threshold;
        const std::string* group;

        bool operator==(const GroupElement& _ge) {
            return _ge.position == position
                && _ge.threshold == threshold
                && *_ge.group == *group;
        };
    };

    std::map<size_t, std::vector<GroupElement>> repeatGroups;

    for (TextLabel* textLabel : _visibleSet) {
        auto& options = textLabel->options();
        if (options.repeatDistance == 0.f) { continue; }

        size_t hash = std::hash<std::string>()(options.repeatGroup);

        GroupElement element {
            textLabel->center(),
            options.repeatDistance,
            &options.repeatGroup
        };

        auto it = repeatGroups.find(hash);
        if (it == repeatGroups.end()) {
            repeatGroups[hash].push_back(element);
            continue;
        }

        std::vector<GroupElement>& group = it->second;
        if (std::find(group.begin(), group.end(), element) != group.end()) {
            // TBD: when is this case possible?
            // -> When two tiles contain the same label?
            continue;
        }

        bool add = true;
        float threshold2 = pow(element.threshold, 2);

        for (const GroupElement& ge : group) {
            float d2 = distance2(ge.position, element.position);
            if (d2 < threshold2) {
                textLabel->occlude(Label::OcclusionType::repeat_group);
{'fraction_non_alphanumeric': 0.14446541728461643, 'fraction_numerical': 0.00943777807738978, 'mean_word_length': 1.996364370834175, 'pattern_counts': {'":': 0, '<': 65, '<?xml version=': 0, '>': 165, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5493934', 'n_tokens_mistral': 4519, 'n_tokens_neox': 4352, 'n_words': 877}