extract-curves 0.1.1-mswin32

Sign up to get free protection for your applications and to get access to all the features.
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.dll +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