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,9 @@
1
+ #!/usr/bin/ruby
2
+ require 'pav/pix/contour/carp_calc'
3
+
4
+ c_plms = {}
5
+ situations = [ PPix::ContourCarpetCalc.get_id2lm(PPix::Subpix8, true, c_plms),
6
+ PPix::ContourCarpetCalc.get_id2lm(PPix::Subpix8, false,c_plms),
7
+ PPix::ContourCarpetCalc.get_id2lm(PPix::Subpix4, true, c_plms),
8
+ PPix::ContourCarpetCalc.get_id2lm(PPix::Subpix4, false,c_plms)]
9
+ open('situations.dmp', 'w') { |file| Marshal.dump(situations, file) }
@@ -0,0 +1,212 @@
1
+ require 'pav/pix'
2
+ require 'pav/pix/subpix'
3
+
4
+ module PPix
5
+
6
+ class ContourCarpetCalc
7
+ def self.subpix_situ8_unpack(situ)
8
+ res = Array.new(8, 0)
9
+ pos = 0
10
+ while situ > 0
11
+ res[pos] = situ % 3
12
+ pos += 1
13
+ situ /= 3
14
+ end
15
+ res
16
+ end
17
+
18
+ def self.subpix_situ8p_unpack(situ)
19
+ res = Array.new(9, 0)
20
+ res[-1] = 1
21
+ pos = 0
22
+ while situ > 0
23
+ res[pos] = situ % 3
24
+ pos += 1
25
+ situ /= 3
26
+ end
27
+ res
28
+ end
29
+
30
+ def self.subpix_situ8_pack(situ_arr)
31
+ #$PDbgLog.sig_call(self)
32
+ res = 0; order = 1
33
+ for ngb_id in situ_arr
34
+ res += order * ngb_id
35
+ order *= 3
36
+ end
37
+ #$PDbgLog.sig_return(situ_arr.inspect + ": 0x%x" % res)
38
+ res
39
+ end
40
+
41
+ def self.situ_subpix_goable_to(situ, sub_n, subpix_class, border_id)
42
+ situ_arr = self.subpix_situ8p_unpack(situ)
43
+ subpix_class::SUB_N_BORDER[sub_n].detect { |bordn_spec|
44
+ situ_arr[Xy::ADJ8_ID2SPEC[PixbufPix.adj8_xy_id(
45
+ bordn_spec.x, bordn_spec.y)][1]] == border_id
46
+ }
47
+ end
48
+
49
+ def self.subpix_situ_neighbs(situ, sub_n, subpix_class, border_id)
50
+ #$PDbgLog.sig_call(self)
51
+ situ_arr = self.subpix_situ8p_unpack(situ)
52
+ #$PDbgLog.print_msg("situ=#{"0x%x" % situ}, sub_n=#{sub_n
53
+ # }, subpix_class=#{subpix_class.name}, border_id=#{border_id
54
+ # }; situ_arr=#{situ_arr.inspect}: ")
55
+ res = []
56
+ subpix_class::SUB_N_NEIGHBS[sub_n].each_with_index {|ngb_spec,i|
57
+ if situ_arr[Xy::ADJ8_ID2SPEC[PixbufPix.adj8_xy_id(
58
+ x=ngb_spec.x, y=ngb_spec.y)][1]] == 1 &&
59
+ subpix_class::SUB_N_BORDER[ngb_spec.sub_n].detect{|bordn_spec|
60
+ situ_arr[Xy::ADJ8_ID2SPEC[PixbufPix.adj8_xy_id(
61
+ x+bordn_spec.x, y+bordn_spec.y)][1]]==border_id
62
+ }
63
+ res[i/2] = ngb_spec
64
+ end
65
+ }
66
+ #$PDbgLog.sig_return(res.inspect)
67
+ res
68
+ end
69
+
70
+ def self.subpix_situ_path(situ, sub_n_0, subpix_class, external)
71
+ #$PDbgLog.sig_call(self)
72
+ # $PDbgLog.print_msg "situ=%x, sub_n_0=#{sub_n_0}, subpix_class=#{
73
+ # subpix_class.name}, external=#{external}: " % situ
74
+ border_id = external ? 2 : 0
75
+ if sub_n_0 >= subpix_class::SUB_N_NEIGHBS.length-1
76
+ ngbs = self.subpix_situ_neighbs(situ, sub_n_0,
77
+ subpix_class, border_id)
78
+ unless (n = ngbs.detect {|ngb| ngb})
79
+ #$PDbgLog.sig_return('Empty.')
80
+ return []
81
+ end
82
+ else
83
+ unless self.situ_subpix_goable_to(situ, sub_n_0,
84
+ subpix_class, border_id)
85
+ #$PDbgLog.sig_return('[]')
86
+ return []
87
+ end
88
+ n = SubXy.new(0, 0, sub_n_0)
89
+ end
90
+ res = [prev_n = n]
91
+ ngbs0 = self.subpix_situ_neighbs(situ, n.sub_n, subpix_class,
92
+ border_id)
93
+ n = ngbs0[0]
94
+ while n
95
+ res.unshift(n)
96
+ break if n.y != 0 || n.x != 0
97
+ ngbs = self.subpix_situ_neighbs(situ, n.sub_n,
98
+ subpix_class, border_id)
99
+ tmp = n
100
+ n = ngbs.first == prev_n ? ngbs.last : ngbs.first
101
+ if res.include?(n)
102
+ #$PDbgLog.sig_return(res.inspect)
103
+ return res
104
+ end
105
+ prev_n = tmp
106
+ end
107
+ prev_n = res.last
108
+ n = ngbs0[1]
109
+ #$PDbgLog.puts_msg "half1: " + res.inspect + ": "
110
+ #$PDbgLog.puts_msg "prev_n=#{prev_n}, n=#{n}: "
111
+ #$PDbgLog.puts_msg "res.include?(n): #{res.include?(n)}"
112
+ while n && !res.include?(n)
113
+ res << n
114
+ break if n.y != 0 || n.x != 0
115
+ ngbs = self.subpix_situ_neighbs(situ, n.sub_n,
116
+ subpix_class, border_id)
117
+ #$PDbgLog.puts_msg "ngbs: #{ngbs.inspect}"
118
+ tmp = n
119
+ n = ngbs.first == prev_n ? ngbs.last : ngbs.first
120
+ prev_n = tmp
121
+ end
122
+ #$PDbgLog.sig_return(res.inspect)
123
+ res
124
+ end
125
+
126
+ def self.subpix_path_border_dirs8_id(path, subpix_class)
127
+ res = 0
128
+ for ngb_spec in path
129
+ subpix_class::SUB_N_BORDER[ngb_spec.sub_n].each { |n|
130
+ res |= Xy::ADJ8_ID2SPEC.at(PixbufPix.
131
+ adj8_xy_id(n.x, n.y)).at(2) if n.y != 0 || n.x != 0
132
+ }
133
+ end
134
+ res
135
+ end
136
+
137
+ def self.subpix_pix_path_normalize(path, subpix_class)
138
+ res = path.collect { |spec| spec.sub_n }
139
+ cyc = subpix_class::SUB_N_NEIGHBS.length-1
140
+ if res.length > 1 && res.first == (res.last + 1) % cyc
141
+ i_min = min_sub_n = 1.0/0.0
142
+ res.each_with_index { |sub_n, i|
143
+ if sub_n < min_sub_n
144
+ min_sub_n = sub_n; i_min = i
145
+ end
146
+ }
147
+ tmp = []
148
+ res.each_index {|i| tmp << res[(i+i_min) % res.length] }
149
+ res = tmp
150
+ end
151
+ res
152
+ end
153
+
154
+ ContourPathLm = Struct.new("ContourPathLm", #:sub_n_path,
155
+ :entry, :exit, :border_dirs8_id, :path_horiz_cnt,
156
+ :path_vert_cnt)
157
+
158
+ def self.get_id2lm(subpix_class, external, c_path_lms={})
159
+ $PDbgLog.sig_call(self)
160
+ $PDbgLog.new_progr
161
+ $PDbgLog.progr.show_as = "/ situations"
162
+ $PDbgLog.progr.total_progr_units = 3**8
163
+ res =
164
+ (0...3**8).collect { |situ8|
165
+ $PDbgLog.progr.progr_units = situ8
166
+ sub_n_lms = Array.new(subpix_class::SUB_N_NEIGHBS.length, nil)
167
+ (0...subpix_class::SUB_N_NEIGHBS.length).each { |sub_n_0|
168
+ path = self.subpix_situ_path(situ8, sub_n_0, subpix_class,
169
+ external)
170
+ if path[0] && (path[0].y != 0 || path[0].x != 0)
171
+ p_entry = path.shift
172
+ else
173
+ p_entry = nil
174
+ end
175
+ if path.last && (path.last.y != 0 || path.last.x != 0)
176
+ p_exit = path.pop
177
+ else
178
+ p_exit = nil
179
+ end
180
+ path_lm = ContourPathLm.new(
181
+ #self.subpix_pix_path_normalize(path, subpix_class),
182
+ p_entry, p_exit,
183
+ self.subpix_path_border_dirs8_id(path, subpix_class),
184
+ path.find_all{|spec| subpix_class.horizontal?(spec.sub_n)}.
185
+ length,
186
+ path.find_all{|spec| subpix_class.vertical?(spec.sub_n)}.
187
+ length)
188
+ path_lm = c_path_lms.include?(path_lm) ? c_path_lms[path_lm] :
189
+ (c_path_lms[path_lm] = path_lm)
190
+ path.each { |spec| sub_n = spec.sub_n
191
+ if sub_n_lms[sub_n]
192
+ if sub_n_lms[sub_n] != path_lm
193
+ raise "Conflicting path elements for #{subpix_class.name
194
+ }, #{external ? 'ex' : 'in'}ternal, situ8=#{
195
+ situ8}, sub_n=#{sub_n} from sub_n_0=#{sub_n_0
196
+ }: #{sub_n_lms[sub_n]} vs. #{path_lm}!"
197
+ end
198
+ else
199
+ sub_n_lms[sub_n] = path_lm
200
+ end
201
+ }
202
+ sub_n_lms[sub_n_0] = path_lm
203
+ }
204
+ sub_n_lms
205
+ }
206
+ $PDbgLog.progr.progr_units += 1
207
+ $PDbgLog.sig_return(' done.')
208
+ res
209
+ end
210
+ end
211
+
212
+ end
@@ -0,0 +1,21 @@
1
+ require 'pav/pav_find'
2
+ require 'pav/string/ext'
3
+ require 'pav/pix/contour/carp_calc'
4
+
5
+ module PPix
6
+
7
+ class ContourCarpetCalc
8
+ PavFind.pfind($:) { |path|
9
+ if path.ends_with('pav/pix/contour/situations.dmp')
10
+ open(path, 'r') { |file|
11
+ CONTOUR_PATH_EXTERNAL_SITU8_ID2LM,
12
+ CONTOUR_PATH_INTERNAL_SITU8_ID2LM,
13
+ CONTOUR_PATH_EXTERNAL_SITU4_ID2LM,
14
+ CONTOUR_PATH_INTERNAL_SITU4_ID2LM = *Marshal.load(file)
15
+ }
16
+ break
17
+ end
18
+ }
19
+ end
20
+
21
+ end
@@ -0,0 +1,1544 @@
1
+ require 'set'
2
+ require 'pav/pix'
3
+ require 'pav/set'
4
+ require 'pav/search'
5
+ require 'pav/sub_arr'
6
+ require 'pav/pix/node'
7
+ require 'pav/pix/aapix'
8
+ require 'pav/pix/subpix'
9
+ require 'pav/pix/contour'
10
+ require 'pav/pix/img_obj'
11
+
12
+ module PPix
13
+
14
+ class Blob
15
+ def curve_carpet(multiple_curves=false)
16
+ return @curve_carp if @curve_carp
17
+ $PDbgLog.sig_call(self)
18
+ @curve_carp = CurveCarpet.new(self, multiple_curves)
19
+ $PDbgLog.sig_return
20
+ @curve_carp.pguiobj_post_destroy_flag.add_observer(
21
+ PGuiObj::ObjDeathObserverInstcMeth.new(
22
+ self.method(:delete_curve_carpet)))
23
+ @curve_carp
24
+ end
25
+
26
+ self.img_obj_add_meths(meth=PGuiObj::PMethod.new(:curve_carpet,
27
+ [PGuiObj::Argument.new(PGuiObj::Bool,nil,nil,false,'multiple curves',
28
+ "Whether to skeletonize contour loops as islands, allowing them to break the connection with each other that they have in blob's contour carpet.")],
29
+ 'curve carpet'))
30
+ PGuiObj::MethObserverCachedAttr.observe(meth)
31
+
32
+ def delete_curve_carpet
33
+ @curve_carp = nil
34
+ end
35
+ end
36
+
37
+ class LoopSegs < Array
38
+ attr_reader :lp, :prev_segs_i, :prev_lp_i
39
+
40
+ def initialize(lp)
41
+ @lp = lp
42
+ super()
43
+ self.rewind
44
+ end
45
+
46
+ def rewind
47
+ @prev_segs_i = 0
48
+ if self.empty?
49
+ @prev_lp_i = 0
50
+ else
51
+ @prev_lp_i = self[0][0]
52
+ end
53
+ end
54
+
55
+ def append_seg(ofs_from_last_start, len)
56
+ self << [ofs_from_last_start, len]
57
+ end
58
+
59
+ def extend_seg_to(seg_i, seg_start_i, end_i)
60
+ j = seg_i
61
+ j_i = seg_start_i
62
+ while j + 1 < self.length && j_i + self[j+1][0] <= end_i
63
+ j += 1
64
+ j_i += self[j][0]
65
+ end
66
+ if tmp = j_i + self[j][1] > end_i then end_i = tmp; end
67
+ self[seg_i][1] = end_i - seg_start_i
68
+ self[seg_i+1..j] = nil
69
+ end
70
+
71
+ def subtract_seg(start_i, end_i)
72
+ #$PDbgLog.sig_call(self)
73
+ seg_i, lp_i = *self.cpix_i_get_seg_i_start(start_i)
74
+ #$PDbgLog.print_msg "#{start_i}..#{end_i}: seg_i=#{seg_i
75
+ # }, lp_i=#{lp_i}; "
76
+ if seg_i >= 0
77
+ if start_i <= lp_i
78
+ if end_i+1 < lp_i + self[seg_i][1]
79
+ len_ch = end_i+1 - lp_i
80
+ self[seg_i][0] += len_ch
81
+ self[seg_i][1] -= len_ch
82
+ @prev_segs_i = seg_i
83
+ @prev_lp_i = end_i+1
84
+ #$PDbgLog.sig_return(".")
85
+ return
86
+ else
87
+ lp_i -= self[seg_i][0]
88
+ end
89
+ elsif end_i+1 < lp_i + self[seg_i][1]
90
+ self[seg_i+1, 0] = [[end_i+1 - lp_i,
91
+ lp_i + self[seg_i][1] - end_i]]
92
+ self[seg_i][1] = start_i-lp_i
93
+ @prev_segs_i = seg_i
94
+ @prev_lp_i = lp_i
95
+ #$PDbgLog.sig_return(".")
96
+ return
97
+ else
98
+ self[seg_i][1] = start_i-lp_i
99
+ seg_i += 1
100
+ end
101
+ else
102
+ seg_i = -seg_i-1
103
+ end
104
+ @prev_segs_i = seg_i-1
105
+ @prev_lp_i = lp_i
106
+ if (j = seg_i) >= self.length
107
+ #$PDbgLog.sig_return(".")
108
+ return
109
+ end
110
+ j_i = lp_i + self[seg_i][0]
111
+ #$PDbgLog.print_msg "j=#{j}, j_i=#{j_i}; "
112
+ while j+1 < self.length && j_i + self[j+1][0] < end_i
113
+ j += 1; j_i += self[j][0]
114
+ end
115
+ #$PDbgLog.print_msg "j=#{j}, j_i=#{j_i}; "
116
+ if j_i + self[j][1] > end_i+1
117
+ self[j][0] = end_i - lp_i
118
+ self[j][1] -= end_i - j_i
119
+ self[j+1][0] -= end_i - j_i if j+1 < self.length
120
+ else
121
+ j += 1
122
+ self[j][0] = j_i + self[j][0] - lp_i if j < self.length
123
+ end
124
+ self[seg_i...j] = nil
125
+ #$PDbgLog.sig_return(".")
126
+ end
127
+
128
+ def each_seg_with_index
129
+ lp_i = 0
130
+ self.each_with_index { |seg, seg_i| lp_i += seg[0]
131
+ yield(SubArr.new(@lp, lp_i, lp_i+seg[1]), seg_i)
132
+ }
133
+ end
134
+
135
+ def each_abs_seg
136
+ lp_i = 0
137
+ self.each { |seg| lp_i += seg[0]; yield(lp_i, lp_i + seg[1]) }
138
+ end
139
+
140
+ def map_cpix!(cpix_i, maps_to)
141
+ #$PDbgLog.sig_call(self)
142
+ len_ch = maps_to.length - 1
143
+ #$PDbgLog.puts_msg "(#{cpix_i}) len_ch=#{len_ch} in #{self.to_s(
144
+ # 0, true)}"
145
+ self.rewind if @prev_lp_i > cpix_i
146
+ if @prev_segs_i >= self.length || @prev_segs_i < 0
147
+ self.rewind
148
+ if @prev_segs_i >= self.length
149
+ warn "LoopSegs.map_cpix!: BUG: @prev_segs_i(=#{
150
+ @prev_segs_i}) >= self.length(=#{self.length})"
151
+ #$PDbgLog.sig_return
152
+ return
153
+ end
154
+ end
155
+ loop do
156
+ if @prev_lp_i > cpix_i
157
+ #$PDbgLog.puts_msg "Mapped pixel out of " +
158
+ # "LoopSegs: #{self.segs_to_s(2)}"
159
+ #$PDbgLog.sig_return
160
+ return
161
+ end
162
+ break if @prev_lp_i + self[@prev_segs_i][1] > cpix_i
163
+ @prev_segs_i += 1
164
+ if @prev_segs_i >= self.length
165
+ #$PDbgLog.puts_msg "Mapped pixel out of " +
166
+ # "LoopSegs: #{self.segs_to_s(2)}"
167
+ self.rewind
168
+ #$PDbgLog.sig_return
169
+ return
170
+ end
171
+ @prev_lp_i += self[@prev_segs_i][0]
172
+ end
173
+ lp_i = @prev_lp_i
174
+ segs_i = @prev_segs_i
175
+ while lp_i <= cpix_i
176
+ if lp_i + self[segs_i][1] > cpix_i &&
177
+ (self[segs_i][1] += len_ch) < 1
178
+ self[segs_i][0] -= 1 if lp_i >= @lp.length
179
+ self[segs_i][1] += 1
180
+ end
181
+ segs_i += 1
182
+ break if segs_i >= self.length
183
+ lp_i += self[segs_i][0]
184
+ end
185
+ self[segs_i][0] += len_ch if segs_i < self.length
186
+ #$PDbgLog.puts_msg "Segs: #{self.segs_to_s(2)}"
187
+ #$PDbgLog.sig_return
188
+ end
189
+
190
+ def cpix_i_get_seg_i_start(cpix_i)
191
+ #$PDbgLog.sig_call(self)
192
+ #$PDbgLog.puts_msg "(#{cpix_i}) in #{self.to_s(0, true)}"
193
+ if @prev_lp_i > cpix_i || @prev_segs_i >= self.length ||
194
+ @prev_segs_i < 0
195
+ self.rewind
196
+ if @prev_segs_i >= self.length || @prev_lp_i > cpix_i
197
+ #$PDbgLog.sig_return
198
+ return [-1, 0]
199
+ end
200
+ end
201
+ loop do
202
+ break if @prev_lp_i + self[@prev_segs_i][1] > cpix_i
203
+ if @prev_segs_i+1 >= self.length
204
+ #$PDbgLog.sig_return
205
+ return [-@prev_segs_i-2, @prev_lp_i]
206
+ end
207
+ if @prev_lp_i + self[@prev_segs_i+1][0] > cpix_i
208
+ #$PDbgLog.sig_return
209
+ return [-@prev_segs_i-2, @prev_lp_i]
210
+ end
211
+ @prev_segs_i += 1; @prev_lp_i += self[@prev_segs_i][0]
212
+ end
213
+ #$PDbgLog.sig_return
214
+ [@prev_segs_i, @prev_lp_i]
215
+ end
216
+
217
+ def segs_to_s(detail_lev=3)
218
+ case detail_lev
219
+ when 0 then self.length.to_s
220
+ when 1 then self.collect{|seg| seg.inspect}.join(" ")
221
+ when 2 then segs = ""
222
+ self.each_abs_seg { |s, e| segs << "#{s}...#{e}, " }
223
+ segs[-2,2] = "" if segs.length > 1
224
+ segs
225
+ when 3 then segs = ""
226
+ self.each_abs_seg { |s, e| segs << "#{s}..#{e-1}, " }
227
+ segs[-2,2] = "" if segs.length > 1
228
+ segs
229
+ else
230
+ segs = ""
231
+ self.each_seg_with_index { |pixs, seg_i|
232
+ segs << self[seg_i].inspect << ": " <<
233
+ PixbufPix.path_to_s(pixs) << "; "
234
+ }
235
+ segs[-2,2] = "" if segs.length > 1
236
+ segs
237
+ end
238
+ end
239
+
240
+ def lp_to_s(repr_id=1)
241
+ case repr_id
242
+ when 0 then idmem = @lp.object_id &
243
+ ( (1 << (8*@lp.object_id.size)) - 1 )
244
+ "0x%x" % idmem
245
+ when 1 then lp = "#{@lp.length} element"
246
+ lp += "s" if @lp.length != 1
247
+ lp
248
+ else
249
+ PixbufPix.path_to_s(@lp)
250
+ end
251
+ end
252
+
253
+ def to_s(expand_segs=3, expand_loop=1)
254
+ "<#{self.class.name}: @lp: #{self.lp_to_s(expand_loop)
255
+ }; segs: #{self.segs_to_s(expand_segs)}>"
256
+ end
257
+
258
+ def inspect(expand_segs=3, expand_loop=1)
259
+ "<#{self.class.name}: @lp: #{self.lp_to_s(expand_loop)
260
+ }, @prev_segs_i=#{@prev_segs_i}, @prev_lp_i=#{@prev_lp_i.
261
+ inspect}; segs: #{self.segs_to_s(expand_segs)}>"
262
+ end
263
+ end
264
+
265
+ class AaPix
266
+ attr_accessor :border_dirs8_id, :subpix_path_dir
267
+ end
268
+
269
+ class AaPixWithPrevs < AaPix
270
+ attr_reader :prevs
271
+
272
+ def initialize(pixbuf, ax, ay)
273
+ super(pixbuf, ax, ay)
274
+ @prevs = Set.new
275
+ end
276
+
277
+ def tree_to_pp_s(indent_str='', indent_step=' ')
278
+ next_indent_str = indent_str + indent_step
279
+ res = indent_str + self.coords_to_s + ':' +
280
+ (@prevs.to_a.first.kind_of?(AaPixWithPrevs) ? "\n" : ' ') +
281
+ @prevs.collect{|pix| pix.kind_of?(AaPixWithPrevs) ?
282
+ pix.tree_to_pp_s(next_indent_str, indent_step) :
283
+ pix.inspect + "\n"
284
+ }.to_s
285
+ end
286
+ end
287
+
288
+ class BlobLoopCalc
289
+ attr_accessor :map_loop_end_i, :excl_set, :start_lp
290
+ attr_reader :blob, :lp, :prevs, :loop_segs, :not_done_pix_segs,
291
+ :layer_set, :blob_id
292
+
293
+ def initialize(blob, lp)
294
+ @blob = blob
295
+ @lp = lp.class.new
296
+ lp.each_with_index { |pix, pix_i|
297
+ pp = AaPixWithPrevs.new(pix.pixbuf, pix.ax, pix.ay)
298
+ pp.subpix_path_dir = pix.subpix_path_dir
299
+ pp.border_dirs8_id = pix.border_dirs8_id
300
+ pp.prevs << pix_i
301
+ @lp << pp
302
+ }
303
+ @start_lp = @lp
304
+ @excl_set = {}
305
+ @layer_set = {}; @lp.each { |pix| @layer_set[pix] = true }
306
+ @path_cache = {}
307
+ @tail = []
308
+ @neighbs = []
309
+ @next_lp = lp.class.new
310
+ @next_prevs = []
311
+ @prev_layer_set = {}
312
+ @map_loop_meth = self.method("map_loop_without_segs!")
313
+ @pixbuf = @blob.pixbuf
314
+ @blob_id = @blob.blob_id
315
+ end
316
+
317
+ def pix_map_into(pix,res, excl_set, prev_cpix, next_cpix, path_cache={},
318
+ tail=[], neighbs=[])
319
+ #$PDbgLog.sig_call(self)
320
+ #$PDbgLog.print_msg "excl_set: #{PixbufPix.yx_path_to_s(
321
+ # excl_set.keys.sort)}; "
322
+ #$PDbgLog.print_msg "#{pix.coords_to_s} prev_cpix#{prev_cpix.
323
+ # coords_to_s}, next_cpix#{next_cpix.coords_to_s}: "
324
+ if prev_cpix.y == next_cpix.y && prev_cpix.x == next_cpix.x
325
+ # $PDbgLog.print_msg "blob neighbs.: #{PixbufPix.path_to_s(
326
+ # @blobpix.pix_neighbs(pix)}: "
327
+ if @blob.pix_neighbs(pix).detect { |p|
328
+ !excl_set.include?(p.yx) && p.y != prev_cpix.y &&
329
+ p.x != prev_cpix.x && p.y != next_cpix.y &&
330
+ p.x != next_cpix.x
331
+ }
332
+ #$PDbgLog.sig_return("")
333
+ return []
334
+ else
335
+ #$PDbgLog.sig_return("nil")
336
+ return nil
337
+ end
338
+ end
339
+ pixbuf = @blob.pixbuf
340
+ blob_id = @blob.blob_id
341
+ if prev_cpix.virtual? || next_cpix.virtual? ||
342
+ !pixbuf.blob_id_at?(prev_cpix.x, prev_cpix.y, blob_id) ||
343
+ !pixbuf.blob_id_at?(next_cpix.x, next_cpix.y, blob_id)
344
+ get_neighbs_proc = proc { |cpix, ngb_res|
345
+ #$PDbgLog.sig_call(self)
346
+ # $PDbgLog.print_msg "get ngbs proc #{cpix.coords_to_s}: "
347
+ ngb_res.clear
348
+ PixbufPix::ADJACENT8.each { |adj|
349
+ p = pixbuf.get_pix(cpix.x+adj.x, cpix.y+adj.y)
350
+ ngb_res << p if p.yx != pix.yx &&
351
+ !excl_set.include?(p.yx) &&
352
+ (p.yx == next_cpix.yx ||
353
+ (pixbuf.blob_id_at?(p.x, p.y, blob_id) &&
354
+ ((adj4 = p.adj4).length < 4 ||
355
+ adj4.detect { |px| excl_set.include?(px.yx) ||
356
+ px.yx == pix.yx ||
357
+ !pixbuf.blob_id_at?(px.x, px.y, blob_id)
358
+ }))
359
+ )
360
+ }
361
+ #$PDbgLog.sig_return(PixbufPix.path_to_s(ngb_res))
362
+ ngb_res
363
+ }
364
+ else
365
+ get_neighbs_proc = proc { |cpix, ngb_res|
366
+ #$PDbgLog.sig_call(self)
367
+ # $PDbgLog.print_msg "get ngbs proc #{cpix.coords_to_s}: "
368
+ ngb_res.clear
369
+ @blob.pix_neighbs(cpix).each { |p|
370
+ ngb_res << p if
371
+ p.yx != pix.yx && !excl_set.include?(p.yx) &&
372
+ ((adj4 = p.adj4).length < 4 ||
373
+ adj4.detect { |px| excl_set.include?(px.yx) ||
374
+ px.yx == pix.yx ||
375
+ !pixbuf.blob_id_at?(px.x, px.y, blob_id)
376
+ })
377
+ }
378
+ #$PDbgLog.sig_return(PixbufPix.path_to_s(ngb_res))
379
+ ngb_res
380
+ }
381
+ end
382
+ if (res=prev_cpix.find_shortest_pix_path_into(res, next_cpix, 7,
383
+ path_cache, tail, neighbs, &get_neighbs_proc))
384
+ res.pop
385
+ end
386
+ #$PDbgLog.sig_return(res ? PixbufPix.path_to_s(res) : "nil")
387
+ res
388
+ end
389
+
390
+ def add_loop_segs
391
+ @loop_segs = LoopSegs.new(@lp) unless @loop_segs
392
+ @map_loop_meth = self.method("map_loop_with_segs!")
393
+ end
394
+
395
+ def map_loop_without_segs!(start_i, end_i)
396
+ $PDbgLog.sig_call(self)
397
+ #$PDbgLog.puts_msg "start_i=#{start_i}, end_i=#{end_i} in #{
398
+ # @lp.length} pixel#{@lp.length==1 ? "" : "s"}: #{
399
+ # PixbufPix.path_to_s(@lp)}"
400
+ next_px = nil
401
+ mapped_a_pix = false
402
+ i = start_i - 1
403
+ #nil_seq_i0 = nil_seq_end_i = 0
404
+ to = []
405
+ while (i+=1) < end_i
406
+ cpix = @lp[i]
407
+ if next_px && cpix != next_px
408
+ $PDbgLog.print_msg "i=#{i}, cpix#{cpix.coords_to_s
409
+ }, next_px#{next_px.coords_to_s}, start_i=#{
410
+ start_i}, end_i=#{end_i}); @lp.length=#{@lp.
411
+ length}; "
412
+ $PDbgLog.print_msg "@lp: #{PixbufPix.path_to_s(@lp)} "
413
+ end
414
+ unless self.pix_map_into(cpix, to, @excl_set,
415
+ @lp.at(i-1), next_px=@lp.at((i+1) % @lp.length),
416
+ @path_cache, @tail, @neighbs)
417
+ #if i - nil_seq_end_i < 2
418
+ # nil_seq_end_i += 1
419
+ #elsif nil_seq_end_i - nil_seq_i0 > 25
420
+ # @not_done_pix_segs.subtract_seg(
421
+ # nil_seq_i0, nil_seq_end_i)
422
+ #else
423
+ # nil_seq_start_i = nil_seq_end_i = i
424
+ #end
425
+ next
426
+ end
427
+ @lp[i,1] = to
428
+ new_i = i + to.length - 1
429
+ end_i += to.length - 1
430
+ @excl_set[cpix.yx] = true
431
+ #@not_done_pix_segs.map_cpix!(i, to)
432
+ #map_proc.call(cpix, i, to)
433
+ if to.empty? && @lp.length > 1 &&
434
+ @lp[i-1].y == @lp[lp_i=i % @lp.length].y &&
435
+ @lp[i-1].x == @lp[lp_i].x
436
+ @lp[lp_i,1] = nil
437
+ #@not_done_pix_segs.map_cpix!(i, to)
438
+ #map_proc.call(@lp[lp_i-1], i, [])
439
+ end_i -= 1
440
+ next_px = @lp[i % @lp.length]
441
+ end
442
+ i = new_i
443
+ mapped_a_pix = true
444
+ end
445
+ #@not_done_pix_segs.subtract_seg(nil_seq_i0, nil_seq_end_i) if
446
+ # nil_seq_end_i - nil_seq_i0 > 25
447
+ $PDbgLog.sig_return
448
+ mapped_a_pix
449
+ end
450
+
451
+ def map_loop_with_segs!(start_i, end_i)
452
+ $PDbgLog.sig_call(self)
453
+ #$PDbgLog.puts_msg "start_i=#{start_i}, end_i=#{end_i} in #{
454
+ # @lp.length} pixel#{@lp.length==1 ? "" : "s"}: #{
455
+ # PixbufPix.path_to_s(@lp)}"
456
+ next_px = nil
457
+ mapped_a_pix = false
458
+ i = start_i - 1
459
+ #nil_seq_i0 = nil_seq_end_i = 0
460
+ to = []
461
+ while (i+=1) < end_i
462
+ cpix = @lp[i]
463
+ if next_px && cpix != next_px
464
+ $PDbgLog.print_msg "i=#{i}, cpix#{cpix.coords_to_s
465
+ }, next_px#{next_px.coords_to_s}, start_i=#{
466
+ start_i}, end_i=#{end_i}); @lp.length=#{@lp.
467
+ length}; "
468
+ $PDbgLog.print_msg "@lp: #{PixbufPix.path_to_s(@lp)} "
469
+ end
470
+ unless self.pix_map_into(cpix, to, @excl_set,
471
+ @lp.at(i-1), next_px=@lp.at((i+1) % @lp.length),
472
+ @path_cache, @tail, @neighbs)
473
+ #if i - nil_seq_end_i < 2
474
+ # nil_seq_end_i += 1
475
+ #elsif nil_seq_end_i - nil_seq_i0 > 25
476
+ # @not_done_pix_segs.subtract_seg(
477
+ # nil_seq_i0, nil_seq_end_i)
478
+ #else
479
+ # nil_seq_start_i = nil_seq_end_i = i
480
+ #end
481
+ next
482
+ end
483
+ @lp[i,1] = to
484
+ new_i = i + to.length - 1
485
+ end_i += to.length - 1
486
+ @excl_set[cpix.yx] = true
487
+ #@not_done_pix_segs.map_cpix!(i, to)
488
+ @loop_segs.map_cpix!(i, to)
489
+ if to.empty? && @lp.length > 1 &&
490
+ @lp[i-1].y == @lp[lp_i=i % @lp.length].y &&
491
+ @lp[i-1].x == @lp[lp_i].x
492
+ @lp[lp_i,1] = nil
493
+ #@not_done_pix_segs.map_cpix!(i, to)
494
+ @loop_segs.map_cpix!(i, [])
495
+ end_i -= 1
496
+ next_px = @lp[i % @lp.length]
497
+ end
498
+ i = new_i
499
+ mapped_a_pix = true
500
+ end
501
+ #@not_done_pix_segs.subtract_seg(nil_seq_i0, nil_seq_end_i) if
502
+ # nil_seq_end_i - nil_seq_i0 > 25
503
+ $PDbgLog.sig_return
504
+ mapped_a_pix
505
+ end
506
+
507
+ def map_loop!(start_i, end_i)
508
+ return @map_loop_meth.call(start_i, end_i)
509
+ #$PDbgLog.sig_call(self)
510
+ seg_i,lp_i = *@not_done_pix_segs.cpix_i_get_seg_i_start(start_i)
511
+ #$PDbgLog.print_msg "#{start_i}...#{end_i}: seg_i=#{seg_i
512
+ # }, lp_i=#{lp_i}. "
513
+ if seg_i < 0
514
+ seg_i = -seg_i-1
515
+ if seg_i >= @not_done_pix_segs.length
516
+ #$PDbgLog.sig_return
517
+ return
518
+ end
519
+ lp_i += @not_done_pix_segs[seg_i][0]
520
+ end
521
+ mapped_a_pix = false
522
+ while lp_i < end_i
523
+ start_i = lp_i if lp_i > start_i
524
+ mapped_a_pix = true if @map_loop_meth.call(start_i,
525
+ lp_i + @not_done_pix_segs[seg_i][1])
526
+ break if (seg_i += 1) >= @not_done_pix_segs.length
527
+ lp_i += @not_done_pix_segs[seg_i]
528
+ end
529
+ #$PDbgLog.sig_return
530
+ mapped_a_pix
531
+ end
532
+
533
+ def map_loop_segments!
534
+ $PDbgLog.sig_call(self)
535
+ $PDbgLog.print_msg "#{@loop_segs.length} segment#{@loop_segs.
536
+ length == 1 ? "" : "s"}: "
537
+ $PDbgLog.new_progr
538
+ $PDbgLog.progr.total_progr_units = @blob.length
539
+ if @blob.external_contours
540
+ $PDbgLog.progr.total_progr_units += @lp.length
541
+ end
542
+ $PDbgLog.progr.show_as = "/ pixel#{@blob.length==1 ? "" : "s"}"
543
+ mapped_a_pix = true
544
+ while mapped_a_pix
545
+ mapped_a_pix = false
546
+ lp_start_i = @loop_segs[0][0]
547
+ @loop_segs.each_abs_seg { |start_i, end_i|
548
+ #$PDbgLog.print_msg "lp_start_i=#{lp_start_i}; Segment [#{
549
+ # start_i}; #{end_i}) " #; @lp: #{PixbufPix.path_to_s(@lp)
550
+ #} -> "
551
+ if end_i > lp_start_i
552
+ mapped_a_pix = true if self.map_loop!(
553
+ [lp_start_i, start_i].max, end_i)
554
+ lp_start_i = end_i
555
+ end
556
+ #$PDbgLog.puts_msg "Segment [#{start_i}; #{end_i}); Loop: #{
557
+ # PixbufPix.path_to_s(@lp)}"
558
+ $PDbgLog.progr.progr_units = excl_set.length
559
+ }
560
+ end
561
+ $PDbgLog.sig_return(" done.")
562
+ end
563
+ end
564
+
565
+ class BlobLoopCalc
566
+ def self.situation8_id_lms(id)
567
+ [id & 0b111, (id>>3) & 0b111, id >> 6]
568
+ end
569
+
570
+ def self.situation8_path(start_p, end_p, adj8_s, dir=+1)
571
+ path = []
572
+ pos = start_p + dir
573
+ if dir == 1
574
+ i_ofs = -1
575
+ else
576
+ i_ofs = (start_p == end_p ? 7 : 6)
577
+ end
578
+ while (pos % 8) != end_p
579
+ if adj8_s[pos-start_p+i_ofs] == 0 && (pos % 2 == 1 ||
580
+ (!path.empty? && adj8_s[path[-1]-start_p+i_ofs] == 0))
581
+ if start_p == end_p
582
+ d = (start_p % 2 == 0 ? 1 : 2)
583
+ if (-d..d).detect { |i| adj8_s[i] == 1 }
584
+ return []
585
+ else
586
+ return nil
587
+ end
588
+ else
589
+ return nil
590
+ end
591
+ end
592
+ if path.length > 0 && path[-1] % 2 == 0
593
+ path[-1] = pos
594
+ else
595
+ path << pos
596
+ end
597
+ pos += dir
598
+ end
599
+ if !path.empty? && path[-1] % 2 == 0
600
+ if path.length > 1
601
+ path.pop
602
+ elsif adj8_s[path[-1]-start_p+i_ofs] == 0
603
+ return nil
604
+ end
605
+ end
606
+ path.collect{|i| PixbufPix::ADJ8_CCW_ORDER.at(i % 8)}
607
+ end
608
+
609
+ PIX_SITUS8_4_PATHS = (0...(1 << 13)).collect { |sit_id|
610
+ start_p, end_p, adj8_s = self.situation8_id_lms(sit_id)
611
+ i_path = self.situation8_path(start_p, end_p, adj8_s, +1)
612
+ d_path = self.situation8_path(start_p, end_p, adj8_s, -1)
613
+ if i_path
614
+ if d_path
615
+ [i_path, d_path]
616
+ else
617
+ i_path
618
+ end
619
+ elsif d_path
620
+ d_path
621
+ else
622
+ nil
623
+ end
624
+ }
625
+
626
+ def pix_adj8(pix)
627
+ pix.adj8.find_all { |px| !@excl_set.include?(px.a_yx) }
628
+ end
629
+
630
+ def pix_adj8_s(pix)
631
+ res = 0
632
+ width=pix.pixbuf.picture_width; height=pix.pixbuf.picture_height
633
+ b = 1
634
+ PixbufPix::ADJ8_CCW_ORDER.each { |adj|
635
+ x = pix.x + adj.x; y = pix.y + adj.y
636
+ if x >= 0 && x < width && y >= 0 && y < height
637
+ res |= b
638
+ end
639
+ b = (b << 1)
640
+ }
641
+ res
642
+ end
643
+
644
+ def xy_mappable_to(x, y)
645
+ #$PDbgLog.sig_call(self)
646
+ #res =
647
+ x >= 0 && x < @pixbuf.picture_width && y >= 0 &&
648
+ y < @pixbuf.picture_height && !@excl_set.include?(
649
+ AaPix.a_yx(x, y)) && @pixbuf.blob_id_at?(x, y, @blob_id)
650
+ #$PDbgLog.sig_return("(#{x}, #{y}): #{res.inspect}")
651
+ #res
652
+ end
653
+
654
+ def pix_prev_next_situation8_id(pix, prev_p, next_p)
655
+ #$PDbgLog.sig_call(self)
656
+ if !pix.a_neighb8?(prev_p,0.51) || !pix.a_neighb8?(next_p, 0.51)
657
+ warn("#{self.class.name}.pix_prev_next_situation8_id: "+
658
+ "BUG: Pixels not contiguous: pix#{pix.coords_to_s
659
+ }, prev_p#{prev_p.coords_to_s}, next_p#{next_p.
660
+ coords_to_s}")
661
+ end
662
+ x_p = (prev_p.ax - pix.ax).round; y_p = (prev_p.ay-pix.ay).round
663
+ x_n = (next_p.ax - pix.ax).round; y_n = (next_p.ay-pix.ay).round
664
+ if x_p > 1 then x_p = 1; elsif x_p < -1 then x_p = -1; end
665
+ if y_p > 1 then y_p = 1; elsif y_p < -1 then y_p = -1; end
666
+ if x_n > 1 then x_n = 1; elsif x_n < -1 then x_n = -1; end
667
+ if y_n > 1 then y_n = 1; elsif y_n < -1 then y_n = -1; end
668
+ if (x_p.abs < 1 && y_p.abs < 1) || (x_n.abs < 1 && y_n.abs < 1)
669
+ warn("#{self.class.name}.pix_prev_next_situation8_id: "+
670
+ "BUG: Two subsequent pix in loop are the same: pix#{
671
+ pix.coords_to_s}, prev_p#{prev_p.coords_to_s
672
+ }, next_p#{next_p.coords_to_s}")
673
+ end
674
+ # $PDbgLog.print_msg "pix#{pix.coords_to_s}, prev_p#{prev_p.
675
+ # coords_to_s}, next_p#{next_p.coords_to_s}"+#"; x_p=#{x_p
676
+ # }, y_p=#{y_p}; x_n=#{x_n}, y_n=#{y_n}"
677
+ #$PDbgLog.print_msg ": "
678
+ i_p = i_n = nil
679
+ b = 1
680
+ adj8_s = 0
681
+ Xy::ADJ8_CCW_ORDER.each_with_index{ |a,i|
682
+ if a.y == y_p && a.x == x_p
683
+ i_p = i
684
+ i_n = i if a.y == y_n && a.x == x_n
685
+ adj8_s <<= 6 - (i - (i_n ? 1 : 0))
686
+ b = 1
687
+ elsif a.y == y_n && a.x == x_n
688
+ i_n = i
689
+ else
690
+ x = pix.ax + a.x; y = pix.ay + a.y
691
+ if a.x < 0
692
+ x = x.ceil
693
+ if a.y < 0 then y = y.ceil
694
+ flag = self.xy_mappable_to(x, y)
695
+ elsif a.y > 0 then y = y.floor
696
+ flag = self.xy_mappable_to(x, y)
697
+ else
698
+ flag = self.xy_mappable_to(x, y.floor)&&
699
+ self.xy_mappable_to(x, y.ceil)
700
+ end
701
+ elsif a.x > 0
702
+ x = x.floor
703
+ if a.y < 0 then y = y.ceil
704
+ flag = self.xy_mappable_to(x, y)
705
+ elsif a.y > 0 then y = y.floor
706
+ flag = self.xy_mappable_to(x, y)
707
+ else
708
+ flag = self.xy_mappable_to(x, y.floor)&&
709
+ self.xy_mappable_to(x, y.ceil)
710
+ end
711
+ elsif a.y < 0
712
+ y = y.ceil
713
+ flag = self.xy_mappable_to(x.floor, y)&&
714
+ self.xy_mappable_to(x.ceil, y)
715
+ elsif a.y > 0
716
+ y = y.floor
717
+ flag = self.xy_mappable_to(x.floor, y)&&
718
+ self.xy_mappable_to(x.ceil, y)
719
+ elsif a.y == 0
720
+ flag = self.xy_mappable_to(x.floor,
721
+ y.floor) &&
722
+ self.xy_mappable_to(x.floor,y.ceil) &&
723
+ self.xy_mappable_to(x.ceil,y.floor) &&
724
+ self.xy_mappable_to(x.ceil,y.ceil)
725
+ end
726
+ adj8_s |= b if flag
727
+ #$PDbgLog.print_msg "(#{pix.ax+a.x}, #{
728
+ # pix.ay+a.y})[#{i}]: #{flag} "
729
+ b = (b << 1)
730
+ end
731
+ }
732
+ #$PDbgLog.print_msg "i_p=#{i_p.inspect}, i_n=#{i_n.inspect}: "
733
+ #res =
734
+ i_p | (i_n << 3) | (adj8_s << 6)
735
+ #$PDbgLog.sig_return("%b" % res)
736
+ #res
737
+ end
738
+
739
+ def pix_map8_4_into(pix, prev_p, next_p, res_prev_p, res=[[], []])
740
+ #$PDbgLog.sig_call(self)
741
+ #$PDbgLog.print_msg pix.coords_to_s + (pix.border_dirs8_id ?
742
+ # "+border_dirs8_id=%b" % pix.border_dirs8_id : '') +
743
+ # (pix.subpix_path_dir ? "+subpix_path_dir=" +
744
+ # pix.subpix_path_dir.inspect : '') +
745
+ # ", prev_p#{prev_p.coords_to_s}, next_p#{next_p.
746
+ # coords_to_s}: "
747
+ #$PDbgLog.print_msg "res_prev_p#{res_prev_p ?
748
+ # res_prev_p.coords_to_s : "=" + res_prev_p.inspect}: "
749
+ if pix.y != pix.ay || pix.x != pix.ax
750
+ res[0].clear
751
+ res[0] << pix
752
+ #$PDbgLog.sig_return('self')
753
+ return res[0]
754
+ end
755
+ situ_id = self.pix_prev_next_situation8_id(pix, prev_p, next_p)
756
+ #$PDbgLog.print_msg "situ.: %b " % situ_id
757
+ if (paths = BlobLoopCalc::PIX_SITUS8_4_PATHS.at(situ_id))
758
+ if paths[0].kind_of?(Array)
759
+ res[0].clear; res[1].clear
760
+ paths[0].each{|a| res[0] << AaPixWithPrevs.new(@pixbuf,
761
+ pix.x + a.x, pix.y + a.y) }
762
+ paths[1].each{|a| res[1] << AaPixWithPrevs.new(@pixbuf,
763
+ pix.x + a.x, pix.y + a.y) }
764
+ #$PDbgLog.puts_msg "paths: 0: #{PixbufPix.path_to_s(
765
+ # res[0])}, 1: #{PixbufPix.path_to_s(res[1])}"
766
+ if !res_prev_p
767
+ if pix.subpix_path_dir == -1
768
+ # $PDbgLog.sig_return("1: " + PixbufPix.path_to_s(
769
+ # res[1]))
770
+ res[1]
771
+ else
772
+ # $PDbgLog.sig_return("0: " + PixbufPix.path_to_s(
773
+ # res[0]))
774
+ res[0]
775
+ end
776
+ elsif !res[0].empty? &&
777
+ !res_prev_p.a_neighb8?(res[0][0])
778
+ if !(res[1]).empty? &&
779
+ !res_prev_p.neighb8?(res[1][0])
780
+ res[1].clear
781
+ res[1] << pix
782
+ end
783
+ # $PDbgLog.sig_return("1: " + PixbufPix.path_to_s(
784
+ # res[1]))
785
+ res[1]
786
+ elsif pix.subpix_path_dir == -1 &&
787
+ (res[1].empty? || res_prev_p.a_neighb8?(res[1][0]))
788
+ # $PDbgLog.sig_return("1: " + PixbufPix.path_to_s(
789
+ # res[1]))
790
+ res[1]
791
+ else
792
+ # $PDbgLog.sig_return("0: " + PixbufPix.path_to_s(
793
+ # res[0]))
794
+ res[0]
795
+ end
796
+ else
797
+ res[0].clear
798
+ paths.each{|a| res[0] << AaPixWithPrevs.new(@pixbuf,
799
+ pix.x + a.x, pix.y + a.y) }
800
+ if res_prev_p && !res[0].empty? &&
801
+ !res_prev_p.neighb8?(res[0][0])
802
+ res[0].clear
803
+ res[0] << pix
804
+ end
805
+ #$PDbgLog.sig_return(PixbufPix.path_to_s(res[0]))
806
+ res[0]
807
+ end
808
+ else
809
+ res[0].clear
810
+ res[0] << pix
811
+ #$PDbgLog.sig_return('self')
812
+ res[0]
813
+ end
814
+ end
815
+
816
+ def pix_trace_min_seg(pix)
817
+ #$PDbgLog.sig_call(self)
818
+ #$PDbgLog.print_msg "#{pix.coords_to_s}: "
819
+ while !(p_prevs = pix.prevs.to_a).detect{|p| !p.kind_of?(AaPix)}
820
+ ngbs_arr = pix.semi_neighbs8_contig_arr(p_prevs)
821
+ if (segs = AaXy.semi_neighbs8_contig_arr_get_segs(
822
+ ngbs_arr)).empty?
823
+ pix = p_prevs.first
824
+ next
825
+ end
826
+ p_next_i = ngbs_arr[segs[0][0]]
827
+ pix = p_prevs[p_next_i]
828
+ end
829
+ #$PDbgLog.sig_return(p_prevs)
830
+ p_prevs.detect { |p| p.kind_of?(Integer) }
831
+ end
832
+
833
+ def pix_trace_max_seg(pix)
834
+ #$PDbgLog.sig_call(self)
835
+ #$PDbgLog.print_msg "#{pix.coords_to_s}: "
836
+ while !(p_prevs = pix.prevs.to_a).detect{|p| !p.kind_of?(AaPix)}
837
+ ngbs_arr = pix.semi_neighbs8_contig_arr(p_prevs)
838
+ if (segs = AaXy.semi_neighbs8_contig_arr_get_segs(
839
+ ngbs_arr)).empty?
840
+ pix = p_prevs.first
841
+ next
842
+ end
843
+ p_next_i = ngbs_arr[segs.last[1] - 1]
844
+ pix = p_prevs[p_next_i]
845
+ end
846
+ #$PDbgLog.sig_return(p_prevs)
847
+ p_prevs.detect { |p| p.kind_of?(Integer) }
848
+ end
849
+
850
+ def pix_trace_mid_seg(pix, p_prevs, i_err)
851
+ #$PDbgLog.sig_call(self)
852
+ #$PDbgLog.print_msg "pix#{pix.coords_to_s} "
853
+ path = [pix]
854
+ i_mid = 0; seg = [0, 0]
855
+ pixbuf = pix.pixbuf
856
+ while !p_prevs.detect { |p| !p.kind_of?(AaPixWithPrevs) }
857
+ ngbs_arr = pix.semi_neighbs8_contig_arr(p_prevs)
858
+ #$PDbgLog.print_msg "prevs: #{Xy.path_to_s(
859
+ # p_prevs)}, i_err=#{i_err}; "
860
+ unless (segs=AaXy.semi_neighbs8_contig_arr_get_segs(
861
+ ngbs_arr)).empty?
862
+ seg = [segs.first[0], segs.last[1]]
863
+ p_i_mid = Float(seg[0] + seg[1]-1 - i_err) / 2.0
864
+ i_mid = p_i_mid.round
865
+ #$PDbgLog.print_msg "ngbs_arr=#{ngbs_arr.inspect
866
+ # }, segs=#{segs.inspect}, seg=#{seg.inspect
867
+ # }, p_i_mid=#{p_i_mid}, i_mid=#{i_mid}; "
868
+ if i_mid >= seg[1]
869
+ i_mid = seg[1]-1
870
+ elsif i_mid < seg[0]
871
+ i_mid = seg[0]
872
+ end
873
+ #$PDbgLog.print_msg "i_mid=#{i_mid}; "
874
+ i_err = p_i_mid - i_mid
875
+ a = AaXy::SEMI_ADJ8[16 + i_mid % 16]
876
+ pix = AaPix.new(pixbuf, pix.ax+a.ax,pix.ay+a.ay)
877
+ end
878
+ next_prevs = []
879
+ p_prevs.each { |px|
880
+ if pix.a_eql?(px)
881
+ next_prevs = px.prevs.to_a; break
882
+ elsif pix.a_close_neighb?(px) &&
883
+ !path[-1].a_close_neighb?(px)
884
+ next_prevs << px; next
885
+ end
886
+ px.prevs.each { |t_pix|
887
+ next unless t_pix.kind_of?(AaPixWithPrevs)
888
+ if pix.a_neighb8?(t_pix) && !path[-1].
889
+ semi_close_neighb?(t_pix)
890
+ next_prevs << t_pix
891
+ end
892
+ }
893
+ }
894
+ if next_prevs.empty?
895
+ if ngbs_arr[i_mid]
896
+ p_next_i = ngbs_arr[i_mid]
897
+ else
898
+ d = 1
899
+ loop do
900
+ if i_mid - d < seg[0]
901
+ p_next_i = ngbs_arr[i_mid = seg[0]]; break
902
+ elsif i_mid+d >= seg[1]
903
+ p_next_i = ngbs_arr[i_mid = seg[1]-1]; break
904
+ elsif i_mid - d >= seg[0] && ngbs_arr[i_mid-d]
905
+ p_next_i = ngbs_arr[i_mid = i_mid-d]; break
906
+ elsif ngbs_arr[i_mid+d]
907
+ p_next_i = ngbs_arr[i_mid = i_mid+d]; break
908
+ end
909
+ d += 1
910
+ end
911
+ end
912
+ p_next_i = 0 unless p_next_i
913
+ pix = p_prevs[p_next_i]
914
+ next_prevs = pix.prevs.to_a
915
+ end
916
+ p_prevs = next_prevs
917
+ if path.length > 1 && path[-2].a_neighb8?(pix)
918
+ path[-1] = pix
919
+ else
920
+ path << pix
921
+ end
922
+ #$PDbgLog.print_msg "pix#{pix.coords_to_s} "
923
+ end
924
+ #$PDbgLog.sig_return(p_prevs)
925
+ [p_prevs.detect {|p| p.kind_of?(Integer)}, path]
926
+ end
927
+
928
+ def pix_trace_seg(pix, pix_prevs, ngbs_arr, seg_i_0, seg_i_end)
929
+ #$PDbgLog.sig_call(self)
930
+ #$PDbgLog.puts_msg "pix#{pix.coords_to_s},pix_prevs: #{Xy.
931
+ # path_to_s(pix_prevs)}, seg_i_0=#{seg_i_0}, seg_i_end=#{
932
+ # seg_i_end}"
933
+ p_min_next_i = ngbs_arr[seg_i_0]
934
+ p_max_next_i = ngbs_arr[seg_i_end-1]
935
+ p_i_mid = Float(seg_i_0 + seg_i_end-1) / 2.0
936
+ i_mid = p_i_mid.round
937
+ if i_mid >= seg_i_end
938
+ i_mid = seg_i_end-1
939
+ elsif i_mid < seg_i_0
940
+ i_mid = seg_i_0
941
+ end
942
+ a = AaXy::SEMI_ADJ8[16 + i_mid % 16]
943
+ mid_pix = AaPix.new(pix.pixbuf, pix.ax+a.ax, pix.ay+a.ay)
944
+ # $PDbgLog.puts_msg "i_mid=#{i_mid},mid_pix#{mid_pix.coords_to_s}"
945
+ p_mid_next_prevs = []
946
+ pix_prevs.each { |px|
947
+ if mid_pix.a_eql?(px)
948
+ p_mid_next_prevs = px.prevs.to_a; break
949
+ elsif mid_pix.a_close_neighb?(px) &&
950
+ !pix.a_close_neighb?(px)
951
+ p_mid_next_prevs << px; next
952
+ end
953
+ #$PDbgLog.print_msg "px#{px.coords_to_s} "
954
+ px.prevs.each_with_index { |t_pix, t_i|
955
+ next unless t_pix.kind_of?(AaPixWithPrevs)
956
+ #$PDbgLog.print_msg "t_pix#{t_pix.coords_to_s} "
957
+ if mid_pix.a_neighb8?(t_pix) && !pix.
958
+ semi_close_neighb?(t_pix)
959
+ p_mid_next_prevs << t_pix
960
+ end
961
+ }
962
+ }
963
+ #$PDbgLog.puts_msg "p_mid_next_prevs: #{p_mid_next_prevs.
964
+ # join(' ')}"
965
+ if p_mid_next_prevs.empty?
966
+ if ngbs_arr[i_mid]
967
+ p_next_i = ngbs_arr[i_mid]
968
+ else
969
+ d = 1
970
+ loop do
971
+ if i_mid - d < seg_i_0
972
+ p_next_i = ngbs_arr[i_mid = seg[0]]; break
973
+ elsif i_mid+d >= seg_i_end
974
+ p_next_i = ngbs_arr[i_mid = seg[1]-1]; break
975
+ elsif i_mid - d >= seg_i_0 && ngbs_arr[i_mid-d]
976
+ p_next_i = ngbs_arr[i_mid = i_mid-d]; break
977
+ elsif ngbs_arr[i_mid+d]
978
+ p_next_i = ngbs_arr[i_mid = i_mid+d]; break
979
+ end
980
+ d += 1
981
+ end
982
+ end
983
+ p_next_i = 0 unless p_next_i
984
+ mid_pix = pix_prevs[p_next_i]
985
+ p_mid_next_prevs = pix_prevs[p_next_i].prevs.to_a
986
+ end
987
+ #$PDbgLog.sig_return
988
+ [self.pix_trace_min_seg(pix_prevs[p_min_next_i]),
989
+ self.pix_trace_mid_seg(mid_pix,p_mid_next_prevs,p_i_mid-i_mid),
990
+ self.pix_trace_max_seg(pix_prevs[p_max_next_i])]
991
+ end
992
+
993
+ def lp_add_pix(pix, old_pix, prev_lp_i)
994
+ #$PDbgLog.sig_call(self)
995
+ if pix != old_pix && @prev_layer_set.include?(pix)
996
+ new_pix = AaPixWithPrevs.new(pix.pixbuf,
997
+ (Float(pix.x)+old_pix.x)/2.0,
998
+ (Float(pix.y)+old_pix.y)/2.0)
999
+ else
1000
+ new_pix = pix
1001
+ end
1002
+ #$PDbgLog.print_msg "pix#{pix.coords_to_s}, old_pix#{old_pix.
1003
+ # coords_to_s}, prev_lp_i=#{prev_lp_i}, new_pix#{new_pix.
1004
+ # coords_to_s}: " #+ "\n"
1005
+ #$PDbgLog.puts_msg "@next_lp: #{PixbufPix.path_to_s(@next_lp)}"
1006
+ if @next_lp[-1] && @next_lp[-1].a_eql?(new_pix)
1007
+ @layer_set[new_pix] = @next_lp.length-1
1008
+ if new_pix.a_eql?(old_pix)
1009
+ @next_lp[-1].prevs.merge(old_pix.prevs)
1010
+ else
1011
+ @next_lp[-1].prevs << old_pix
1012
+ end
1013
+ else
1014
+ if new_pix.a_eql?(old_pix)
1015
+ new_pix.prevs.merge(old_pix.prevs)
1016
+ else
1017
+ new_pix.prevs << old_pix
1018
+ end
1019
+ @layer_set[new_pix] = @next_lp.length
1020
+ @next_lp << new_pix
1021
+ end
1022
+ if @next_lp[-2] && !@next_lp[-2].neighb8?(@next_lp[-1])
1023
+ warn("#{self.class.name}.lp_add_pix: Uncontiguous loop: #{
1024
+ PixbufPix.path_to_s(@next_lp[-3,3])
1025
+ } from #{PixbufPix.path_to_s(@lp[prev_lp_i-3,6])}")
1026
+ end
1027
+ #$PDbgLog.sig_return
1028
+ new_pix
1029
+ end
1030
+
1031
+ def lp_optimize_out_4neighbs(lp)
1032
+ return lp if lp.length < 3
1033
+ #$PDbgLog.sig_call(self)
1034
+ #$PDbgLog.puts_msg "lp: #{Xy.path_to_s(lp)}"
1035
+ i = 0
1036
+ while i < lp.length
1037
+ p0 = lp.at(i-1)
1038
+ p1 = lp.at(i)
1039
+ p2 = lp.at((i+1) % lp.length)
1040
+ #$PDbgLog.print_msg "p0#{p0.coords_to_s}->p1#{
1041
+ # p1.coords_to_s}[#{i}],p2#{p2.coords_to_s}: "
1042
+ if p1.a_neighb4?(p0) && p1.a_neighb4?(p2) &&
1043
+ p0.a_neighb8?(p2) && !p0.a_close_neighb?(p2)
1044
+ #$PDbgLog.puts "delete #{i}"
1045
+ lp.delete_at(i)
1046
+ break if lp.length < 3
1047
+ else
1048
+ i += 1
1049
+ #$PDbgLog.puts "."
1050
+ end
1051
+ end
1052
+ #$PDbgLog.sig_return
1053
+ end
1054
+
1055
+ def next_layer!(i_p_map1=[[], []], i_p_map2=[[], []])
1056
+ #$PDbgLog.sig_call(self)
1057
+ #$PDbgLog.puts_msg "@lp (#{@lp.length}): #{Xy.path_to_s(@lp)}"
1058
+ #$PDbgLog.puts_msg "@lp (#{@lp.length}): #{@lp.inspect}"
1059
+ if @lp.length < 3
1060
+ #$PDbgLog.sig_return(false)
1061
+ return false
1062
+ end
1063
+ @next_lp.clear
1064
+ tmp = @layer_set
1065
+ @layer_set = @prev_layer_set
1066
+ @prev_layer_set = tmp
1067
+ @layer_set.clear
1068
+ mapped_a_pix = false
1069
+ p0 = @lp[-3]
1070
+ p1 = @lp[i1=-2]
1071
+ p2 = @lp[i2=-1]
1072
+ p3 = @lp[0]
1073
+ i3 = 0
1074
+ p1_map = self.pix_map8_4_into(p1, p0, p2, nil, i_p_map1)
1075
+ while i3 < @lp.length
1076
+ while (p2_map=self.pix_map8_4_into(p2,p1,p3, p1_map[-1],
1077
+ i_p_map2)).empty? && i3 + 1 < @lp.length
1078
+ #$PDbgLog.print_msg "Gobble p2: p0#{p0.coords_to_s
1079
+ # }, p1#{p1.coords_to_s}[#{i1}], p2#{p2.coords_to_s
1080
+ # }[#{i2}], p3#{p3.coords_to_s}[#{i3}]: "
1081
+ begin
1082
+ if i3 + 1 >= @lp.length
1083
+ break
1084
+ end
1085
+ if p1 == p2
1086
+ p1.prevs.merge(p2.prevs)
1087
+ else
1088
+ p1.prevs << p2
1089
+ end
1090
+ @lp.at(i3).prevs << p2
1091
+ i2 = i3; i3 += 1
1092
+ p2 = p3; p3 = @lp.at(i3)
1093
+ #$PDbgLog.print_msg "p2#{p2.coords_to_s}[#{i2
1094
+ # }], p3#{p3.coords_to_s}[#{i3}]; "
1095
+ end while p1==p2 || (p2_map=self.pix_map8_4_into(p2,
1096
+ p1, p3, p1_map[-1], i_p_map2)).empty?
1097
+ if p1 == p2
1098
+ p1_map.clear; break
1099
+ end
1100
+ p1_map << p1 if (p1_map = self.pix_map8_4_into(p1, p0,
1101
+ p2, @next_lp[-1], i_p_map1)).empty?
1102
+ mapped_a_pix = true
1103
+ end
1104
+ #$PDbgLog.puts_msg "p0#{p0.coords_to_s}->p1#{p1.
1105
+ # coords_to_s}[#{i1}]->p2#{p2.coords_to_s}[#{i2
1106
+ # }], p3#{p3.coords_to_s}[#{i3}]: #{Xy.path_to_s(
1107
+ # p1_map)}"
1108
+ p1_map.each { |pix| mapped_a_pix = true if
1109
+ !self.lp_add_pix(pix, p1, i1).a_eql?(p1)
1110
+ #$PDbgLog.print_msg @next_lp[-1].tree_to_pp_s
1111
+ }
1112
+ p1_map = p2_map
1113
+ tmp = i_p_map2; i_p_map2 = i_p_map1; i_p_map1 = tmp
1114
+ i1 = i2; i2 = i3; i3 += 1
1115
+ p0 = p1; p1 = p2; p2 = p3; p3 = @lp.at(i3)
1116
+ end
1117
+ if mapped_a_pix
1118
+ #$PDbgLog.puts_msg "@next_lp (#{@next_lp.length}): #{
1119
+ # Xy.path_to_s(@next_lp)}"
1120
+ while @next_lp.length > 1 &&
1121
+ @next_lp[-1].a_eql?(@next_lp[0])
1122
+ @next_lp[0].prevs.merge(@next_lp[-1].prevs)
1123
+ @next_lp.pop
1124
+ end
1125
+ tmp = @lp
1126
+ @lp = @next_lp
1127
+ @next_lp = tmp
1128
+ @prev_layer_set.each_key { |p_map|
1129
+ @excl_set[p_map.a_yx] = true unless
1130
+ @layer_set.include?(p_map) }
1131
+ #$PDbgLog.puts_msg "@layer_set: #{Xy.path_to_s(
1132
+ # @layer_set.keys.to_a.sort)}"
1133
+ #$PDbgLog.puts_msg "@excl_set (#{@excl_set.length}): #{
1134
+ # Xy.yx_path_to_s(@excl_set.keys.to_a.sort)}"
1135
+ #$PDbgLog.puts_msg "@lp (#{@lp.length}): #{Xy.path_to_s(
1136
+ # @lp)}"
1137
+ #$PDbgLog.sig_return(true)
1138
+ true
1139
+ else
1140
+ #$PDbgLog.puts_msg "@lp (#{@lp.length}): #{Xy.path_to_s(
1141
+ # @lp)}"
1142
+ # $PDbgLog.puts_msg @lp.collect {|pix| pix.tree_to_pp_s }.
1143
+ # join("\n")
1144
+ self.lp_optimize_out_4neighbs(@lp)
1145
+ #$PDbgLog.puts_msg "@excl_set (#{@excl_set.length}): #{
1146
+ # Xy.yx_path_to_s(@excl_set.keys.to_a.sort)}"
1147
+ #$PDbgLog.puts_msg "@lp (#{@lp.length}): #{Xy.path_to_s(
1148
+ # @lp)}"
1149
+ # $PDbgLog.puts_msg @lp.collect {|pix| pix.tree_to_pp_s }.
1150
+ # join("\n")
1151
+ #$PDbgLog.puts_msg @lp.collect {|pix| pix.inspect}.join(
1152
+ # "\n")
1153
+ #$PDbgLog.sig_return(false)
1154
+ false
1155
+ end
1156
+ end
1157
+
1158
+ def self.pix_in_curve_blob?(pix, blob_id)
1159
+ !pix.virtual? && (pix.a_yx != pix.yx || pix.pixbuf.blob_id_at?(
1160
+ pix.x, pix.y, blob_id))
1161
+ end
1162
+
1163
+ def self.pix_neighbs_add(pix_neighbs, pix, neighb, blob_id)
1164
+ #$PDbgLog.sig_call(self)
1165
+ #$PDbgLog.print_msg pix.coords_to_s + ": " + neighb.coords_to_s
1166
+ if pix_neighbs.include?(pix.a_yx)
1167
+ unless (dest=pix_neighbs[pix.a_yx]).
1168
+ detect { |m| m.a_eql?(neighb) }
1169
+ dest << neighb
1170
+ end
1171
+ else
1172
+ pix_neighbs[pix.a_yx] = [neighb]
1173
+ end
1174
+ # $PDbgLog.sig_return(' = ' + Xy.path_to_s(pix_neighbs[pix.a_yx]))
1175
+ end
1176
+
1177
+ def self.loop_calcs_mid_ln_segs!(calcs, pix_neighbs)
1178
+ $PDbgLog.sig_call(self)
1179
+ pixbuf = calcs[0].lp[0].pixbuf
1180
+ excl_set = calcs[0].excl_set
1181
+ calcs.each { |calc| calc.start_lp = calc.lp.dup
1182
+ calc.excl_set = excl_set }
1183
+ active_calcs = calcs.dup
1184
+ $PDbgLog.print_msg "Skeletonizing: "
1185
+ $PDbgLog.new_progr
1186
+ blob = calcs[0].blob
1187
+ $PDbgLog.progr.total_progr_units = blob.length
1188
+ if blob.external_contours
1189
+ calcs.each { |calc|
1190
+ $PDbgLog.progr.total_progr_units += calc.lp.length }
1191
+ end
1192
+ $PDbgLog.progr.show_as = "/ pixel#{$PDbgLog.progr.
1193
+ total_progr_units==1 ? "" : "s"}"
1194
+ i_p_map = [[], []]
1195
+ c_i = -1
1196
+ while !active_calcs.empty?
1197
+ c_i = 0
1198
+ while c_i < active_calcs.length
1199
+ if active_calcs[c_i].next_layer!(i_p_map)
1200
+ c_i += 1
1201
+ else
1202
+ active_calcs.delete_at(c_i)
1203
+ end
1204
+ $PDbgLog.progr.progr_units = excl_set.length
1205
+ end
1206
+ end
1207
+ $PDbgLog.puts_msg " done."
1208
+ core = Set.new
1209
+ core_debris = []
1210
+ prevs = {}
1211
+ blob_id = calcs[0].blob_id
1212
+ if pix_neighbs
1213
+ calcs.each { |calc| next if (lp = calc.lp).length < 2
1214
+ #$PDbgLog.puts_msg "lp: #{Xy.path_to_s(lp)}"
1215
+ if lp.length == 2
1216
+ p0, p1 = *lp
1217
+ self.pix_neighbs_add(pix_neighbs, p0.dup_aapix,
1218
+ p1.dup_aapix, blob_id)
1219
+ self.pix_neighbs_add(pix_neighbs, p1.dup_aapix,
1220
+ p0.dup_aapix, blob_id)
1221
+ next
1222
+ end
1223
+ p0 = lp[-2]; p1 = lp[-1]
1224
+ lp.each { |p2|
1225
+ self.pix_neighbs_add(pix_neighbs, p1.dup_aapix,
1226
+ p0.dup_aapix, blob_id)
1227
+ self.pix_neighbs_add(pix_neighbs, p1.dup_aapix,
1228
+ p2.dup_aapix, blob_id)
1229
+ p0 = p1; p1 = p2
1230
+ }
1231
+ }
1232
+ end
1233
+ calcs.each { |calc| calc.lp.each_with_index { |pix, pix_i|
1234
+ core << AaPix.new(pix.pixbuf, pix.ax, pix.ay) if
1235
+ self.pix_in_curve_blob?(pix, blob_id)
1236
+ if prevs.include?(pix.a_yx)
1237
+ known_calc = false
1238
+ prevs[pix.a_yx].each { |prc|
1239
+ next unless prc[1].equal?(calc)
1240
+ p_prevs = prc[0]
1241
+ p_prevs.merge(pix.prevs)
1242
+ known_calc = true
1243
+ break
1244
+ }
1245
+ prevs[pix.a_yx] << [pix.prevs.dup, calc] unless
1246
+ known_calc
1247
+ else
1248
+ prevs[pix.a_yx] = [[pix.prevs.dup, calc]]
1249
+ end
1250
+ }}
1251
+ #$PDbgLog.puts_msg "Core: #{Xy.path_to_s(core.to_a.sort)}"
1252
+ neg_segs = {}
1253
+ core.each { |pix|
1254
+ ngb = ngb_p = nil
1255
+ p_end_pt = true
1256
+ pix.semi_adj8_incl_set_each(core) { |t_ngb, s_adj8_i|
1257
+ if ngb
1258
+ p_end_pt = false; break
1259
+ else
1260
+ ngb = t_ngb; ngb_p = s_adj8_i
1261
+ end
1262
+ }
1263
+ if p_end_pt
1264
+ warn("#{self.class.name}.loop_calcs_mid_ln_segs!: End " +
1265
+ "point#{pix.coords_to_s} resulting from many loops!") if
1266
+ prevs[pix.a_yx].length > 1
1267
+ p_prevs, p_calc = *(prevs[pix.a_yx][0])
1268
+ $PDbgLog.puts_msg "End point #{pix.coords_to_s}: from #{
1269
+ Xy.path_to_s(p_prevs)}"
1270
+ ngbs_arr = pix.semi_neighbs8_contig_arr(p_prevs)
1271
+ if !(p_segs=AaXy.semi_neighbs8_contig_arr_get_segs(
1272
+ ngbs_arr)).empty? && (p_segs != [[0, 16]] || ngb )
1273
+ if p_segs == [[0, 16]]
1274
+ # $PDbgLog.puts_msg "ngb#{ngb.coords_to_s} ngb_p=#{ngb_p}"
1275
+ p_segs = [[ngb_p-15, ngb_p]]
1276
+ end
1277
+ p_segs.each { |seg|
1278
+ p_ends = p_calc.pix_trace_seg(pix,p_prevs.to_a,ngbs_arr,
1279
+ seg.first, seg.last)
1280
+ s_mid, mid_pix = *p_ends[1]
1281
+ neg_segs[p_calc.start_lp] = [] unless neg_segs.include?(
1282
+ p_calc.start_lp)
1283
+ s_0 = p_ends[0]
1284
+ s_0 -= p_calc.start_lp.length if s_0 > s_mid
1285
+ s_end = p_ends[2]
1286
+ s_end += p_calc.start_lp.length if s_end < s_mid
1287
+ neg_segs[p_calc.start_lp] << [(s_0+s_mid)/2,
1288
+ (s_end+s_mid)/2]
1289
+ i = 0
1290
+ while i < mid_pix.length
1291
+ break unless self.pix_in_curve_blob?(
1292
+ mid_pix.at(i), blob_id)
1293
+ i += 1
1294
+ end
1295
+ mid_pix[i, mid_pix.length-i] = nil
1296
+ core_debris.concat(mid_pix)
1297
+ # $PDbgLog.puts_msg "mid_pix left: "+Xy.path_to_s(mid_pix)
1298
+ if pix_neighbs && !mid_pix.empty?
1299
+ self.pix_neighbs_add(pix_neighbs,pix,mid_pix[0],
1300
+ blob_id)
1301
+ self.pix_neighbs_add(pix_neighbs,mid_pix[0],pix,
1302
+ blob_id)
1303
+ if mid_pix.length > 1
1304
+ self.pix_neighbs_add(pix_neighbs,mid_pix[-2],
1305
+ mid_pix[-1], blob_id)
1306
+ self.pix_neighbs_add(pix_neighbs,mid_pix[-1],
1307
+ mid_pix[-2], blob_id)
1308
+ p0 = pix; p1 = mid_pix[0]
1309
+ for i in 1...mid_pix.length
1310
+ p2 = mid_pix[i]
1311
+ self.pix_neighbs_add(pix_neighbs,p1,p0,blob_id)
1312
+ self.pix_neighbs_add(pix_neighbs,p1,p2,blob_id)
1313
+ p0 = p1; p1 = p2
1314
+ end
1315
+ end
1316
+ end
1317
+ }
1318
+ end
1319
+ end
1320
+ }
1321
+ core_debris.each { |pix| core << pix }
1322
+ segs = []
1323
+ neg_segs.each_pair { |lp, n_segs|
1324
+ n_segs.sort! { |a,b| (a[0] % lp.length) <=>
1325
+ (b[0] % lp.length) }
1326
+ for seg_i in 0...n_segs.length
1327
+ i_0 = n_segs[seg_i-1][1]
1328
+ i_end = n_segs[seg_i][0]
1329
+ i_0 -= lp.length while i_0 > i_end
1330
+ #$PDbgLog.print_msg "seg[#{i_0}; #{i_end}] "
1331
+ segs << ImageObject.new(lp, i_0, i_end)
1332
+ end
1333
+ }
1334
+ #$PDbgLog.puts_msg "pix_neighbs: {#{pix_neighbs.keys.to_a.sort.
1335
+ # collect{|k| "(#{k[1]}, #{k[0]}): [" + PixbufPix.path_to_s(
1336
+ # pix_neighbs[k].to_a)}.join("]; ")}}" if pix_neighbs # "
1337
+ $PDbgLog.sig_return
1338
+ [core, segs]
1339
+ end
1340
+ end
1341
+
1342
+ class ContourLoop
1343
+ attr_accessor :blob_lp_calc
1344
+
1345
+ def next_layer
1346
+ return nil unless self.blob
1347
+ self.blob_lp_calc = BlobLoopCalc.new(self.blob, self.dup) unless
1348
+ self.blob_lp_calc
1349
+ self.blob_lp_calc.next_layer!
1350
+ ctr_lp = self.blob_lp_calc.lp.dup
1351
+ ctr_lp.blob = self.blob
1352
+ ctr_lp.blob_lp_calc = self.blob_lp_calc
1353
+ ctr_lp
1354
+ end
1355
+
1356
+ self.img_obj_add_meths(meth=PGuiObj::PMethod.new(:next_layer, [],
1357
+ 'next layer'))
1358
+ PGuiObj::MethObserverChildrenNoDeleteBtn.observe(meth)
1359
+ end
1360
+
1361
+ class ContourLoopCarpet
1362
+ self.img_obj_add_meths(meth=PGuiObj::PMethod.new(:next_layer, [],
1363
+ 'next layer'))
1364
+ PGuiObj::MethObserverChildrenNoDeleteBtn.observe(meth)
1365
+ end
1366
+
1367
+ class Curve < ImageObject
1368
+ attr_reader :curve_carpet, :segments, :set, :min_lp_grps,
1369
+ :min_lp_grp_terminals, :pix_neighbs_proc
1370
+
1371
+ def initialize(curve_carp, segments, pix_neighbs=nil)
1372
+ @curve_carpet = curve_carp
1373
+ @segments = segments
1374
+ if pix_neighbs
1375
+ @pix_neighbs_proc = proc { |pix| pix_neighbs[pix.a_yx] }
1376
+ else
1377
+ @pix_neighbs_proc = proc { |pix| ngbs = []
1378
+ #$PDbgLog.sig_call(self)
1379
+ #$PDbgLog.print_msg "ngbs:"+pix.coords_to_s + ": "
1380
+ pix.adj8_semi_adj8_each { |ngb|
1381
+ #$PDbgLog.print_msg "ngb#{ngb.coords_to_s} "
1382
+ if self.set.include?(ngb)
1383
+ ngbs << ngb; true; else false; end }
1384
+ #$PDbgLog.sig_return(PixbufPix.path_to_s(ngbs))
1385
+ ngbs
1386
+ }
1387
+ end
1388
+ @set = SortedArrSet.new(self) { |a,b| a.a_yx <=> b.a_yx }
1389
+ @min_lp_grps = []
1390
+ @min_lp_grp_terminals = []
1391
+ @pix_mapping2end_pt = {}
1392
+ @pix_mapping2body_pt = {}
1393
+ @pix_nodes = {}
1394
+ super()
1395
+ self.pguiobj_config.icon = :"pgtk-curve"
1396
+ end
1397
+
1398
+ def img_obj_segments
1399
+ (res = self.segments).each{|s|s.pguiobj_config.destroy_btn=false
1400
+ s.pguiobj_config.name = '<Segment>' }
1401
+ res = ImageObject.create_array(res)
1402
+ res.pguiobj_config.name = '[Segments]'
1403
+ res.pguiobj_config.destroy_btn = false
1404
+ res
1405
+ end
1406
+
1407
+ self.img_obj_add_meths(meth=PGuiObj::PMethod.new(:img_obj_segments, [],
1408
+ 'segments'))
1409
+ PGuiObj::MethObserverChildrenNoDeleteBtn.observe(meth)
1410
+ PGuiObj::MethObserverCachedAttr.observe(meth)
1411
+
1412
+ def pixbuf
1413
+ @curve_carpet.blob.pixbuf
1414
+ end
1415
+ end
1416
+
1417
+ class CurveCarpet < ImageObject
1418
+ attr_reader :blob, :curves, :multiple_curves, :pix_neighbs_from_curve
1419
+
1420
+ def initialize(blob, multiple_curves=false, pix_neighbs_from_curve=true)
1421
+ $PDbgLog.sig_call(self)
1422
+ @blob = blob
1423
+ @multiple_curves = multiple_curves
1424
+ @pix_neighbs_from_curve = pix_neighbs_from_curve
1425
+ @curves = []
1426
+ super()
1427
+ self.pguiobj_config.icon = :"pgtk-curve-carpet"
1428
+ self.rebuild
1429
+ $PDbgLog.sig_return
1430
+ end
1431
+
1432
+ def rebuild
1433
+ $PDbgLog.sig_call(self)
1434
+ loop_calcs = []
1435
+ pix_neighbs = @pix_neighbs_from_curve ? Hash.new([]) : nil
1436
+ @curves.clear
1437
+ if @multiple_curves
1438
+ @blob.contour_carpets.each { |ctr| ctr.loops.each { |lp|
1439
+ loop_calcs << BlobLoopCalc.new(@blob,lp[0,lp.length]) }}
1440
+ core, segs = *BlobLoopCalc.loop_calcs_mid_ln_segs!(loop_calcs,
1441
+ pix_neighbs)
1442
+ visited = Set.new
1443
+ tail = []
1444
+ while !core.empty?
1445
+ pix = nil; core.each { |pix| break }
1446
+ @curves << (curve=Curve.new(self,c_segs=[],pix_neighbs))
1447
+ curve << pix
1448
+ c_set = curve.img_obj_ayx_set
1449
+ tail << pix
1450
+ while !tail.empty?
1451
+ pix = tail.pop
1452
+ ngbs = pix.adj8.find_all{|px| core.include?(px)}
1453
+ for ngb in ngbs
1454
+ unless c_set.include?(ngb.a_yx)
1455
+ tail << ngb
1456
+ curve << ngb
1457
+ c_set << ngb.a_yx
1458
+ end
1459
+ end
1460
+ end
1461
+ curve.sort! { |a,b| a.a_leg(b) }
1462
+ curve.each { |pix| core.delete(pix) }
1463
+ i = 0
1464
+ while i < segs.length
1465
+ seg = segs.at(i)
1466
+ if c_set.include?(seg.first.a_yx)
1467
+ warn(self.class.name + ".rebuild: " +
1468
+ "Segment goes across curves!") unless
1469
+ c_set.include?(seg.last.a_yx)
1470
+ c_segs << seg
1471
+ segs.delete_at(i)
1472
+ else
1473
+ i += 1
1474
+ end
1475
+ end
1476
+ end
1477
+ warn(self.class.name + ".rebuild: Unclaimed segments (#{segs.
1478
+ length})!") unless segs.empty?
1479
+ else
1480
+ @blob.contour_carpets.each { |ctr| lp = ctr.loop_carpet
1481
+ loop_calcs << BlobLoopCalc.new(@blob, lp[0,lp.length]) }
1482
+ core, segs = *BlobLoopCalc.loop_calcs_mid_ln_segs!(loop_calcs,
1483
+ pix_neighbs)
1484
+ @curves << (curve = Curve.new(self, segs, pix_neighbs))
1485
+ core.each { |pix| curve << pix }
1486
+ curve.sort! { |a,b| a.a_leg(b) }
1487
+ end
1488
+ self.clear
1489
+ core.each { |pix| self << pix }
1490
+ $PDbgLog.sig_return("#{@curves.length} curve#{@curves.length ==
1491
+ 1 ? "" : "s" }")
1492
+ end
1493
+
1494
+ def img_obj_curves
1495
+ (res=self.curves).each{|c| c.pguiobj_config.destroy_btn=false }
1496
+ res = ImageObject.create_array(res)
1497
+ res.pguiobj_config.name = '[Curves]'
1498
+ res.pguiobj_config.destroy_btn = false
1499
+ res
1500
+ end
1501
+
1502
+ self.img_obj_add_meths(meth=PGuiObj::PMethod.new(:img_obj_curves, [],
1503
+ 'curves'))
1504
+ PGuiObj::MethObserverCachedAttr.observe(meth)
1505
+ end
1506
+
1507
+ class Curve
1508
+ def pix_maps_to_end_pt?(pix)
1509
+ @pix_mapping2end_pt[pix.a_yx]
1510
+ end
1511
+
1512
+ def pix_maps_to_end_pt(pix, val)
1513
+ @pix_mapping2end_pt[pix.a_yx] = val
1514
+ end
1515
+
1516
+ def pix_maps_to_body_pt?(pix)
1517
+ @pix_mapping2body_pt[pix.a_yx]
1518
+ end
1519
+
1520
+ def pix_maps_to_body_pt(pix, val)
1521
+ @pix_mapping2body_pt[pix.a_yx] = val
1522
+ end
1523
+
1524
+ def pix_end_pt?(pix, incl_set)
1525
+ pix.adj8.find_all { |px| incl_set.include?(px.yx) }.length < 2
1526
+ end
1527
+
1528
+ def pix_node(pix)
1529
+ tmp = @pix_nodes[pix.a_yx]
1530
+ return tmp if tmp
1531
+ pix_conns = pix.connections(&@pix_neighbs_proc)
1532
+ node = @pix_nodes[pix.a_yx] = AaXyNode.new(pix.ax, pix.ay,
1533
+ conns = [])
1534
+ pix_conns.each { |pixs|
1535
+ cn = PPix::NodeConn.new(self.pix_node(pixs.last),
1536
+ pixs.length)
1537
+ cn.path = XyPath.new([pix] + pixs)
1538
+ conns << cn
1539
+ }
1540
+ node
1541
+ end
1542
+ end
1543
+
1544
+ end