slippy_tiles_scorer 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a911e3fb4c9a3d30d90e52664a3619f2c208581a0a31ee938ab4c44444d9608
4
- data.tar.gz: 8093375ef475c76b1c996b31335a7b151e850778c8cd9c285411b0d33926e7a7
3
+ metadata.gz: 45a20e9e3a868777563acc17792b2ba76f8324fe8e4c13ee7419796dd34282b1
4
+ data.tar.gz: 32beb051875c07b2b6ef153ebd28927897463f6d587bf4d9d76954f9a93b70bb
5
5
  SHA512:
6
- metadata.gz: ad02a2cbdda34c8976cdd4cea4a9d90c5c9b80afe3c77c55eec578f054de4c4d89272b1e10eaa47f253184ea96d4fa2716fc16873d7ae51dfe2bf836e47d61e4
7
- data.tar.gz: c55a90cf772a57db236e2b182ac6853bc0137685ca92567c48883a1e897e05d5e0bb38bde02e50a63832793b117b1ba619c2bdf255530668fd51b4031d1d9d9d
6
+ metadata.gz: c9d98be61799953007c5bf71ecd7cfc36ba70d9ed528e83259d9124c81d04d6e618cfe92455b2dc4d1dd5bb3d995eff781ed53b791a306bdaa68f4e2049b2d52
7
+ data.tar.gz: b3b99dedef94d6cf0fd2248867c7649b1ecd18ee089c77d66971296bcaec2634a9f24a7bfdf847a3d681b059019c90fb799a17cc64a8b709f6535ec7d7f291dc
@@ -3,84 +3,144 @@
3
3
  require "set"
4
4
 
5
5
  module SlippyTilesScorer
6
- # finds clusters in a collection/tiles_x_y of points (x, y)
6
+ # Finds connected clusters in a collection of x/y tiles.
7
7
  class Cluster
8
+ MISSING = Object.new.freeze
9
+ private_constant :MISSING
10
+
8
11
  attr_accessor :tiles_x_y
9
12
 
10
13
  def initialize(tiles_x_y: Set.new)
11
14
  @tiles_x_y = tiles_x_y
12
- @cluster_tiles = Set.new
13
- @visited = {}
14
- @clusters = []
15
15
  end
16
16
 
17
- # @return [Hash] The clusters and the tiles in the clusters.
18
- def clusters
19
- @tiles_x_y.each do |i|
20
- next if visited?(i[0], i[1])
17
+ # @return [Hash] The clusters and the tiles surrounded on all four sides.
18
+ def clusters # rubocop:disable Metrics/MethodLength
19
+ tile_index = build_tile_index
20
+
21
+ clusters = []
22
+ cluster_tiles = Set.new
23
+
24
+ @tiles_x_y.each do |start|
25
+ x = start[0]
26
+ y = start[1]
27
+
28
+ row = tile_index[y]
29
+
30
+ # nil means this tile was already visited.
31
+ next unless row && row[x]
32
+
33
+ row[x] = nil
34
+
35
+ cluster = [start]
36
+ todo = [start]
37
+
38
+ broad_search!(
39
+ todo,
40
+ cluster,
41
+ cluster_tiles,
42
+ tile_index
43
+ )
21
44
 
22
- visit!(i[0], i[1])
23
- find_cluster_around(i)
45
+ clusters << cluster
24
46
  end
25
- { clusters: @clusters, cluster_tiles: @cluster_tiles }
47
+
48
+ {
49
+ clusters: clusters,
50
+ cluster_tiles: cluster_tiles
51
+ }
26
52
  end
27
53
 
28
54
  private
29
55
 
30
- # @param start [Array] The x and y coordinate of the start point.
31
- # @return [Array<Array<Integer, Integer>>] The cluster of points.
32
- def find_cluster_around(start)
33
- cluster = []
34
- cluster.push(start)
35
- todo = [start]
36
- broad_search!(todo, cluster)
56
+ # Builds a sparse coordinate lookup.
57
+ #
58
+ # Values initially contain the original tile Array:
59
+ #
60
+ # {
61
+ # y => {
62
+ # x => [x, y]
63
+ # }
64
+ # }
65
+ #
66
+ # Once a tile has been scheduled for traversal its value becomes nil.
67
+ # Hash#key? / Hash#fetch can therefore still distinguish:
68
+ #
69
+ # missing -> MISSING
70
+ # visited -> nil
71
+ # unvisited -> [x, y]
72
+ #
73
+ def build_tile_index # rubocop:disable Metrics/MethodLength
74
+ index = {}
75
+
76
+ @tiles_x_y.each do |tile|
77
+ x = tile[0]
78
+ y = tile[1]
37
79
 
