extract-curves 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) 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/cstr.c +82 -0
  11. data/ruby_ext/pav/cstr.h +17 -0
  12. data/ruby_ext/pav/extconf.rb +22 -0
  13. data/ruby_ext/pav/pav.c +162 -0
  14. data/ruby_ext/pav/pgtk.c +40 -0
  15. data/ruby_ext/pav/pgtk.h +14 -0
  16. data/ruby_ext/pav/pix.c +806 -0
  17. data/ruby_ext/pav/pix.h +236 -0
  18. data/ruby_ext/pav/t.rb +35 -0
  19. data/ruby_ext/pav/t1.rb +35 -0
  20. data/ruby_libs/pav/attr_cache.rb +211 -0
  21. data/ruby_libs/pav/attr_cache.t1.rb +32 -0
  22. data/ruby_libs/pav/cache.rb +31 -0
  23. data/ruby_libs/pav/collection/std.rb +58 -0
  24. data/ruby_libs/pav/dbg_log.rb +458 -0
  25. data/ruby_libs/pav/floatsio.rb +53 -0
  26. data/ruby_libs/pav/generator_cache.rb +165 -0
  27. data/ruby_libs/pav/graph/node.rb +602 -0
  28. data/ruby_libs/pav/graph/node_grp.rb +865 -0
  29. data/ruby_libs/pav/gtk.rb +6 -0
  30. data/ruby_libs/pav/gtk/button.rb +118 -0
  31. data/ruby_libs/pav/gtk/dialog.rb +29 -0
  32. data/ruby_libs/pav/gtk/guiobj.rb +772 -0
  33. data/ruby_libs/pav/gtk/icons.rb +124 -0
  34. data/ruby_libs/pav/gtk/rulers.rb +264 -0
  35. data/ruby_libs/pav/gtk/toolbar.rb +189 -0
  36. data/ruby_libs/pav/guiobj.rb +2 -0
  37. data/ruby_libs/pav/guiobj/info_asm.rb +41 -0
  38. data/ruby_libs/pav/guiobj/method.rb +211 -0
  39. data/ruby_libs/pav/guiobj/obj.rb +134 -0
  40. data/ruby_libs/pav/guiobj/signals.rb +9 -0
  41. data/ruby_libs/pav/heap.rb +54 -0
  42. data/ruby_libs/pav/icons/alt_handle.xcf +0 -0
  43. data/ruby_libs/pav/icons/alt_handle.xpm +3832 -0
  44. data/ruby_libs/pav/icons/alt_handle_hover.xcf +0 -0
  45. data/ruby_libs/pav/icons/alt_handle_hover.xpm +3368 -0
  46. data/ruby_libs/pav/icons/alt_handle_pressed.xcf +0 -0
  47. data/ruby_libs/pav/icons/alt_handle_pressed.xpm +3828 -0
  48. data/ruby_libs/pav/icons/blob.gif +0 -0
  49. data/ruby_libs/pav/icons/clover_base.xcf +0 -0
  50. data/ruby_libs/pav/icons/contour.gif +0 -0
  51. data/ruby_libs/pav/icons/contour.xcf +0 -0
  52. data/ruby_libs/pav/icons/contour_carpet.gif +0 -0
  53. data/ruby_libs/pav/icons/contour_carpet.xcf +0 -0
  54. data/ruby_libs/pav/icons/curve.gif +0 -0
  55. data/ruby_libs/pav/icons/curve.xcf +0 -0
  56. data/ruby_libs/pav/icons/curve_carpet.gif +0 -0
  57. data/ruby_libs/pav/icons/curve_carpet.xcf +0 -0
  58. data/ruby_libs/pav/icons/expand_base.xcf +0 -0
  59. data/ruby_libs/pav/icons/expand_closed.xpm +1791 -0
  60. data/ruby_libs/pav/icons/expand_closed_hover.xpm +1775 -0
  61. data/ruby_libs/pav/icons/expand_open.xpm +1788 -0
  62. data/ruby_libs/pav/icons/expand_open_hover.xpm +1752 -0
  63. data/ruby_libs/pav/icons/extract_curves/extract_curves-icon-rgb.ppm +14 -0
  64. data/ruby_libs/pav/icons/extract_curves/extract_curves-logo-rgb.gif +0 -0
  65. data/ruby_libs/pav/icons/extract_curves/extract_curves-logo-rgb.xcf +0 -0
  66. data/ruby_libs/pav/icons/extract_curves/trace_mark.xcf +0 -0
  67. data/ruby_libs/pav/icons/extract_curves/trace_mark.xpm +38 -0
  68. data/ruby_libs/pav/icons/handle.xcf +0 -0
  69. data/ruby_libs/pav/icons/handle.xpm +213 -0
  70. data/ruby_libs/pav/icons/loop.gif +0 -0
  71. data/ruby_libs/pav/icons/loop.xcf +0 -0
  72. data/ruby_libs/pav/icons/loop_carpet.gif +0 -0
  73. data/ruby_libs/pav/icons/loop_carpet.xcf +0 -0
  74. data/ruby_libs/pav/icons/next.xpm +29 -0
  75. data/ruby_libs/pav/icons/next_hover.xpm +315 -0
  76. data/ruby_libs/pav/icons/next_pressed.xpm +144 -0
  77. data/ruby_libs/pav/icons/prev.xpm +29 -0
  78. data/ruby_libs/pav/icons/prev_hover.xpm +315 -0
  79. data/ruby_libs/pav/icons/prev_pressed.xpm +144 -0
  80. data/ruby_libs/pav/icons/shaved-core.gif +0 -0
  81. data/ruby_libs/pav/icons/vnext.xpm +29 -0
  82. data/ruby_libs/pav/icons/vprev.xpm +29 -0
  83. data/ruby_libs/pav/numeric/ext.rb +21 -0
  84. data/ruby_libs/pav/patterns/hsep.gif +0 -0
  85. data/ruby_libs/pav/patterns/tnode.gif +0 -0
  86. data/ruby_libs/pav/patterns/tnode_w_link.gif +0 -0
  87. data/ruby_libs/pav/patterns/vlink.gif +0 -0
  88. data/ruby_libs/pav/patterns/vsep.gif +0 -0
  89. data/ruby_libs/pav/patterns/yg_hrope.xpm +492 -0
  90. data/ruby_libs/pav/patterns/yg_hrope_thick.xpm +1904 -0
  91. data/ruby_libs/pav/patterns/yg_hrope_thin.xpm +130 -0
  92. data/ruby_libs/pav/patterns/yg_tnode.xpm +180 -0
  93. data/ruby_libs/pav/patterns/yg_tnode_thick.xpm +615 -0
  94. data/ruby_libs/pav/patterns/yg_tnode_thin.xpm +55 -0
  95. data/ruby_libs/pav/patterns/yg_tnode_w_link.xpm +190 -0
  96. data/ruby_libs/pav/patterns/yg_tnode_w_link_thick.xpm +676 -0
  97. data/ruby_libs/pav/patterns/yg_tnode_w_link_thin.xpm +62 -0
  98. data/ruby_libs/pav/patterns/yg_vrope.xpm +563 -0
  99. data/ruby_libs/pav/patterns/yg_vrope_thick.xpm +2047 -0
  100. data/ruby_libs/pav/patterns/yg_vrope_thin.xpm +166 -0
  101. data/ruby_libs/pav/pav_find.rb +90 -0
  102. data/ruby_libs/pav/pix.rb +402 -0
  103. data/ruby_libs/pav/pix/aapix.rb +378 -0
  104. data/ruby_libs/pav/pix/blob.rb +678 -0
  105. data/ruby_libs/pav/pix/circle.rb +73 -0
  106. data/ruby_libs/pav/pix/contour.rb +676 -0
  107. data/ruby_libs/pav/pix/contour/calc_situations.rb +9 -0
  108. data/ruby_libs/pav/pix/contour/carp_calc.rb +212 -0
  109. data/ruby_libs/pav/pix/contour/situations.dmp +0 -0
  110. data/ruby_libs/pav/pix/contour/situations.rb +21 -0
  111. data/ruby_libs/pav/pix/curve.rb +1544 -0
  112. data/ruby_libs/pav/pix/img_obj.rb +865 -0
  113. data/ruby_libs/pav/pix/node.rb +159 -0
  114. data/ruby_libs/pav/pix/shaved_core.rb +697 -0
  115. data/ruby_libs/pav/pix/subpix.rb +212 -0
  116. data/ruby_libs/pav/rand_accessible.rb +16 -0
  117. data/ruby_libs/pav/rangeset.rb +63 -0
  118. data/ruby_libs/pav/search.rb +210 -0
  119. data/ruby_libs/pav/set.rb +130 -0
  120. data/ruby_libs/pav/string/bits.rb +523 -0
  121. data/ruby_libs/pav/string/ext.rb +58 -0
  122. data/ruby_libs/pav/string/observable.rb +155 -0
  123. data/ruby_libs/pav/string/text.rb +79 -0
  124. data/ruby_libs/pav/string/words.rb +42 -0
  125. data/ruby_libs/pav/sub_arr.rb +56 -0
  126. data/ruby_libs/pav/traced_obj.rb +79 -0
  127. data/web/index.html +280 -0
  128. data/web/media/icons/alt_handle.xcf +0 -0
  129. data/web/media/icons/alt_handle.xpm +3832 -0
  130. data/web/media/icons/alt_handle_hover.xcf +0 -0
  131. data/web/media/icons/alt_handle_hover.xpm +3368 -0
  132. data/web/media/icons/alt_handle_pressed.xcf +0 -0
  133. data/web/media/icons/alt_handle_pressed.xpm +3828 -0
  134. data/web/media/icons/blob.gif +0 -0
  135. data/web/media/icons/clover_base.xcf +0 -0
  136. data/web/media/icons/contour.gif +0 -0
  137. data/web/media/icons/contour.xcf +0 -0
  138. data/web/media/icons/contour_carpet.gif +0 -0
  139. data/web/media/icons/contour_carpet.xcf +0 -0
  140. data/web/media/icons/curve.gif +0 -0
  141. data/web/media/icons/curve.xcf +0 -0
  142. data/web/media/icons/curve_carpet.gif +0 -0
  143. data/web/media/icons/curve_carpet.xcf +0 -0
  144. data/web/media/icons/expand_base.xcf +0 -0
  145. data/web/media/icons/expand_closed.xpm +1791 -0
  146. data/web/media/icons/expand_closed_hover.xpm +1775 -0
  147. data/web/media/icons/expand_open.xpm +1788 -0
  148. data/web/media/icons/expand_open_hover.xpm +1752 -0
  149. data/web/media/icons/extract_curves/extract_curves-icon-rgb.ppm +14 -0
  150. data/web/media/icons/extract_curves/extract_curves-logo-rgb.gif +0 -0
  151. data/web/media/icons/extract_curves/extract_curves-logo-rgb.xcf +0 -0
  152. data/web/media/icons/extract_curves/trace_mark.xcf +0 -0
  153. data/web/media/icons/extract_curves/trace_mark.xpm +38 -0
  154. data/web/media/icons/handle.xcf +0 -0
  155. data/web/media/icons/handle.xpm +213 -0
  156. data/web/media/icons/loop.gif +0 -0
  157. data/web/media/icons/loop.xcf +0 -0
  158. data/web/media/icons/loop_carpet.gif +0 -0
  159. data/web/media/icons/loop_carpet.xcf +0 -0
  160. data/web/media/icons/next.xpm +29 -0
  161. data/web/media/icons/next_hover.xpm +315 -0
  162. data/web/media/icons/next_pressed.xpm +144 -0
  163. data/web/media/icons/prev.xpm +29 -0
  164. data/web/media/icons/prev_hover.xpm +315 -0
  165. data/web/media/icons/prev_pressed.xpm +144 -0
  166. data/web/media/icons/shaved-core.gif +0 -0
  167. data/web/media/icons/vnext.xpm +29 -0
  168. data/web/media/icons/vprev.xpm +29 -0
  169. data/web/media/title.jpeg +0 -0
  170. data/web/media/title.xcf +0 -0
  171. data/web/stylesheets/default.css +20 -0
  172. metadata +229 -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