Sha256: 126c2fd840f52c5b7b5af758d66e20369a85b767476c1b66fbe4f30386cc0ff3

Contents?: true

Size: 1.57 KB

Versions: 1

Compression:

Stored size: 1.57 KB

Contents

# frozen_string_literal: true

module SlippyTilesScorer
  # finds clusters in a collection/tiles_x_y of points (x, y)
  class Cluster
    attr_accessor :tiles_x_y

    def initialize(tiles_x_y: Set.new)
      @tiles_x_y = tiles_x_y
      @cluster_tiles = Set.new
      @visited = {}
      @clusters = []
    end

    def clusters
      @tiles_x_y.each do |i|
        next if visited?(i[0], i[1])
        visit!(i[0], i[1])
        find_cluster_around(i)
      end
      { clusters: @clusters, cluster_tiles: @cluster_tiles }
    end

    private

    def find_cluster_around(start)
      cluster = []
      cluster.push(start)
      todo = [start]
      broad_search!(todo, cluster)

      @clusters.push(cluster) if cluster.size > 0
      cluster
    end

    def neighbors_up_down_left_right?(neighbors)
      neighbors.all? { |n| @tiles_x_y.include?(n) }
    end

    def broad_search!(todo, cluster) # rubocop:disable Metrics/MethodLength
      until todo.empty?
        point = todo.pop
        neighbors = neighbor_points(*point)
        neighbors_up_down_left_right?(neighbors) && @cluster_tiles.add(point)
        neighbors.each do |neighbor|
          next if visited?(neighbor[0], neighbor[1])
          next unless @tiles_x_y.include?(neighbor)
          visit!(*neighbor) && todo.push(neighbor) && cluster.push(neighbor)
        end
      end
    end

    def visit!(x, y)
      @visited[x] ||= {}
      @visited[x][y] = true
    end

    def visited?(x, y)
      @visited.dig(x, y)
    end

    def neighbor_points(x, y)
      [[x - 1, y], [x + 1, y], [x, y + 1], [x, y - 1]]
    end
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
slippy_tiles_scorer-0.0.2 lib/slippy_tiles_scorer/cluster.rb