38
- @clusters.push(cluster)
39
- cluster
80
+ row = index[y]
81
+
82
+ if row
83
+ row[x] = tile
84
+ else
85
+ index[y] = { x => tile }
86
+ end
87
+ end
88
+
89
+ index
40
90
  end
41
91
 
42
- # @param todo [Array] The points to visit.
43
- # @param cluster [Array] The cluster of points.
44
- def broad_search!(todo, cluster) # rubocop:disable Metrics/CyclomaticComplexity
92
+ def broad_search!(todo, cluster, cluster_tiles, tile_index) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
45
93
  until todo.empty?
46
94
  point = todo.pop
47
- neighbors = neighbor_points(*point)
48
- neighbors_up_down_left_right?(neighbors) && @cluster_tiles.add(point)
49
- neighbors.each do |neighbor|
50
- next if visited?(neighbor[0], neighbor[1])
51
- next unless @tiles_x_y.include?(neighbor)
52
95
 
53
- visit!(*neighbor) && todo.push(neighbor) && cluster.push(neighbor)
96
+ x = point[0]
97
+ y = point[1]
98
+
99
+ row = tile_index[y]
100
+
101
+ left_x = x - 1
102
+ right_x = x + 1
103
+
104
+ left = row.fetch(left_x, MISSING)
105
+ right = row.fetch(right_x, MISSING)
106
+
107
+ down_row = tile_index[y + 1]
108
+ up_row = tile_index[y - 1]
109
+
110
+ down = down_row ? down_row.fetch(x, MISSING) : MISSING
111
+ up = up_row ? up_row.fetch(x, MISSING) : MISSING
112
+
113
+ if !left.equal?(MISSING) &&
114
+ !right.equal?(MISSING) &&
115
+ !down.equal?(MISSING) &&
116
+ !up.equal?(MISSING)
117
+ cluster_tiles.add(point)
54
118
  end
55
- end
56
- end
57
119
 
58
- # @param x [Integer] The x coordinate of the point.
59
- # @param y [Integer] The y coordinate of the point.
60
- # @return [Boolean] True if the point is visited.
61
- def visit!(x, y)
62
- @visited[x] ||= {}
63
- @visited[x][y] = true
64
- end
120
+ if left && !left.equal?(MISSING)
121
+ row[left_x] = nil
122
+ todo << left
123
+ cluster << left
124
+ end
65
125
 
66
- # @param x [Integer] The x coordinate of the point.
67
- # @param y [Integer] The y coordinate of the point.
68
- # @return [Boolean|Nil] True if the point is visited.
69
- def visited?(x, y)
70
- @visited.dig(x, y)
71
- end
126
+ if right && !right.equal?(MISSING)
127
+ row[right_x] = nil
128
+ todo << right
129
+ cluster << right
130
+ end
72
131
 
73
- # @param neighbors [Array] The neighbors of the point.
74
- # @return [Boolean] True if all neighbors are in the tiles_x_y.
75
- def neighbors_up_down_left_right?(neighbors)
76
- neighbors.all? { |n| @tiles_x_y.include?(n) }
77
- end
132
+ if down && !down.equal?(MISSING)
133
+ down_row[x] = nil
134
+ todo << down
135
+ cluster << down
136
+ end
137
+
138
+ next unless up && !up.equal?(MISSING)
78
139
 
79
- # @param x [Integer] The x coordinate of the point.
80
- # @param y [Integer] The y coordinate of the point.
81
- # @return [Array<Array<Integer, Integer>>] The neighbors of the point.
82
- def neighbor_points(x, y)
83
- [[x - 1, y], [x + 1, y], [x, y + 1], [x, y - 1]]
140
+ up_row[x] = nil
141
+ todo << up
142
+ cluster << up
143
+ end
84
144
  end
85
145
  end
86
146
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SlippyTilesScorer
4
- VERSION = "0.0.5"
4
+ VERSION = "0.0.6"
5
5
  end
data/mise.toml CHANGED
@@ -1,2 +1,2 @@
1
1
  [tools]
2
- ruby = "jruby-10"
2
+ ruby = "4"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slippy_tiles_scorer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Neutert
@@ -65,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
65
  - !ruby/object:Gem::Version
66
66
  version: '0'
67
67
  requirements: []
68
- rubygems_version: 4.0.13
68
+ rubygems_version: 4.0.16
69
69
  specification_version: 4
70
70
  summary: A gem to score a set of slippy tiles.
71
71
  test_files: []