extract-curves 0.1.1-i586-linux

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.
Files changed (135) hide show
  1. data/Changelog +21 -0
  2. data/bin/ec_rect2polar +22 -0
  3. data/bin/ec_rect2polar.rb +22 -0
  4. data/bin/ec_rev_lines +5 -0
  5. data/bin/ec_rev_lines.rb +5 -0
  6. data/bin/ec_sph_area +30 -0
  7. data/bin/ec_sph_area.rb +30 -0
  8. data/bin/extract_curves +1670 -0
  9. data/bin/extract_curves.rb +1670 -0
  10. data/ruby_ext/pav/extconf.rb +22 -0
  11. data/ruby_ext/pav/pav.so +0 -0
  12. data/ruby_libs/pav/attr_cache.rb +211 -0
  13. data/ruby_libs/pav/attr_cache.t1.rb +32 -0
  14. data/ruby_libs/pav/cache.rb +31 -0
  15. data/ruby_libs/pav/collection/std.rb +58 -0
  16. data/ruby_libs/pav/dbg_log.rb +458 -0
  17. data/ruby_libs/pav/floatsio.rb +53 -0
  18. data/ruby_libs/pav/generator_cache.rb +165 -0
  19. data/ruby_libs/pav/graph/node.rb +602 -0
  20. data/ruby_libs/pav/graph/node_grp.rb +865 -0
  21. data/ruby_libs/pav/gtk.rb +6 -0
  22. data/ruby_libs/pav/gtk/button.rb +118 -0
  23. data/ruby_libs/pav/gtk/dialog.rb +29 -0
  24. data/ruby_libs/pav/gtk/guiobj.rb +772 -0
  25. data/ruby_libs/pav/gtk/icons.rb +124 -0
  26. data/ruby_libs/pav/gtk/rulers.rb +264 -0
  27. data/ruby_libs/pav/gtk/toolbar.rb +189 -0
  28. data/ruby_libs/pav/guiobj.rb +2 -0
  29. data/ruby_libs/pav/guiobj/info_asm.rb +41 -0
  30. data/ruby_libs/pav/guiobj/method.rb +211 -0
  31. data/ruby_libs/pav/guiobj/obj.rb +134 -0
  32. data/ruby_libs/pav/guiobj/signals.rb +9 -0
  33. data/ruby_libs/pav/heap.rb +54 -0
  34. data/ruby_libs/pav/icons/alt_handle.xpm +3832 -0
  35. data/ruby_libs/pav/icons/alt_handle_hover.xpm +3368 -0
  36. data/ruby_libs/pav/icons/alt_handle_pressed.xpm +3828 -0
  37. data/ruby_libs/pav/icons/blob.gif +0 -0
  38. data/ruby_libs/pav/icons/contour.gif +0 -0
  39. data/ruby_libs/pav/icons/contour_carpet.gif +0 -0
  40. data/ruby_libs/pav/icons/curve.gif +0 -0
  41. data/ruby_libs/pav/icons/curve_carpet.gif +0 -0
  42. data/ruby_libs/pav/icons/expand_closed.xpm +1791 -0
  43. data/ruby_libs/pav/icons/expand_closed_hover.xpm +1775 -0
  44. data/ruby_libs/pav/icons/expand_open.xpm +1788 -0
  45. data/ruby_libs/pav/icons/expand_open_hover.xpm +1752 -0
  46. data/ruby_libs/pav/icons/extract_curves/extract_curves-icon-rgb.ppm +14 -0
  47. data/ruby_libs/pav/icons/extract_curves/extract_curves-logo-rgb.gif +0 -0
  48. data/ruby_libs/pav/icons/extract_curves/trace_mark.xpm +38 -0
  49. data/ruby_libs/pav/icons/handle.xpm +213 -0
  50. data/ruby_libs/pav/icons/loop.gif +0 -0
  51. data/ruby_libs/pav/icons/loop_carpet.gif +0 -0
  52. data/ruby_libs/pav/icons/next.xpm +29 -0
  53. data/ruby_libs/pav/icons/next_hover.xpm +315 -0
  54. data/ruby_libs/pav/icons/next_pressed.xpm +144 -0
  55. data/ruby_libs/pav/icons/prev.xpm +29 -0
  56. data/ruby_libs/pav/icons/prev_hover.xpm +315 -0
  57. data/ruby_libs/pav/icons/prev_pressed.xpm +144 -0
  58. data/ruby_libs/pav/icons/shaved-core.gif +0 -0
  59. data/ruby_libs/pav/icons/vnext.xpm +29 -0
  60. data/ruby_libs/pav/icons/vprev.xpm +29 -0
  61. data/ruby_libs/pav/numeric/ext.rb +21 -0
  62. data/ruby_libs/pav/patterns/hsep.gif +0 -0
  63. data/ruby_libs/pav/patterns/tnode.gif +0 -0
  64. data/ruby_libs/pav/patterns/tnode_w_link.gif +0 -0
  65. data/ruby_libs/pav/patterns/vlink.gif +0 -0
  66. data/ruby_libs/pav/patterns/vsep.gif +0 -0
  67. data/ruby_libs/pav/patterns/yg_hrope.xpm +492 -0
  68. data/ruby_libs/pav/patterns/yg_hrope_thick.xpm +1904 -0
  69. data/ruby_libs/pav/patterns/yg_hrope_thin.xpm +130 -0
  70. data/ruby_libs/pav/patterns/yg_tnode.xpm +180 -0
  71. data/ruby_libs/pav/patterns/yg_tnode_thick.xpm +615 -0
  72. data/ruby_libs/pav/patterns/yg_tnode_thin.xpm +55 -0
  73. data/ruby_libs/pav/patterns/yg_tnode_w_link.xpm +190 -0
  74. data/ruby_libs/pav/patterns/yg_tnode_w_link_thick.xpm +676 -0
  75. data/ruby_libs/pav/patterns/yg_tnode_w_link_thin.xpm +62 -0
  76. data/ruby_libs/pav/patterns/yg_vrope.xpm +563 -0
  77. data/ruby_libs/pav/patterns/yg_vrope_thick.xpm +2047 -0
  78. data/ruby_libs/pav/patterns/yg_vrope_thin.xpm +166 -0
  79. data/ruby_libs/pav/pav_find.rb +90 -0
  80. data/ruby_libs/pav/pix.rb +402 -0
  81. data/ruby_libs/pav/pix/aapix.rb +378 -0
  82. data/ruby_libs/pav/pix/blob.rb +678 -0
  83. data/ruby_libs/pav/pix/circle.rb +73 -0
  84. data/ruby_libs/pav/pix/contour.rb +676 -0
  85. data/ruby_libs/pav/pix/contour/calc_situations.rb +9 -0
  86. data/ruby_libs/pav/pix/contour/carp_calc.rb +212 -0
  87. data/ruby_libs/pav/pix/contour/situations.dmp +0 -0
  88. data/ruby_libs/pav/pix/contour/situations.rb +21 -0
  89. data/ruby_libs/pav/pix/curve.rb +1544 -0
  90. data/ruby_libs/pav/pix/img_obj.rb +865 -0
  91. data/ruby_libs/pav/pix/node.rb +159 -0
  92. data/ruby_libs/pav/pix/shaved_core.rb +697 -0
  93. data/ruby_libs/pav/pix/subpix.rb +212 -0
  94. data/ruby_libs/pav/rand_accessible.rb +16 -0
  95. data/ruby_libs/pav/rangeset.rb +63 -0
  96. data/ruby_libs/pav/search.rb +210 -0
  97. data/ruby_libs/pav/set.rb +130 -0
  98. data/ruby_libs/pav/string/bits.rb +523 -0
  99. data/ruby_libs/pav/string/ext.rb +58 -0
  100. data/ruby_libs/pav/string/observable.rb +155 -0
  101. data/ruby_libs/pav/string/text.rb +79 -0
  102. data/ruby_libs/pav/string/words.rb +42 -0
  103. data/ruby_libs/pav/sub_arr.rb +56 -0
  104. data/ruby_libs/pav/traced_obj.rb +79 -0
  105. data/web/index.html +280 -0
  106. data/web/media/icons/alt_handle.xpm +3832 -0
  107. data/web/media/icons/alt_handle_hover.xpm +3368 -0
  108. data/web/media/icons/alt_handle_pressed.xpm +3828 -0
  109. data/web/media/icons/blob.gif +0 -0
  110. data/web/media/icons/contour.gif +0 -0
  111. data/web/media/icons/contour_carpet.gif +0 -0
  112. data/web/media/icons/curve.gif +0 -0
  113. data/web/media/icons/curve_carpet.gif +0 -0
  114. data/web/media/icons/expand_closed.xpm +1791 -0
  115. data/web/media/icons/expand_closed_hover.xpm +1775 -0
  116. data/web/media/icons/expand_open.xpm +1788 -0
  117. data/web/media/icons/expand_open_hover.xpm +1752 -0
  118. data/web/media/icons/extract_curves/extract_curves-icon-rgb.ppm +14 -0
  119. data/web/media/icons/extract_curves/extract_curves-logo-rgb.gif +0 -0
  120. data/web/media/icons/extract_curves/trace_mark.xpm +38 -0
  121. data/web/media/icons/handle.xpm +213 -0
  122. data/web/media/icons/loop.gif +0 -0
  123. data/web/media/icons/loop_carpet.gif +0 -0
  124. data/web/media/icons/next.xpm +29 -0
  125. data/web/media/icons/next_hover.xpm +315 -0
  126. data/web/media/icons/next_pressed.xpm +144 -0
  127. data/web/media/icons/prev.xpm +29 -0
  128. data/web/media/icons/prev_hover.xpm +315 -0
  129. data/web/media/icons/prev_pressed.xpm +144 -0
  130. data/web/media/icons/shaved-core.gif +0 -0
  131. data/web/media/icons/vnext.xpm +29 -0
  132. data/web/media/icons/vprev.xpm +29 -0
  133. data/web/media/title.jpeg +0 -0
  134. data/web/stylesheets/default.css +20 -0
  135. metadata +192 -0
@@ -0,0 +1,378 @@
1
+ require 'pav/pix'
2
+ require 'pav/numeric/ext'
3
+
4
+ module PPix
5
+
6
+ class Xy
7
+ def a_eql?(pix, eps=2e-10)
8
+ (self.ay - pix.ay).abs < eps && (self.ax - pix.ax).abs < eps
9
+ end
10
+
11
+ alias_method :ax, :x
12
+ alias_method :ay, :y
13
+ alias_method :eql?, :a_eql?
14
+
15
+ def a_neighb8?(pix, eps=2e-10)
16
+ dx = (self.ax - pix.ax).abs; dy = (self.ay - pix.ay).abs
17
+ dx <= 1.0+eps && dy <= 1.0+eps
18
+ end
19
+
20
+ def a_neighb4?(pix, eps=2e-10)
21
+ dx = (self.ax - pix.ax).abs; dy = (self.ay - pix.ay).abs
22
+ (dx <= 1.0+eps && dy < eps) || (dy <= 1.0+eps && dx < eps)
23
+ end
24
+
25
+ def a_close_neighb?(pix, eps=2e-10)
26
+ dx = (self.ax - pix.ax).abs; dy = (self.ay - pix.ay).abs
27
+ (dx <= 0.5+eps && dy <= 0.5+eps)
28
+ end
29
+
30
+ def semi_neighb8?(pix, eps=2e-10)
31
+ dx = (self.ax - pix.ax).abs; dy = (self.ay - pix.ay).abs
32
+ dx <= 1.5+eps && dy <= 1.5+eps
33
+ end
34
+
35
+ def semi_neighb4?(pix, eps=2e-10)
36
+ dx = (self.ax - pix.ax).abs; dy = (self.ay - pix.ay).abs
37
+ (dx <= 1.5+eps && dy < eps) || (dy <= 1.5+eps && dx < eps)
38
+ end
39
+
40
+ def semi_close_neighb?(pix, eps=2e-10)
41
+ dx = (self.ax - pix.ax).abs; dy = (self.ay - pix.ay).abs
42
+ (dx <= 0.5-eps && dy <= 0.5-eps)
43
+ end
44
+
45
+ def a_yx(eps=1e-10)
46
+ [self.ay.scaled_round(eps), self.ax.scaled_round(eps)]
47
+ end
48
+
49
+ def a_leg(pix)
50
+ leg = (self.ay <=> pix.ay)
51
+ return leg if leg != 0
52
+ self.ax <=> pix.ax
53
+ end
54
+ end
55
+
56
+ class AaXy < Xy
57
+ attr_reader :ax, :ay
58
+
59
+ def self.a_yx(x, y, eps=1e-10)
60
+ [y.scaled_round(eps), x.scaled_round(eps)]
61
+ end
62
+
63
+ def initialize(ax, ay)
64
+ @ax = ax; @ay = ay
65
+ super(@ax.round, @ay.round)
66
+ end
67
+
68
+ def a_eql?(pix, eps=1e-10)
69
+ (@ay - pix.ay).abs < eps && (@ax - pix.ax).abs < eps
70
+ end
71
+
72
+ def hash
73
+ self.a_yx.hash
74
+ end
75
+
76
+ alias_method :eql?, :a_eql?
77
+
78
+ def to_s
79
+ "(#{@ax}, #{@ay})"
80
+ end
81
+
82
+ alias_method :coords_to_s, :to_s
83
+ end
84
+
85
+ class AaPix < AaXy
86
+ include MPixbufPix
87
+
88
+ def initialize(pixbuf, ax, ay)
89
+ @pixbuf = pixbuf
90
+ super(ax, ay)
91
+ end
92
+
93
+ def dup_aapix
94
+ AaPix.new(@pixbuf, self.ax, self.ay)
95
+ end
96
+ end
97
+
98
+ class AaXyCancelI < AaXy
99
+ attr_accessor :cancel, :semi_adj8_i, :semi_adj8_idxs
100
+
101
+ def initialize(ax, ay, cancel, semi_adj8_i, semi_adj8_idxs)
102
+ @cancel = cancel
103
+ @semi_adj8_i = semi_adj8_i
104
+ @semi_adj8_idxs = semi_adj8_idxs
105
+ super(ax, ay)
106
+ end
107
+ end
108
+
109
+ class AaXy
110
+ ADJ8_SEMI_ADJ8_SPEC = [
111
+ AaXyCancelI.new( 0,-0.5, [ 1,17,18], 3, [15,0, 1, 2, 3, 4, 5, 6, 7]),
112
+ AaXyCancelI.new( 0, -1, [], 3, [1, 2, 3, 4, 5]),
113
+ AaXyCancelI.new(-0.5, 0, [ 3,19,20], 7, [3, 4, 5, 6, 7, 8, 9,10,11]),
114
+ AaXyCancelI.new( -1, 0, [], 7, [5, 6, 7, 8, 9]),
115
+ AaXyCancelI.new( 0, 0.5, [ 5,21,22],11, [7, 8, 9,10,11,12,13,14,15]),
116
+ AaXyCancelI.new( 0, 1, [],11, [9,10,11,12,13]),
117
+ AaXyCancelI.new( 0.5, 0, [ 7,23,16],15, [11,12,13,14,15,0, 1, 2, 3]),
118
+ AaXyCancelI.new( 1, 0, [],15, [13,14,15,0, 1]),
119
+ AaXyCancelI.new( 0.5,-0.5, [ 9,16,17], 1, [15,0, 1, 2, 3]),
120
+ AaXyCancelI.new( 1, -1, [], 1, [0, 1, 2]),
121
+ AaXyCancelI.new(-0.5,-0.5, [11,18,19], 5, [3, 4, 5, 6, 7]),
122
+ AaXyCancelI.new( -1, -1, [], 5, [4, 5, 6]),
123
+ AaXyCancelI.new(-0.5, 0.5, [13,20,21], 9, [7, 8, 9,10,11]),
124
+ AaXyCancelI.new( -1, 1, [], 9, [8, 9,10]),
125
+ AaXyCancelI.new( 0.5, 0.5, [15,22,23],13, [11,12,13,14,15]),
126
+ AaXyCancelI.new( 1, 1, [],13, [12,13,14]),
127
+ AaXyCancelI.new( 1,-0.5, [], 0, [15,0, 1, 2]),
128
+ AaXyCancelI.new( 0.5, -1, [], 2, [0, 1, 2, 3]),
129
+ AaXyCancelI.new(-0.5, -1, [], 4, [3, 4, 5, 6]),
130
+ AaXyCancelI.new( -1,-0.5, [], 6, [4, 5, 6, 7]),
131
+ AaXyCancelI.new( -1, 0.5, [], 8, [7, 8, 9,10]),
132
+ AaXyCancelI.new(-0.5, 1, [],10, [8, 9,10,11]),
133
+ AaXyCancelI.new( 0.5, 1, [],12, [11,12,13,14]),
134
+ AaXyCancelI.new( 1, 0.5, [],14, [12,13,14,15]),
135
+ ]
136
+
137
+ tmp1 = []; 16.times { tmp1 << [] }
138
+ ADJ8_SEMI_ADJ8_SPEC.each { |a| tmp1[a.semi_adj8_i] << a }
139
+ tmp2 = []
140
+ tmp1.each { |m| tmp2 << m.first }; tmp1.each { |m| tmp2 << m.last }
141
+ SEMI_ADJ8 = tmp2
142
+
143
+ def self.semi_neighbs8_contig_arr_get_segs(arr)
144
+ #$PDbgLog.sig_call(self)
145
+ #$PDbgLog.print_msg "ngbs: #{(0...arr.length).find_all { |i|
146
+ # arr[i]}.join(" ")}: "
147
+ end_i = -1
148
+ while !arr[end_i]
149
+ end_i -= 1
150
+ if end_i < -15
151
+ #$PDbgLog.sig_return('Empty.')
152
+ return []
153
+ end
154
+ end
155
+ end_i += 17
156
+ #$PDbgLog.print_msg "end_i=#{end_i} "
157
+ start_p = 0
158
+ start_p += 1 while !arr[start_p]
159
+ #$PDbgLog.print_msg "start_p=#{start_p} "
160
+ if start_p >= end_i
161
+ #$PDbgLog.sig_return("[[#{start_p}, #{start_p+1}]]")
162
+ return [[start_p, start_p+1]]
163
+ end
164
+ res = []
165
+ i = start_p + 1
166
+ loop do
167
+ i += 1 while arr[i]
168
+ res << [start_p, i]
169
+ break if i >= end_i
170
+ i += 1 while !arr[i]
171
+ start_p = i
172
+ end
173
+ if res.length > 1 && res[0][0] % 16 == res.last[1] % 16
174
+ len = res.last[1] - res.last[0]
175
+ res[0][0] -= len
176
+ res.pop
177
+ end
178
+ #$PDbgLog.sig_return(res.inspect)
179
+ res
180
+ end
181
+
182
+ def self.semi_neighbs8_arr_get_segs(ngbs)
183
+ #$PDbgLog.sig_call(self)
184
+ #$PDbgLog.print_msg "ngbs: #{(0...ngbs.length).find_all { |i|
185
+ # ngbs[i]}.join(" ")}: "
186
+ end_p = start_p = -1
187
+ if ngbs[start_p]
188
+ while ngbs[end_p += 1] || end_p % 4 < 3
189
+ if end_p + 1 >= 16
190
+ #$PDbgLog.sig_return('whole circle.')
191
+ return [[0, 0]]
192
+ end
193
+ end
194
+ while ngbs[start_p-=1] || start_p % 4 < 3
195
+ end
196
+ end_i = 16 + start_p
197
+ start_p += 1
198
+ start_p += 1 while !ngbs[start_p]
199
+ end_p -= 1 while !ngbs[end_p-1]
200
+ res = [[start_p, end_p]]
201
+ else
202
+ res = []
203
+ end_i = 15
204
+ end
205
+ loop do
206
+ start_p = end_p + 1
207
+ while !ngbs[start_p]
208
+ start_p += 1
209
+ if start_p >= end_i
210
+ #$PDbgLog.sig_return(res.inspect)
211
+ return res
212
+ end
213
+ end
214
+ end_p = start_p + 1
215
+ end_p += 1 while (ngbs[end_p] || end_p % 4 < 3) &&
216
+ end_p < end_i
217
+ end_p -= 1 while !ngbs[end_p-1]
218
+ res << [start_p, end_p]
219
+ end
220
+ end
221
+
222
+ def self.semi_neighbs8_with_arr_get_segs(arr, ngbs)
223
+ $PDbgLog.sig_call(self)
224
+ $PDbgLog.print_msg "ngbs: #{(0...arr.length).find_all { |i|
225
+ arr[i]}.collect{|i|ngbs[arr[i]].coords_to_s+"[#{i}]"}.join(" "
226
+ )}: " #"
227
+ end_i = -1
228
+ while !arr[end_i]
229
+ end_i -= 1
230
+ if end_i < -15
231
+ $PDbgLog.sig_return('Empty.')
232
+ return []
233
+ end
234
+ end
235
+ end_i += 16
236
+ $PDbgLog.print_msg "end_i=#{end_i} "
237
+ start_p = 0
238
+ start_p += 1 while !arr[start_p]
239
+ $PDbgLog.print_msg "start_p=#{start_p} "
240
+ if start_p >= end_i
241
+ $PDbgLog.sig_return("[[#{start_p}, #{start_p+1}]]")
242
+ return [[start_p, start_p+1]]
243
+ end
244
+ last_pix = ngbs[arr[start_p]]
245
+ res = []
246
+ end_p = i = start_p
247
+ loop do
248
+ i += 1
249
+ i += 1 while !arr[i]
250
+ $PDbgLog.print_msg "i=#{i} "
251
+ if last_pix.a_neighb8?(ngbs[arr[i]])
252
+ if (end_p = i) >= end_i
253
+ if res.empty?
254
+ # if arr.compact.length > 2 && ngbs[arr[
255
+ # start_p]].a_neighb8?(ngbs[arr[i]])
256
+ # $PDbgLog.print_msg("Full circle ")
257
+ # res << [0, 0]
258
+ # else
259
+ res << [start_p, end_p+1]
260
+ # end
261
+ elsif ngbs[arr[res[0][0]]].a_neighb8?(
262
+ ngbs[arr[i]])
263
+ res[0][0] = start_p - 16
264
+ else
265
+ res << [start_p, end_p+1]
266
+ end
267
+ $PDbgLog.sig_return(res.inspect)
268
+ return res
269
+ end
270
+ else
271
+ $PDbgLog.puts_msg "seg[#{start_p},#{end_p+1})"
272
+ res << [start_p, end_p+1]
273
+ if i >= end_i
274
+ if ngbs[arr[res[0][0]]].neighb8?(ngbs[arr[i]])
275
+ res[0][0] = i - 16
276
+ else
277
+ res << [i, i+1]
278
+ end
279
+ $PDbgLog.sig_return(res.inspect)
280
+ return res
281
+ end
282
+ start_p = i
283
+ last_pix = ngbs[arr[start_p]]
284
+ end
285
+ last_pix = ngbs[arr[i]]
286
+ end
287
+ end
288
+ end
289
+
290
+ class Xy
291
+ def semi_neighbs8_arr(neighbs)
292
+ #$PDbgLog.sig_call(self)
293
+ #$PDbgLog.print_msg self.coords_to_s + ", neighbs: #{PixbufPix.
294
+ # path_to_s(neighbs)}: "
295
+ warn("#{self.class.name}.semi_neighbs8_arr: " +
296
+ "Non-neighbors given to #{self.coords_to_s}: #{PixbufPix.
297
+ path_to_s(neighbs)}") if neighbs.detect { |a|
298
+ !a.a_neighb8?(self) }
299
+ res = Array.new(16)
300
+ ranks = Array.new(16, 100)
301
+ neighbs.each_with_index { |ngb, ngb_i|
302
+ ax = (ngb.ax - self.ax).scaled_round(0.5)
303
+ ay = (ngb.ay - self.ay).scaled_round(0.5)
304
+ AaXy::ADJ8_SEMI_ADJ8_SPEC.each_with_index{|spec,rk|
305
+ if ay == spec.ay && ax == spec.ax
306
+ if ranks[spec.semi_adj8_i] > rk
307
+ res[spec.semi_adj8_i] = ngb_i
308
+ ranks[spec.semi_adj8_i] = rk
309
+ end
310
+ break
311
+ end
312
+ }
313
+ }
314
+ #$PDbgLog.sig_return(res.inspect)
315
+ res
316
+ end
317
+
318
+ def semi_neighbs8_contig_arr(neighbs)
319
+ #$PDbgLog.sig_call(self)
320
+ #$PDbgLog.print_msg self.coords_to_s + ", neighbs: #{Xy.
321
+ # path_to_s(neighbs)}: "
322
+ warn("#{self.class.name}.semi_neighbs8_contig_arr: " +
323
+ "Non-neighbors given to #{self.coords_to_s}: #{PixbufPix.
324
+ path_to_s(neighbs)}") if neighbs.detect { |a|
325
+ !a.semi_neighb8?(self) }
326
+ res = Array.new(16)
327
+ ranks = Array.new(16, 100)
328
+ neighbs.each_with_index { |ngb, ngb_i|
329
+ ax = (ngb.ax - self.ax).scaled_round(0.5)
330
+ ay = (ngb.ay - self.ay).scaled_round(0.5)
331
+ AaXy::ADJ8_SEMI_ADJ8_SPEC.each_with_index{|spec,rk|
332
+ if ay == spec.ay && ax == spec.ax
333
+ spec.semi_adj8_idxs.each { |semi_adj8_i|
334
+ if ranks[semi_adj8_i] > rk
335
+ res[semi_adj8_i] = ngb_i
336
+ ranks[semi_adj8_i] = rk
337
+ end
338
+ }
339
+ break
340
+ end
341
+ }
342
+ }
343
+ #$PDbgLog.sig_return(res.inspect)
344
+ res
345
+ end
346
+ end
347
+
348
+ module MPixbufPix
349
+ def adj8_semi_adj8_each
350
+ #$PDbgLog.sig_call(self)
351
+ pixbuf = self.pixbuf; ax = self.ax; ay = self.ay
352
+ #$PDbgLog.print_msg self.to_s + ",ax=#{ax},ay=#{ay}: "
353
+ cancelled = []
354
+ AaXy::ADJ8_SEMI_ADJ8_SPEC.each_with_index { |a, ai|
355
+ next if cancelled[ai]
356
+ if yield(AaPix.new(pixbuf, ax+a.ax, ay+a.ay))
357
+ a.cancel.each { |cnc| cancelled[cnc] = true }
358
+ end
359
+ }
360
+ #$PDbgLog.sig_return('done.')
361
+ end
362
+
363
+ def semi_adj8_incl_set_each(incl_set)
364
+ i = -1
365
+ self.adj8_semi_adj8_each { |ngb|
366
+ i += 1
367
+ if incl_set.include?(ngb)
368
+ yield(ngb, AaXy::ADJ8_SEMI_ADJ8_SPEC.at(i).
369
+ semi_adj8_i)
370
+ true
371
+ else
372
+ false
373
+ end
374
+ }
375
+ end
376
+ end
377
+
378
+ end
@@ -0,0 +1,678 @@
1
+ require 'set'
2
+ require 'pav/pix'
3
+ require 'pav/cache'
4
+ require 'pav/search'
5
+ require 'pav/attr_cache'
6
+ require 'pav/pix/circle'
7
+ require 'pav/numeric/ext'
8
+ require 'pav/pix/contour'
9
+ require 'pav/pix/img_obj'
10
+ require 'pav/string/bits'
11
+
12
+ module PPix
13
+
14
+ class Pixbuf
15
+ def blob_id_arr
16
+ if !@blob_id_arr
17
+ #@blob_id_arr = IntBitPlaneArr.new(
18
+ # "", self.width * self.height)
19
+ @blob_id_arr = Array.new(self.picture_width *
20
+ self.picture_height, 0)
21
+ end
22
+ @blob_id_arr
23
+ end
24
+
25
+ def blob_id_max
26
+ @blob_id_max = 0 if !@blob_id_max
27
+ @blob_id_max
28
+ end
29
+
30
+ def blob_free_ids
31
+ @blob_free_ids = [] if !@blob_free_ids
32
+ @blob_free_ids
33
+ end
34
+
35
+ def blob_id2ref_arr
36
+ @blob_id2ref_arr = [] if !@blob_id2ref_arr
37
+ @blob_id2ref_arr
38
+ end
39
+
40
+ def set_blob_id(x, y, id)
41
+ @blob_id_max = id if id > self.blob_id_max
42
+ self.blob_id_arr[y * self.picture_width + x] = id
43
+ end
44
+
45
+ def add_blob(blob)
46
+ #$PDbgLog.sig_call(self)
47
+ if self.blob_free_ids.empty?
48
+ #$PDbgLog.print_msg "Using blob_id_max: "
49
+ @blob_id_max = id = self.blob_id_max + 1
50
+ self.blob_id2ref_arr << blob
51
+ else
52
+ #$PDbgLog.print_msg "Using free id: "
53
+ #$PDbgLog.print_msg "#{self.blob_id2ref_arr.collect{|bl|
54
+ # bl.blob_id}.inspect}: "
55
+ id = self.blob_free_ids.shift
56
+ self.blob_id2ref_arr[id-1,0] = [blob]
57
+ #$PDbgLog.print_msg "#{self.blob_id2ref_arr.collect{|bl|
58
+ # bl.blob_id}.inspect}: "
59
+ end
60
+ #$PDbgLog.sig_return(id)
61
+ return id
62
+ end
63
+
64
+ def unregister_blob_id(id)
65
+ return if id == 0
66
+ if id == self.blob_id_max
67
+ #@bpc = self.blob_id_arr.bit_planes_cnt
68
+ #if self.blob_id_max & (self.blob_id_max-1) == 0
69
+ # bpc -= 1
70
+ #end
71
+ self.blob_id2ref_arr.pop
72
+ @blob_id_max = self.blob_id_max-1
73
+ while self.blob_free_ids.last == self.blob_id_max
74
+ #if self.blob_id_max & (self.blob_id_max-1) == 0
75
+ # bpc -= 1
76
+ #end
77
+ @blob_id_max -= self.blob_id_max-1
78
+ self.blob_free_ids.pop
79
+ self.blob_id2ref_arr.pop
80
+ end
81
+ #self.blob_id_arr.bit_planes_cnt = bpc
82
+ else
83
+ if (pos = PSearch.bsearch(self.blob_free_ids, id)) >= 0
84
+ return # blob ID not currently registered
85
+ end
86
+ self.blob_free_ids[-pos-1,0] = id
87
+ self.blob_id2ref_arr[id+pos,1] = nil
88
+ end
89
+ end
90
+
91
+ def blob_id_at(x, y)
92
+ self.blob_id_arr.at(y * self.picture_width + x)
93
+ end
94
+
95
+ def blob_id_at?(x, y, id)
96
+ #self.blob_id_arr.at?(y * self.width + x, id)
97
+ self.blob_id_arr.at(y * self.picture_width + x) == id
98
+ end
99
+
100
+ def blob_at(x, y)
101
+ return nil if (id=self.blob_id_arr[y*self.picture_width+x]) == 0
102
+ self.blob_id2ref_arr[id+PSearch.bsearch(self.blob_free_ids,id)]
103
+ end
104
+
105
+ def blob_by_id(id)
106
+ return nil if id < 1
107
+ return nil if (j = PSearch.bsearch(self.blob_free_ids,id)) >= 0
108
+ self.blob_id2ref_arr[id + j]
109
+ end
110
+
111
+ def delete_blob(spec)
112
+ if spec.kind_of?(Integer)
113
+ blob = self.get_blob_by_id(spec)
114
+ else
115
+ blob = spec
116
+ end
117
+ return unless blob
118
+ blob.each { |pix| self.set_blob_id(pix.x, pix.y, 0) }
119
+ self.unregister_blob_id(blob.blob_id) if blob.blob_id
120
+ end
121
+
122
+ def load_blob_from_txt_stm(f,contour_geometry=8,external_contours=true,
123
+ min_outer_lp_len=0)
124
+ blob = nil
125
+ begin
126
+ FloatsInp.new(f).each_rec { |rec| x, y = *rec
127
+ x = x.to_i; y = y.to_i
128
+ next unless x >= 0 && x < self.picture_width &&
129
+ y >= 0 && y < self.picture_height &&
130
+ self.blob_id_at?(x, y, 0)
131
+ unless blob
132
+ blob_id = self.add_blob(blob =
133
+ PPix::Blob.new(self, contour_geometry,
134
+ external_contours, min_outer_lp_len))
135
+ blob.blob_id = blob_id
136
+ end
137
+ blob.add(self.get_pix(x, y))
138
+ }
139
+ rescue IOError
140
+ self.delete_blob(blob) if blob
141
+ raise
142
+ end
143
+ blob
144
+ end
145
+
146
+ def load_blob_from_txt(file, contour_geometry=8,external_contours=true,
147
+ min_outer_lp_len=0)
148
+ if file.kind_of?(String)
149
+ File.open(file, 'r') { |f|
150
+ return self.load_blob_from_txt_stm(f,contour_geometry,
151
+ external_contours, min_outer_lp_len)
152
+ }
153
+ else
154
+ self.load_blob_from_txt_stm(file, contour_geometry,
155
+ external_contours, min_outer_lp_len)
156
+ end
157
+ end
158
+
159
+ def gui_open_blob
160
+ dialog = Gtk::FileChooserDialog.new("Open blob", self,
161
+ Gtk::FileChooser::ACTION_OPEN, "gnome-vfs",
162
+ [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT],
163
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL])
164
+ dialog.add_shortcut_folder("/tmp") if File.directory?("/tmp")
165
+ dialog.default_response = Gtk::Dialog::RESPONSE_ACCEPT
166
+ dialog.set_extra_widget(param_hbox = Gtk::HBox.new(false, 3))
167
+ param_hbox.pack_start(Gtk::Label.new('Contour geometry:'),
168
+ false, false, 0)
169
+ param_hbox.pack_start(ctr_geom_combo=Gtk::ComboBox.new,false,
170
+ false,0)
171
+ ctr_geom_combo.append_text('8')
172
+ ctr_geom_combo.append_text('4')
173
+ ctr_geom_combo.active = 0
174
+ param_hbox.pack_start(extr_ctrs_check=Gtk::CheckButton.new(
175
+ 'External contours?'), false, false, 0)
176
+ extr_ctrs_check.active = true
177
+ param_hbox.pack_start(Gtk::Label.new('Min. outer lp. length='),
178
+ false, false, 0)
179
+ adj=Gtk::Adjustment.new(0,0,1e6,1,100,100)
180
+ param_hbox.pack_start(min_outer_lp_len_spin=Gtk::SpinButton.new(
181
+ adj, 0, 0))
182
+ param_hbox.pack_start(Gtk::Label.new('pix'), false, false, 0)
183
+ param_hbox.show_all
184
+ fpath = nil
185
+ resp = dialog.run
186
+ fpath = dialog.filename
187
+ ctr_geom = [8,4].at(ctr_geom_combo.active)
188
+ extr_ctrs = extr_ctrs_check.active?
189
+ min_outer_lp_len = min_outer_lp_len_spin.value_as_int
190
+ dialog.destroy
191
+ i = -1
192
+ Gtk.main_iteration_do(false) while (i += 1) < 10 &&
193
+ Gtk.events_pending?
194
+ if resp != Gtk::Dialog::RESPONSE_ACCEPT || !fpath
195
+ return nil
196
+ end
197
+ begin
198
+ return self.load_blob_from_txt(fpath, ctr_geom,
199
+ extr_ctrs, min_outer_lp_len)
200
+ rescue IOError
201
+ PGtk.show_exc_dialog
202
+ end
203
+ nil
204
+ end
205
+ end
206
+
207
+ class PixbufPix
208
+ def blob(contour_geometry=8, external_contours=true,
209
+ min_outer_lp_length=0, &get_neighbs)
210
+ b = self.pixbuf.blob_at(self.x, self.y)
211
+ return b if b
212
+ $PDbgLog.sig_call(self)
213
+ #$PDbgLog.print_msg "Creating blob for #{self.coords_to_s}: "
214
+ id = self.pixbuf.add_blob(b = Blob.new(self.pixbuf,
215
+ contour_geometry, external_contours,
216
+ min_outer_lp_length, &get_neighbs))
217
+ b.blob_id = id
218
+ b.rebuild(self)
219
+ #$PDbgLog.puts_msg "done."
220
+ $PDbgLog.sig_return
221
+ b
222
+ end
223
+ end
224
+
225
+ class BlobYxSet
226
+ attr_reader :blob, :blob_id, :pixbuf
227
+
228
+ def initialize(blob)
229
+ @blob = blob
230
+ @blob_id = @blob.blob_id
231
+ @pixbuf = @blob.pixbuf
232
+ end
233
+
234
+ def include?(yx)
235
+ @pixbuf.blob_id_at?(yx.at(1), yx.at(0), @blob_id)
236
+ end
237
+
238
+ alias_method :member?, :include?
239
+
240
+ def length
241
+ @blob.length
242
+ end
243
+ end
244
+
245
+ class Blob < ImageObject
246
+ def self.mk_rgb_get_neighbs_proc(cmp_pix_to, toler_r, toler_g, toler_b)
247
+ case cmp_pix_to.downcase
248
+ when "blob 8 neighbors' colors"
249
+ #get_neighbs_proc =
250
+ proc { |pix|
251
+ pix.adj_fuzzy_rgb_cocolor(PPix::PixbufPix::ADJACENT8,
252
+ toler_r, toler_g, toler_b).
253
+ find_all { |p| p.pixbuf.blob_id_at?(p.x, p.y, 0) }
254
+ }
255
+ when "blob 4 neighbors' colors"
256
+ #get_neighbs_proc =
257
+ proc { |pix|
258
+ pix.adj_fuzzy_rgb_cocolor(PPix::PixbufPix::ADJACENT4,
259
+ toler_r, toler_g, toler_b).
260
+ find_all { |p| p.pixbuf.blob_id_at?(p.x, p.y, 0) }
261
+ }
262
+ when "blob mean color"
263
+ tot_r = tot_g = tot_b = blen = 0
264
+ #get_neighbs_proc =
265
+ proc { |pix|
266
+ pix.adj8.find_all { |p| r,g,b = *p.color_rgb
267
+ if p.pixbuf.blob_id_at?(p.x, p.y, 0) &&
268
+ (tot_r - r*blen).abs <= toler_r*blen &&
269
+ (tot_g - g*blen).abs <= toler_g*blen &&
270
+ (tot_b - b*blen).abs <= toler_b*blen
271
+ tot_r += r; tot_g += g; tot_b += b; blen += 1
272
+ true
273
+ else
274
+ false
275
+ end
276
+ }
277
+ }
278
+ else
279
+ if cmp_pix_to.kind_of?(Array) && cmp_pix_to.length == 2 &&
280
+ cmp_pix_to[0] == "mean blob color in a circle" &&
281
+ (rad = cmp_pix_to[1]).kind_of?(Numeric)
282
+ #get_neighbs_proc =
283
+ proc { |pix|
284
+ pix.adj8.find_all { |p| mr = mg = mb = mlen = 0
285
+ p.pixbuf.blob_id_at?(p.x, p.y, 0) &&
286
+ (PPix::AFilledCircle(p.x, p.y, rad) { |x,y|
287
+ pp = p.pixbuf.get_pix(x,y)
288
+ unless pp.virtual?
289
+ r, g, b = *pp.color_rgb
290
+ mr += r; mg += g; mb += b; mlen += 1
291
+ end
292
+ }
293
+ r,g,b = *p.color_rgb
294
+ (mr - r*mlen).abs <= toler_r*mlen &&
295
+ (mg - g*mlen).abs <= toler_g*mlen &&
296
+ (mb - b*mlen).abs <= toler_b*mlen)
297
+ }
298
+ }
299
+ else
300
+ raise ArgumentError, "Invalid cmp_pix_to argument: #{
301
+ cmp_pix_to.inspect}!"
302
+ end
303
+ end
304
+ end
305
+
306
+ def self.mk_hsv_get_neighbs_proc(cmp_pix_to, toler_h, toler_s, toler_v)
307
+ case cmp_pix_to.downcase
308
+ when "blob 8 neighbors' colors"
309
+ #get_neighbs_proc =
310
+ proc { |pix|
311
+ pix.adj_fuzzy_hsv_cocolor(PPix::PixbufPix::ADJACENT8,
312
+ toler_h, toler_s, toler_v).
313
+ find_all { |p| p.pixbuf.blob_id_at?(p.x, p.y, 0) }
314
+ }
315
+ when "blob 4 neighbors' colors"
316
+ #get_neighbs_proc =
317
+ proc { |pix|
318
+ pix.adj_fuzzy_hsv_cocolor(PPix::PixbufPix::ADJACENT4,
319
+ toler_h, toler_s, toler_v).
320
+ find_all { |p| p.pixbuf.blob_id_at?(p.x, p.y, 0) }
321
+ }
322
+ when "blob mean color"
323
+ tot_h = tot_s = tot_v = blen = 0
324
+ #get_neighbs_proc =
325
+ proc { |pix|
326
+ pix.adj8.find_all { |p| h,s,v = *p.color_hsv
327
+ if p.pixbuf.blob_id_at?(p.x, p.y, 0) &&
328
+ (tot_h - h*blen).abs <= toler_h*blen &&
329
+ (tot_s - s*blen).abs <= toler_s*blen &&
330
+ (tot_v - v*blen).abs <= toler_v*blen
331
+ tot_h += h; tot_s += s; tot_v += v; blen += 1
332
+ true
333
+ else
334
+ false
335
+ end
336
+ }
337
+ }
338
+ else
339
+ if cmp_pix_to.kind_of?(Array) && cmp_pix_to.length == 2 &&
340
+ cmp_pix_to[0] == "mean blob color in a circle" &&
341
+ (rad = cmp_pix_to[1]).kind_of?(Numeric)
342
+ #get_neighbs_proc =
343
+ proc { |pix|
344
+ pix.adj8.find_all { |p| mh = ms = mv = mlen = 0
345
+ p.pixbuf.blob_id_at?(p.x, p.y, 0) &&
346
+ (PPix::AFilledCircle(p.x, p.y, rad) { |x,y|
347
+ pp = p.pixbuf.get_pix(x,y)
348
+ unless pp.virtual?
349
+ h, s, v = *pp.color_hsv
350
+ mh += h; ms += s; mv += v; mlen += 1
351
+ end
352
+ }
353
+ h,s,v = *p.color_hsv
354
+ (mh - h*mlen).abs <= toler_h*mlen &&
355
+ (ms - s*mlen).abs <= toler_s*mlen &&
356
+ (mv - v*mlen).abs <= toler_v*mlen)
357
+ }
358
+ }
359
+ else
360
+ raise ArgumentError, "Invalid cmp_pix_to argument: #{
361
+ cmp_pix_to.inspect}!"
362
+ end
363
+ end
364
+ end
365
+
366
+ def self.mk_get_neighbs_proc(clr_scheme, cmp_pix_to, toler_1, toler_2,
367
+ toler_3)
368
+ case clr_scheme.downcase
369
+ when 'rgb'
370
+ self.mk_rgb_get_neighbs_proc(cmp_pix_to, toler_1,
371
+ toler_2, toler_3)
372
+ when 'hsv'
373
+ self.mk_hsv_get_neighbs_proc(cmp_pix_to, toler_1,
374
+ toler_2, toler_3)
375
+ else
376
+ raise ArgumentError, "Invalid clr_scheme, must be " +
377
+ "'rgb' or 'hsv': #{clr_scheme}!"
378
+ end
379
+ end
380
+
381
+ def self.img_obj_mk_get_neighbs_proc(cmp_pix_to, toler)
382
+ if toler[0] == 'rgb'
383
+ self.mk_get_neighbs_proc(toler[0], cmp_pix_to, toler[1],
384
+ toler[2], toler[3])
385
+ else
386
+ self.mk_get_neighbs_proc(toler[0], cmp_pix_to, toler[1]/360.0,
387
+ toler[2]/100.0, toler[3]/100.0)
388
+ end
389
+ end
390
+
391
+ toler_enum = PGuiObj::Enum.new('RGB', PGuiObj::PMethod.new(
392
+ proc{|r,g,b|['rgb',r,g,b]}, [
393
+ PGuiObj::Argument.new(Integer,
394
+ lim=OpenStruct.new('min'=>0,'max'=>255), nil, 10, 'r'),
395
+ PGuiObj::Argument.new(Integer, lim, nil, 10, 'g'),
396
+ PGuiObj::Argument.new(Integer, lim, nil, 10, 'b')],'tolerance'),
397
+ 'HSV', PGuiObj::PMethod.new(proc{|h,s,v|['hsv',h,s,v]}, [
398
+ PGuiObj::Argument.new(Float,
399
+ OpenStruct.new('min'=>0.0,'max'=>360.0),nil,10.0,'h'),
400
+ PGuiObj::Argument.new(Float,
401
+ lim=OpenStruct.new('min'=>0.0,'max'=>100.0), nil,10.0, 's'),
402
+ PGuiObj::Argument.new(Float, lim, nil,10.0, 'v')], 'tolerance'))
403
+ cmp_pix_to_enum = PGuiObj::Enum.new("blob 8 neighbors' colors",
404
+ "blob 8 neighbors' colors",
405
+ "blob 4 neighbors' colors", "blob 4 neighbors' colors",
406
+ "blob mean color", "blob mean color",
407
+ "mean blob color in a circle", PGuiObj::PMethod.new(
408
+ proc{|r| ["mean blob color in a circle", r]}, [
409
+ PGuiObj::Argument.new(Integer,
410
+ OpenStruct.new('min'=>0,'max'=>100), nil, 5, 'radius', nil,
411
+ OpenStruct.new('units'=>'pix'))], 'of'))
412
+ MkGetNeighbsProcMeth = PGuiObj::PMethod.new(
413
+ self.method(:img_obj_mk_get_neighbs_proc), [
414
+ PGuiObj::Argument.new(PGuiObj::Enum, cmp_pix_to_enum, nil, nil,
415
+ 'compare pix color to'),
416
+ PGuiObj::Argument.new(PGuiObj::Enum, toler_enum, nil, nil,
417
+ 'color tolerance')], 'get neighbors operator')
418
+
419
+ attr_accessor :blob_id
420
+ attr_reader :pixbuf, :top_left_pix, :get_neighbs,
421
+ :external_contours, :min_outer_lp_length, :contour_geometry
422
+
423
+ def initialize(pixbuf, contour_geometry=8, external_contours=true,
424
+ min_outer_lp_length=0, &get_neighbs)
425
+ @blob_id = nil
426
+ @pixbuf = pixbuf
427
+ @contour_geometry = contour_geometry
428
+ @external_contours = external_contours
429
+ @min_outer_lp_length = min_outer_lp_length
430
+ @pix_neighbs_cache = PCache.new
431
+ if block_given?
432
+ @get_neighbs = get_neighbs
433
+ else
434
+ @get_neighbs = proc { |pix| pix.adj_cocolor8.find_all {
435
+ |px| px.pixbuf.blob_id_at?(px.x, px.y, 0) }}
436
+ end
437
+ @contour_carpets = nil
438
+ super()
439
+ self.pguiobj_config.icon = :"pgtk-blob"
440
+ self.pguiobj_post_destroy_flag.add_observer(
441
+ PGuiObj::ObjDeathObserverInstcMeth.new(
442
+ self.method(:imgobj_destroy)))
443
+ end
444
+
445
+ def add(bpix)
446
+ self << bpix
447
+ if @top_left_pix
448
+ @top_left_pix = bpix if bpix.y < @top_left_pix.y ||
449
+ (bpix.y == @top_left_pix.y &&
450
+ bpix.x < @top_left_pix.x)
451
+ else
452
+ @top_left_pix = bpix
453
+ end
454
+ @pixbuf.set_blob_id(bpix.x, bpix.y, @blob_id)
455
+ end
456
+
457
+ def rebuild(start_pix)
458
+ $PDbgLog.sig_call(self)
459
+ @top_left_pix = start_pix
460
+ $PDbgLog.print_msg "id=#{@blob_id}, start pix#{@top_left_pix.
461
+ coords_to_s}: "
462
+ self.clear
463
+ self << @top_left_pix
464
+ @pixbuf.set_blob_id(@top_left_pix.x, @top_left_pix.y, @blob_id)
465
+ tail = []
466
+ self.pix_known_neighbs(@top_left_pix).each { |p|
467
+ next unless @pixbuf.blob_id_at(p.x, p.y) == 0
468
+ @pixbuf.set_blob_id(p.x, p.y, @blob_id)
469
+ @top_left_pix = p if p.y < @top_left_pix.y ||
470
+ (p.y == @top_left_pix.y &&
471
+ p.x < @top_left_pix.x)
472
+ tail.push(p)
473
+ self << p
474
+ }
475
+ if tail.empty?
476
+ $PDbgLog.sig_return
477
+ return
478
+ end
479
+ $PDbgLog.new_progr
480
+ $PDbgLog.progr.show_as = "/ pixels"
481
+ while !tail.empty?
482
+ 500.times { # Don't update the progress bar for every pixel
483
+ break if tail.empty?
484
+ pix = tail.shift
485
+ self.pix_known_neighbs(pix).each { |p|
486
+ next unless @pixbuf.blob_id_at(p.x, p.y) == 0
487
+ @pixbuf.set_blob_id(p.x, p.y, @blob_id)
488
+ @top_left_pix = p if p.y < @top_left_pix.y ||
489
+ (p.y == @top_left_pix.y &&
490
+ p.x < @top_left_pix.x)
491
+ tail.push(p)
492
+ self << p
493
+ }
494
+ }
495
+ $PDbgLog.progr.progr_units = self.length
496
+ end
497
+ $PDbgLog.sig_return(".")
498
+ end
499
+
500
+ def incorp_subblobs(max_size)
501
+ return unless max_size > 0
502
+ #$PDbgLog.print_msg "Incorporating subblobs: "
503
+ # Skip the outer contour carpet which is first:
504
+ i = 0
505
+ carps = self.contour_carpets
506
+ while (i += 1) < carps.length
507
+ ctr = carps[i]
508
+ #$PDbgLog.print_msg "Carpet #{ctr.length} pix "
509
+ next if ctr.length > max_size ||
510
+ ctr.loop_carpet.internal_blob.length > max_size
511
+ ctr.loop_carpet.internal_blob.each { |p|
512
+ next unless @pixbuf.blob_id_at(p.x, p.y) == 0
513
+ @pixbuf.set_blob_id(p.x, p.y, @blob_id)
514
+ self << p
515
+ }
516
+ carps.delete_at(i)
517
+ i -= 1
518
+ end
519
+ end
520
+
521
+ def img_obj_incorp_subblobs(*args)
522
+ len0 = self.length
523
+ self.incorp_subblobs(*args)
524
+ self.pguiobj_impl_obj.pguiobj_sig_changed(ImageObject.new(
525
+ self, len0, self.length)) if self.length != len0
526
+ end
527
+
528
+ self.img_obj_add_meths(PGuiObj::PMethod.new(:img_obj_incorp_subblobs,
529
+ [PGuiObj::Argument.new(Integer, OpenStruct.new('min'=>0), nil, 50,
530
+ 'max. size',
531
+ "Blobs surrounded by this blob, containing less than these many pixels will be incorporated in the surronding blob.",
532
+ OpenStruct.new('page_step'=>100))],
533
+ 'incorporate subblobs', nil, OpenStruct.new('no_res'=>true)))
534
+
535
+ def pix_known_neighbs(pix)
536
+ self.get_neighbs.call(pix)
537
+ end
538
+
539
+ def pix_neighbs(pix)
540
+ if (tmp = @pix_neighbs_cache[pix.yx])
541
+ return tmp
542
+ end
543
+ @pix_neighbs_cache[pix.yx] = pix.adj8.find_all { |ngb|
544
+ pix.pixbuf.blob_id_at?(ngb.x, ngb.y, self.blob_id) }
545
+ end
546
+
547
+ def contour_carpets
548
+ return @contour_carpets if @contour_carpets
549
+ $PDbgLog.sig_call(self)
550
+ @contour_carpets = []
551
+ excl_set = {}
552
+ # Ensure the first contour carpet is the outer one.
553
+ if @external_contours
554
+ if @contour_geometry == 8
555
+ cneighbs_proc = proc { |cngb_pix|
556
+ nres = []
557
+ Xy::ADJACENT8.each { |cngb_adj|
558
+ x = cngb_pix.x + cngb_adj.x
559
+ y = cngb_pix.y + cngb_adj.y
560
+ next unless (x < 0 || x >= @pixbuf.picture_width ||
561
+ y < 0 || y >= @pixbuf.picture_height ||
562
+ !@pixbuf.blob_id_at?(x, y, @blob_id)) &&
563
+ Xy::ADJACENT4.detect { |cngb_ad|
564
+ (x1=x+cngb_ad.x) >= 0 &&
565
+ x1 < @pixbuf.picture_width &&
566
+ (y1=y+cngb_ad.y) >= 0 &&
567
+ y1 < @pixbuf.picture_height &&
568
+ @pixbuf.blob_id_at?(x1, y1, @blob_id)
569
+ }
570
+ nres << @pixbuf.get_pix(x,y)
571
+ }
572
+ nres
573
+ }
574
+ else
575
+ cneighbs_proc = proc { |px|
576
+ nres = []
577
+ Xy::ADJACENT4.each { |adj|
578
+ x = px.x + adj.x; y = px.y + adj.y
579
+ next unless (x < 0 || x >= @pixbuf.picture_width ||
580
+ y < 0 || y >= @pixbuf.picture_height ||
581
+ !@pixbuf.blob_id_at?(x, y, @blob_id)) &&
582
+ PixbufPix::ADJACENT8.detect { |ad|
583
+ (x1=x+ad.x) >= 0 && x1 < @pixbuf.picture_width &&
584
+ (y1=y+ad.y) >= 0 && y1 < @pixbuf.picture_height &&
585
+ @pixbuf.blob_id_at?(x1, y1, @blob_id)
586
+ }
587
+ nres << @pixbuf.get_pix(x,y)
588
+ }
589
+ nres
590
+ }
591
+ end
592
+ @contour_carpets << ContourCarpet.new(
593
+ @pixbuf.get_pix(@top_left_pix.x,@top_left_pix.y-1),self,
594
+ @contour_geometry, @external_contours,
595
+ @min_outer_lp_length, &cneighbs_proc)
596
+ @contour_carpets.last.each{|p| excl_set[p.yx] = true}
597
+ self.each { |pix|
598
+ Xy::ADJACENT4.each { |adj|
599
+ x = pix.x + adj.x; y = pix.y + adj.y
600
+ if (x < 0 || x >= @pixbuf.picture_width ||
601
+ y < 0 || y >= @pixbuf.picture_height ||
602
+ !@pixbuf.blob_id_at?(x, y, @blob_id)) &&
603
+ !excl_set.include?([y, x])
604
+ @contour_carpets << ContourCarpet.new(
605
+ @pixbuf.get_pix(x, y), self,
606
+ @contour_geometry, @external_contours,
607
+ &cneighbs_proc)
608
+ @contour_carpets.last.each { |p|
609
+ excl_set[p.yx] = true }
610
+ end
611
+ }
612
+ }
613
+ else
614
+ @contour_carpets << ContourCarpet.new(@top_left_pix, self,
615
+ @contour_geometry, @external_contours,
616
+ @min_outer_lp_length)
617
+ @contour_carpets.last.each{|p| excl_set[p.yx] = true}
618
+ self.each { |pix|
619
+ #$PDbgLog.print_msg "pix#{pix.coords_to_s}.border4?=#{
620
+ # pix.border4?} "
621
+ if !excl_set.include?(pix.yx) && (
622
+ (adj4 = pix.adj4).length < 4 || adj4.detect { |a|
623
+ !a.pixbuf.blob_id_at?(a.x, a.y, self.blob_id) })
624
+ #$PDbgLog.puts_msg "ContourCarpet"
625
+ @contour_carpets << ContourCarpet.new(pix, self,
626
+ @contour_geometry, @external_contours)
627
+ @contour_carpets.last.each { |p|
628
+ excl_set[p.yx] = true }
629
+ end
630
+ }
631
+ end
632
+ $PDbgLog.sig_return
633
+ @contour_carpets
634
+ end
635
+
636
+ def img_obj_contour_carpets
637
+ res = self.contour_carpets
638
+ res.each { |cc| cc.pguiobj_config.destroy_btn = false }
639
+ res = ImageObject.create_array(res)
640
+ res.pguiobj_config.name = '[Contour carpets]'
641
+ res.pguiobj_post_destroy_flag.add_observer(
642
+ PGuiObj::ObjDeathObserverInstcMeth.new(
643
+ self.method(:delete_contour_carpets)))
644
+ res
645
+ end
646
+
647
+ self.img_obj_add_meths(meth=PGuiObj::PMethod.new(
648
+ :img_obj_contour_carpets, [], 'contour carpets'))
649
+ PGuiObj::MethObserverCachedAttr.observe(meth)
650
+
651
+ def delete_contour_carpets
652
+ @contour_carpets = nil
653
+ end
654
+
655
+ def to_s
656
+ "<Blob:#{self.object_id.mem_hex}: pixbuf:#{@pixbuf}, @blob_id=#{
657
+ @blob_id}, @top_left_pix#{@top_left_pix.coords_to_s
658
+ }, @get_neighbs:#{@get_neighbs}>"
659
+ end
660
+
661
+ alias_method :inspect, :to_s
662
+
663
+ def img_obj_yx_set
664
+ BlobYxSet.new(self)
665
+ end
666
+
667
+ def clear_cache
668
+ @pix_neighbs_cache.clear
669
+ end
670
+
671
+ def imgobj_destroy
672
+ self.pixbuf.delete_blob(self)
673
+ end
674
+
675
+ cache_attr! :img_obj_yx_set
676
+ end
677
+
678
+ end