| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Image::Bitmap2Paths;
|
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
#use 5.022002;
|
|
4
|
1
|
|
|
1
|
|
118496
|
use strict;
|
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
34
|
|
|
5
|
1
|
|
|
1
|
|
57738
|
use utf8;
|
|
|
1
|
|
|
|
|
244
|
|
|
|
1
|
|
|
|
|
6
|
|
|
6
|
1
|
|
|
1
|
|
25
|
use warnings;
|
|
|
1
|
|
|
|
|
6
|
|
|
|
1
|
|
|
|
|
36
|
|
|
7
|
1
|
|
|
1
|
|
10487
|
use Data::Flow qw(0.09);
|
|
|
1
|
|
|
|
|
2565
|
|
|
|
1
|
|
|
|
|
169
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
require Exporter;
|
|
10
|
1
|
|
|
1
|
|
5
|
use AutoLoader qw(AUTOLOAD);
|
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
6
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our @ISA = qw(Exporter);
|
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# Items to export into callers namespace by default. Note: do not export
|
|
15
|
|
|
|
|
|
|
# names by default without a very good reason. Use EXPORT_OK instead.
|
|
16
|
|
|
|
|
|
|
# Do not simply export all your public functions/methods/constants.
|
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# This allows declaration use Image::Bitmap2Paths ':all';
|
|
19
|
|
|
|
|
|
|
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
|
|
20
|
|
|
|
|
|
|
# will save memory.
|
|
21
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => [ qw(
|
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
) ] );
|
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
|
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
our @EXPORT = qw(
|
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
);
|
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
our $VERSION = '0.01001';
|
|
32
|
|
|
|
|
|
|
|
|
33
|
1
|
|
50
|
1
|
|
160
|
BEGIN { my $debug = $ENV{DEBUG_BITMAP2PATHS} || 0;
|
|
34
|
|
|
|
|
|
|
# $debug++ while @ARGV and $ARGV[0] eq '-d' and shift;
|
|
35
|
1
|
50
|
|
0
|
0
|
31
|
eval ( $debug ? 'sub dwarn { warn @_, ("@_" =~ /\n$/ ? q() : "\n") }' : 'sub dwarn {1}') ;
|
|
|
0
|
|
|
|
|
0
|
|
|
36
|
1
|
|
|
|
|
25071
|
eval "sub debug () { $debug }";
|
|
37
|
|
|
|
|
|
|
}
|
|
38
|
|
|
|
|
|
|
my $extend_tip = 1/3; # Crashes of fontforge; see issues #3239 #3240 #3242
|
|
39
|
|
|
|
|
|
|
my($marked, $marked2);
|
|
40
|
|
|
|
|
|
|
|
|
41
|
0
|
|
|
0
|
0
|
0
|
sub marks_clear() {$marked = $marked2 = undef}
|
|
42
|
0
|
|
|
0
|
0
|
0
|
sub marks() {($marked, $marked2)}
|
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# Preloaded methods go here.
|
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# Autoload methods go after =cut, and are processed by the autosplit program.
|
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# Follow the approach in Audio::FindChunks
|
|
49
|
|
|
|
|
|
|
my %defaults = (
|
|
50
|
|
|
|
|
|
|
coarse_blobs => 0,
|
|
51
|
|
|
|
|
|
|
);
|
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
my %mirror_from = ( # May be set separately, otherwise are synonims
|
|
54
|
|
|
|
|
|
|
# min_actual_silence_sec => 'min_silence_sec',
|
|
55
|
|
|
|
|
|
|
);
|
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
my @recognized = # these default to undef, but accessing them is not fatal
|
|
58
|
|
|
|
|
|
|
qw(width height);
|
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
my %subelements = (
|
|
61
|
|
|
|
|
|
|
LbRb => [qw(Lb Rb)],
|
|
62
|
|
|
|
|
|
|
stageOne => [qw(offs cnt cntmin near nearmin doublerays)],
|
|
63
|
|
|
|
|
|
|
stage10 => [qw(rays10 longedges10 seenlong10 inLong10 midLong10)],
|
|
64
|
|
|
|
|
|
|
stage20 => [qw(edge20 cntedge20 lastedge20 rays20 longedges20 seenlong20 midLong20 inLong20 Simple)],
|
|
65
|
|
|
|
|
|
|
stage30 => [qw(edge30 cntedge30 lastedge30 blobs30 blob30 skipExtraBlob)],
|
|
66
|
|
|
|
|
|
|
stage40 => [qw(edge40 cntedge40 lastedge40)],
|
|
67
|
|
|
|
|
|
|
stage50 => [qw(edge50 cntedge50 lastedge50 rays50 longedges50 seenlong50 midLong50 inLong50)],
|
|
68
|
|
|
|
|
|
|
stage60 => [qw(edge60 cntedge60 lastedge60)],
|
|
69
|
|
|
|
|
|
|
stage70 => [qw(edge70 cntedge70 lastedge70 longedges70 seenlong70 midLong70 inLong70)],
|
|
70
|
|
|
|
|
|
|
stage80 => [qw(edge80 cntedge80 lastedge80 tailEdge)],
|
|
71
|
|
|
|
|
|
|
stage90 => [qw(edge90 cntedge90 lastedge90)],
|
|
72
|
|
|
|
|
|
|
stageA0 => [qw(strokes nextEdgeBlob entryPointBlob inCalcEdge)],
|
|
73
|
|
|
|
|
|
|
);
|
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
my %filters = (
|
|
76
|
|
|
|
|
|
|
bitmap => [sub {my $i=shift; [[], (map {['', @$_ ,'']} @$i), []]}, 'minibitmap'],
|
|
77
|
|
|
|
|
|
|
width => [sub {my $i=shift; $#{$i->[1]}-1}, 'bitmap'],
|
|
78
|
|
|
|
|
|
|
height => [sub {my $i=shift; $#$i-1}, 'bitmap'],
|
|
79
|
|
|
|
|
|
|
LbRb => [\&LbRb,, 'bitmap', 'width', 'height'],
|
|
80
|
|
|
|
|
|
|
# Lb => [sub { my $LbRb = shift; $LbRb->[0] }, 'LbRb'], # (Extended) index of the last blank column at start
|
|
81
|
|
|
|
|
|
|
# Rb => [sub { my $LbRb = shift; $LbRb->[1] }, 'LbRb'], # (Extended) index of the first blank column at end
|
|
82
|
|
|
|
|
|
|
stageOne => [\&stageOne, qw(bitmap width height)],
|
|
83
|
|
|
|
|
|
|
stage10 => [\&doRays, qw(bitmap width height offs cnt cntmin near nearmin)],
|
|
84
|
|
|
|
|
|
|
stage20 => [\&do_Simple_and_edges, qw(width height rays10 offs cnt longedges10 seenlong10 inLong10 midLong10)],
|
|
85
|
|
|
|
|
|
|
stage30 => [\&nnn_do_Simple_and_edges, qw(width height offs bitmap edge20 cntedge20 lastedge20)],
|
|
86
|
|
|
|
|
|
|
stage40 => [\&nnn0_do_Simple_and_edges, qw(width height edge30 cntedge30 lastedge30 rays20 inLong10 blob30)],
|
|
87
|
|
|
|
|
|
|
stage50 => [\&nnn1_do_Simple_and_edges,
|
|
88
|
|
|
|
|
|
|
qw(width height edge40 cntedge40 lastedge40 rays20 inLong10 midLong10 seenlong10 longedges10 blob30 offs cnt)],
|
|
89
|
|
|
|
|
|
|
stage60 => [\&scan_degree_rays,
|
|
90
|
|
|
|
|
|
|
qw(width height edge50 cntedge50 lastedge50 rays50 midLong50 offs cnt)],
|
|
91
|
|
|
|
|
|
|
stage70 => [\&nnn3_do_Simple_and_edges,
|
|
92
|
|
|
|
|
|
|
qw(width height edge60 cntedge60 lastedge60 longedges50 seenlong50 midLong50 inLong50 cnt)],
|
|
93
|
|
|
|
|
|
|
stage80 => [\&nnn4_do_Simple_and_edges,
|
|
94
|
|
|
|
|
|
|
qw(width height edge70 cntedge70 lastedge70 rays50 offs cnt)],
|
|
95
|
|
|
|
|
|
|
stage90 => [\&nnn5_do_Simple_and_edges,
|
|
96
|
|
|
|
|
|
|
qw(width height edge80 cntedge80 lastedge80 rays50 offs inLong70 cnt near)],
|
|
97
|
|
|
|
|
|
|
stageA0 => [\&nnn6_do_Simple_and_edges,
|
|
98
|
|
|
|
|
|
|
qw(width height edge90 cntedge90 lastedge90 rays50 offs longedges70 blob30 bitmap skipExtraBlob tailEdge coarse_blobs)],
|
|
99
|
|
|
|
|
|
|
);
|
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
my %recipes = (
|
|
102
|
|
|
|
|
|
|
map(($_ => {default => $defaults{$_}}), keys %defaults),
|
|
103
|
|
|
|
|
|
|
map(($_ => {filter => [sub {shift}, $mirror_from{$_}]}), keys %mirror_from),
|
|
104
|
|
|
|
|
|
|
map( ($_ => {default => undef}),
|
|
105
|
|
|
|
|
|
|
@recognized),
|
|
106
|
|
|
|
|
|
|
map(($_ => {filter => $filters{$_}}), keys %filters),
|
|
107
|
|
|
|
|
|
|
(map {my $coll = $_; my $e = $subelements{$coll}; # For each subelement, create an entry
|
|
108
|
|
|
|
|
|
|
map {$e->[$_] => do {my $i=$_; {filter => [sub {shift()->[$i]}, $coll]}}} 0..$#$e} keys %subelements),
|
|
109
|
|
|
|
|
|
|
# map(($_ => {prerequisites => ['rms_data']}), 'chunks', 'min', 'max'),
|
|
110
|
|
|
|
|
|
|
);
|
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
# As in Audio::FindChunks
|
|
113
|
|
|
|
|
|
|
sub new {
|
|
114
|
2
|
|
|
2
|
0
|
213623
|
my $class = shift;
|
|
115
|
2
|
|
|
|
|
18
|
my $s = new Data::Flow \%recipes;
|
|
116
|
2
|
|
|
|
|
46
|
$s->set(@_);
|
|
117
|
2
|
|
|
|
|
56
|
bless \$s, $class;
|
|
118
|
|
|
|
|
|
|
}
|
|
119
|
0
|
|
|
0
|
0
|
0
|
sub set ($$$) { ${$_[0]}->set($_[1],$_[2]); $_[0] }
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
120
|
45
|
|
|
45
|
0
|
28164
|
sub get ($$) { ${$_[0]}->get($_[1]) }
|
|
|
45
|
|
|
|
|
183
|
|
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
my $height = 16; # Should be a multiple of 4
|
|
123
|
|
|
|
|
|
|
my @dx = (0,1,1,1,0,-1,-1,-1); # Start from "up", go clockwise
|
|
124
|
|
|
|
|
|
|
my @dy = (-1,-1,0,1,1,1,0,-1); # +-direction is "down"
|
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub LbRb ($$$) {
|
|
127
|
2
|
|
|
2
|
0
|
174
|
my($bm,$width,$height) = (shift, shift, shift);
|
|
128
|
2
|
|
|
|
|
6
|
my($Lb, $Rb) = ($width, 1);
|
|
129
|
2
|
|
|
|
|
29
|
for my $i (1..$height) {
|
|
130
|
10
|
|
|
|
|
20
|
my $P = $bm->[$i];
|
|
131
|
10
|
|
100
|
|
|
68
|
$P->[$_] and $Lb = $_-1, last for 1..$Lb;
|
|
132
|
10
|
|
100
|
|
|
68
|
$P->[$_] and $Rb = $_+1, last for reverse($Rb..$width);
|
|
133
|
|
|
|
|
|
|
} # Rb and Lb are one off from the rightmost and leftmost pixels
|
|
134
|
2
|
|
|
|
|
10
|
[$Lb, $Rb]
|
|
135
|
|
|
|
|
|
|
}
|
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
sub stageOne ($$$) {
|
|
138
|
2
|
|
|
2
|
1
|
774
|
my($bm,$width,$height) = (shift, shift, shift);
|
|
139
|
2
|
|
|
|
|
5
|
my(@near, @cnt, @offs, @doublerays, @cntmin, @nearmin);
|
|
140
|
2
|
|
|
|
|
9
|
for my $y (1..$height) { # Enumerate neighbors of the pixel, doublearys, directions having neighbors on both sides
|
|
141
|
10
|
|
|
|
|
20
|
for my $x ( 1..$width ) {
|
|
142
|
82
|
100
|
|
|
|
221
|
next unless $bm->[$y][$x];
|
|
143
|
6
|
|
|
|
|
12
|
my($prev, @OFF) = 0;
|
|
144
|
6
|
|
|
|
|
30
|
for my $n (0..7) {
|
|
145
|
48
|
|
|
|
|
80
|
my $dx = $dx[$n];
|
|
146
|
48
|
|
|
|
|
80
|
my $dy = $dy[$n];
|
|
147
|
|
|
|
|
|
|
# warn 'dx' unless defined $dx;
|
|
148
|
|
|
|
|
|
|
# warn 'dy' unless defined $dy;
|
|
149
|
48
|
100
|
|
|
|
115
|
next unless $bm->[$y + $dy][$x+$dx];
|
|
150
|
8
|
|
|
|
|
21
|
$near[$y][$x][$n] = 1;
|
|
151
|
8
|
|
|
|
|
17
|
push @OFF, $n;
|
|
152
|
8
|
100
|
|
|
|
29
|
next unless $bm->[$y + 2*$dy][$x + 2*$dx];
|
|
153
|
4
|
|
|
|
|
11
|
$doublerays[$y][$x]++;
|
|
154
|
|
|
|
|
|
|
}
|
|
155
|
6
|
50
|
|
|
|
23
|
$cntmin[$y][$x] = $cnt[$y][$x] = @OFF if @OFF;
|
|
156
|
6
|
50
|
|
|
|
19
|
$nearmin[$y][$x] = [ @{ $near[$y][$x] } ] if $near[$y][$x]; # deep copy
|
|
|
6
|
|
|
|
|
33
|
|
|
157
|
6
|
|
|
|
|
20
|
$offs[$y][$x] = \@OFF;
|
|
158
|
|
|
|
|
|
|
}
|
|
159
|
|
|
|
|
|
|
}
|
|
160
|
2
|
|
|
|
|
15
|
[\@offs, \@cnt, \@cntmin, \@near, \@nearmin, \@doublerays];
|
|
161
|
|
|
|
|
|
|
}
|
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
############################################## Stage 10 (two!)
|
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# Note that if a fake-curve is continued on the other side, we may prefer this to joining it to the dependent star
|
|
166
|
|
|
|
|
|
|
# In presence of dependencies below, the type of ray below is conditional on the eventual type of the dependency vertex.
|
|
167
|
|
|
|
|
|
|
# So the “name” below is preliminary, and may be changed later to a “derived type”.
|
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# Dictionary of ray candidates: Dense (>=7 neighbors at dist 1 or 2) (dot denotes an empty place; d is a dependency: rays must be good)
|
|
170
|
|
|
|
|
|
|
# . . . / |. d . . \ .... ..
|
|
171
|
|
|
|
|
|
|
# doubleray: *-- curve: *-. Fork: *d. ./- fake-curve: *-. d/- rhombus: *d. tail: --*- *- ish; serif --* notch:.-* ..*
|
|
172
|
|
|
|
|
|
|
# .|/ .\ | . \ *. \ \ *. d * d .|\ .|. / .|/ | ./. ./|.
|
|
173
|
|
|
|
|
|
|
# fork4: *d. Near-corner: *. m-joint: || elses-ray: *| / 3fork3: *d. *d. Sharp: *--- /. ..|
|
|
174
|
|
|
|
|
|
|
# .|\ .d-- *d d d .|\ . ..\ \.. \- \
|
|
175
|
|
|
|
|
|
|
# Note that dependent is not a neighbor for diagonal elses-ray (and is not unique) - .d- ...\ \
|
|
176
|
|
|
|
|
|
|
# fork4 and one flavor of fork3 are particular cases of fork! Corner-curve: *.\ 3fork3: *| bend-sharp: --*
|
|
177
|
|
|
|
|
|
|
# Later may put: ignore, Ignore, Tail, 2fork3, Enforced, Arrow/(x-)arrow, Probable-curve: *| Joint???: d*-.
|
|
178
|
|
|
|
|
|
|
# 1Spur, MFork; Rhombus-frce, Zh/K-fake-curve is intended to be ½-of-segment .|-
|
|
179
|
|
|
|
|
|
|
# Btail, 4fork, xFork, °. (Also allow longer shaft on Sharp) |.
|
|
180
|
|
|
|
|
|
|
# Opposite-direction pair tail/doubleray is converted to Tail/MFork if tail has cnt==3 (as on top of “M”).
|
|
181
|
|
|
|
|
|
|
# Likewise for a symmetrized case (as at bottom of “V”): if it is C/C with C and the opposite is
|
|
182
|
|
|
|
|
|
|
# unrecognized/C<1Spur>/C (instead of C).
|
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub inspect_ray ($$$$$$$$$) { # returns: type, curvature or undef (0 for tail), is multiplicity checked, dependents, remove, unignore, actions.
|
|
185
|
6
|
|
|
6
|
0
|
18
|
my($x, $y, $cnt, $cntmin, $px, $pxmin, $near, $nearmin, $dirs, @res) = (shift, shift, shift, shift, shift, shift, shift, shift, shift); # dependent of m-joint should be checked separately
|
|
186
|
6
|
|
|
|
|
12
|
for my $dir (@$dirs) { # dependency: $x, $y, $dir,$dir1,...
|
|
187
|
8
|
|
|
|
|
17
|
my $dx = $dx[$dir];
|
|
188
|
8
|
|
|
|
|
16
|
my $dy = $dy[$dir];
|
|
189
|
8
|
|
|
|
|
24
|
my($N, $Nmin) = ($near->[$y+$dy][$x+$dx], $nearmin->[$y+$dy][$x+$dx]);
|
|
190
|
8
|
|
|
|
|
23
|
my($cNmin, $cN, $cN0min) = ($cntmin->[$y+$dy][$x+$dx], $cnt->[$y+$dy][$x+$dx], $cntmin->[$y][$x]);
|
|
191
|
8
|
50
|
0
|
|
|
24
|
push @res, ['Dense'] and next if 6 < $cNmin;
|
|
192
|
8
|
|
|
|
|
19
|
my($N0, $N0min) = ($near->[$y][$x], $nearmin->[$y][$x]);
|
|
193
|
8
|
100
|
|
|
|
20
|
if ($cNmin <= 1) { # below, if @perp, we automatically are diagonal
|
|
194
|
4
|
50
|
50
|
|
|
31
|
push @res, ['tail', 0, 1, undef, undef, undef, ['t']] and next
|
|
|
|
|
33
|
|
|
|
|
|
195
|
|
|
|
|
|
|
if $N0->[($dir+4)%8] or $cN0min <= 2;
|
|
196
|
0
|
0
|
|
|
|
0
|
unless (grep $N0min->[($dir+$_)%8], -1,1) {
|
|
197
|
0
|
|
|
|
|
0
|
my @perp = grep $N0min->[($dir+$_)%8], -2,2;
|
|
198
|
0
|
0
|
0
|
|
|
0
|
push @res, ['1Spur', 0, 1] and next if @perp <= 1; # with @perp==1, allow 2 at 135°: a continuation of perp, and of us
|
|
199
|
|
|
|
|
|
|
}
|
|
200
|
|
|
|
|
|
|
}
|
|
201
|
4
|
50
|
|
|
|
11
|
if ($N->[$dir]) {
|
|
202
|
4
|
|
50
|
|
|
26
|
my $cNN = $cntmin->[$y+2*$dy][$x+2*$dx] || 0;
|
|
203
|
4
|
0
|
0
|
|
|
12
|
push @res, ['Dense'] and next if 6 < $cNN and $Nmin->[$dir]; # Nmin: be most forgiving
|
|
|
|
|
33
|
|
|
|
|
|
204
|
4
|
50
|
|
|
|
21
|
push @res, ['doubleray', 0] and next
|
|
205
|
|
|
|
|
|
|
} # Now know no straight continuation; check 2 next neighbors
|
|
206
|
0
|
|
|
|
|
0
|
my($seen_next2, %is_next, $d, $across2);
|
|
207
|
0
|
|
0
|
|
|
0
|
$Nmin->[($dir+$_)%8] and $seen_next2++, $d = $_, $is_next{$_}++ for -1, 1;
|
|
208
|
0
|
|
|
|
|
0
|
my($step, @NEAR) = 2 - ($dir & 0x1); # detect notches in diag directions, and serifs in HV directions; also for forks
|
|
209
|
|
|
|
|
|
|
# by seen_next2: 2: fork[3,4] 1: curve fake-curve diamond sharp fork3 0: tail(ish) notch serif near-corner m-joint elses-ray Corner-curve
|
|
210
|
0
|
0
|
0
|
|
|
0
|
unless ($seen_next2) { # No suitable curved continuation
|
|
211
|
0
|
|
|
|
|
0
|
for my $D (-1, 1) { # Protrusion? ($dir is serif/notch/ maybe-m-joint???)
|
|
212
|
0
|
|
|
|
|
0
|
my $DD = ($dir+$step*$D)%8; # close H-or-V direction
|
|
213
|
0
|
|
|
|
|
0
|
my $dx1 = $dx[$DD];
|
|
214
|
0
|
|
|
|
|
0
|
my $dy1 = $dy[$DD];
|
|
215
|
0
|
0
|
|
|
|
0
|
$across2++ if $nearmin->[$y+$dy1][$x+$dx1][$DD]; # can go 2 steps in the close H-or-V direction
|
|
216
|
0
|
0
|
0
|
|
|
0
|
next if not $N0min->[$DD] or $N0min->[($DD+4)%8]; # Skip if extends on the other side
|
|
217
|
0
|
|
0
|
|
|
0
|
my $extra = $D * ($cNmin <= 3 and $cN >= 3 and not $dir & 0x1 and !!$N->[$DD]); # Bottom join of M - m-joint (above: with dir=2, DD=0)
|
|
218
|
0
|
|
|
|
|
0
|
my $curved = $nearmin->[$y+$dy1][$x+$dx1][($DD + $extra)%8]; # for m-joint: extra=1, and we have sloped perp continuation
|
|
219
|
|
|
|
|
|
|
# ???? next if $other_dir and ($dir & 0x1 or );
|
|
220
|
|
|
|
|
|
|
# x[2]: straight perp continuation for m-joints
|
|
221
|
0
|
|
|
|
|
0
|
push @NEAR, [$D, $extra, $curved, $nearmin->[$y+$dy1][$x+$dx1][$DD], $cntmin->[$y+$dy1][$x+$dx1] < 5]; # if not ($dir & 0x1 and '???'); # found long stem nearby
|
|
222
|
|
|
|
|
|
|
} # At most one element in @NEAR...
|
|
223
|
0
|
|
|
|
|
0
|
my $n0;
|
|
224
|
0
|
0
|
|
|
|
0
|
if (@NEAR) {
|
|
225
|
0
|
|
|
|
|
0
|
$n0 = $NEAR[0][0]; # Avoid autovivication, do only if @NEAR...
|
|
226
|
0
|
0
|
0
|
|
|
0
|
push @res, ['notch', $n0, 1, undef, [$x+$dx, $y+$dy], [$x+$dx, $y+$dy, ($dir+4)%8], # Don't cancel each other: double-notches 02d0
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
227
|
|
|
|
|
|
|
['I', $x+$dx, $y+$dy, ($dir+2*$n0)%8, ($dir+3*$n0)%8], ['n', $x+$dx, $y+$dy, ($dir+4)%8]] and next # Force ignoring other neighbors
|
|
228
|
|
|
|
|
|
|
if $NEAR[0][2] and $cNmin <= 3 and $cN >= 3 and $dir & 0x1
|
|
229
|
|
|
|
|
|
|
and not $N0min->[($dir + 2*$n0)%8] and not grep $N0min->[($dir + $_*$n0+4)%8], 1, 2;
|
|
230
|
0
|
0
|
0
|
|
|
0
|
push @res, ['serif', $n0, 1, undef, [$x+$dx, $y+$dy], undef, ['I', $x+$dx, $y+$dy, ($dir+3*$n0)%8], ['E', $x+$dx, $y+$dy, ($dir+4)%8]]
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
231
|
|
|
|
|
|
|
and next if $NEAR[0][2] and $cNmin <= 2 and not ($dir & 0x1);
|
|
232
|
|
|
|
|
|
|
# warn("m: $x $y $dir\n"),
|
|
233
|
0
|
0
|
0
|
|
|
0
|
push @res, ['m-joint', $n0, 1, ['`', [$x + $dx, $y + $dy, ($dir+4)%8]],
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
234
|
|
|
|
|
|
|
undef, undef, ['L', $x, $y, ($dir+2*$n0)%8, -$n0, 1, 1, 2]] and next # check separately???
|
|
235
|
|
|
|
|
|
|
if not ($dir & 0x1) and $NEAR[0][1] and ($NEAR[0][2] xor $NEAR[0][3]) and (not $NEAR[0][3] or $NEAR[0][4]) and $cN0min <= 3;
|
|
236
|
|
|
|
|
|
|
}
|
|
237
|
0
|
|
|
|
|
0
|
@NEAR = grep $_->[2], @NEAR; # only curved!
|
|
238
|
0
|
|
|
|
|
0
|
my($nnn, $DD, @NEAR1, $nnn1); # What remains is Near-corner, elses-ray, Corner-curve, bend-sharp, and diagonal notch
|
|
239
|
0
|
|
|
|
|
0
|
for my $D (-1, 1) { # Allow a neighbor come from a ray of the protrusion
|
|
240
|
0
|
0
|
|
|
|
0
|
next unless $Nmin->[($dir+2*$D)%8];
|
|
241
|
0
|
|
|
|
|
0
|
$nnn++, $DD = $D;
|
|
242
|
|
|
|
|
|
|
} # Here dy goes down!!! vvv
|
|
243
|
0
|
0
|
0
|
|
|
0
|
if ($nnn) { # Have a neighbor near end in perpendicular direction
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
244
|
0
|
0
|
0
|
|
|
0
|
push @res, ['elses-ray', undef, 0, ['″', [$x+$dx+$dy,$y+$dy-$dx,($dir+2)%8], [$x+$dx-$dy,$y+$dy+$dx,($dir-2)%8]]] and next
|
|
245
|
|
|
|
|
|
|
if $nnn == 2; # ??? Do we NEED to check the opposite dir:
|
|
246
|
0
|
0
|
0
|
|
|
0
|
if (($N0->[($dir+4)%8] or not($dir & 0x1) and $cN0min == 3) # and $N0min->[($dir+4-$DD)%8]) # do not allow bending away from the corner
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
247
|
|
|
|
|
|
|
and not $N0min->[($dir+2*$DD)%8]
|
|
248
|
|
|
|
|
|
|
and ($px->[$y + $dy + 2*$DD*$dx][$x + $dx - 2*$DD*$dy] # do not allow bending away from us
|
|
249
|
|
|
|
|
|
|
or not($dir & 0x1) and $cntmin->[$y + $dy + $DD*$dx][$x + $dx - $DD*$dy] <= 3)) {
|
|
250
|
|
|
|
|
|
|
# warn "thisC=$cN0min targC=$cNmin notchC=$cntmin->[$y + $dy + $DD*$dx][$x + $dx - $DD*$dy], and ", grep +($N0min->[($dir+4+$_)%8] ? 0 : 1), 1, -1;
|
|
251
|
0
|
0
|
0
|
|
|
0
|
if( $cN0min == 3 and $cNmin == 2 and not($dir % 2) and $cntmin->[$y + $dy + $DD*$dx][$x + $dx - $DD*$dy] == 2
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
252
|
|
|
|
|
|
|
and my @back = grep $N0min->[($dir+4+$_)%8], 1, -1 ) {
|
|
253
|
|
|
|
|
|
|
# $marked++;
|
|
254
|
0
|
0
|
|
|
|
0
|
push @res, ['Btail', $DD, 0, undef, undef, undef,
|
|
|
|
0
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
['N', $x+$dx, $y+$dy, ($dir+2*$DD)%8], ['N', $x+$dx-$DD*$dy, $y+$dy+$DD*$dx, ($dir-2*$DD)%8],
|
|
256
|
|
|
|
|
|
|
['I', $x+$dx-$DD*$dy, $y+$dy+$DD*$dx, ($dir+4+$DD)%8],
|
|
257
|
|
|
|
|
|
|
(@back ? ['E', $x, $y, ($dir+4+$back[-1])%8] : ())] and next
|
|
258
|
|
|
|
|
|
|
}
|
|
259
|
0
|
0
|
|
|
|
0
|
push @res, ['Near-corner', $DD, 0, [',', [$x + $dx, $y + $dy, ($dir+2*$DD)%8]], undef, undef,
|
|
|
|
0
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
($dir & 0x1 ? () # TRY: remove the litation on not bending away
|
|
261
|
|
|
|
|
|
|
: (['I', $x, $y, ($dir+$DD)%8], ['Ef', $x+$dx, $y+$dy, ($dir+4)%8]) )] and next
|
|
262
|
|
|
|
|
|
|
# and $px->[$y + 2*$DD*$dx][$x - 2*$DD*$dy]);
|
|
263
|
|
|
|
|
|
|
}
|
|
264
|
0
|
0
|
0
|
|
|
0
|
push @res, ['Corner-curve', $DD, 1] and next if $dir & 0x1 and not $N0min->[($dir+$DD)%8] and $cNmin <= 2;
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
265
|
0
|
0
|
0
|
|
|
0
|
push @res, ['arrow', $DD, 0, ['…', [$x+$dx, $y+$dy, ($dir + 4)%8, ($dir + 4 - $DD)%8, ($dir + 4 - 2*$DD)%8]], # On barb going to tip
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
266
|
|
|
|
|
|
|
undef, undef, ['a', $DD]] and next # remove, unignore, @rest
|
|
267
|
|
|
|
|
|
|
if $cNmin == 3 and $dir & 0x1 and $nnn == 1 and $N0min->[($dir+4)%8]
|
|
268
|
|
|
|
|
|
|
and $Nmin->[($dir + 4 - $DD)%8] and $Nmin->[($dir + 4 - 2*$DD)%8];
|
|
269
|
|
|
|
|
|
|
} elsif (not @NEAR and $cNmin == 2 and $N0min->[($dir+4)%8]
|
|
270
|
|
|
|
|
|
|
and $cN0min <= 3 + ($dir & 0x1) and $cnt->[$y][$x] >= 3 + ($dir & 0x1)) { # bend-sharp?
|
|
271
|
0
|
|
|
|
|
0
|
my $DDD;
|
|
272
|
0
|
|
|
|
|
0
|
for my $D (-1,1) {
|
|
273
|
0
|
0
|
|
|
|
0
|
$DDD=$D, last if $N0min->[($dir+$D*$step)%8];
|
|
274
|
|
|
|
|
|
|
}
|
|
275
|
0
|
0
|
|
|
|
0
|
die "bend-sharp: panic" unless $DDD;
|
|
276
|
0
|
|
|
|
|
0
|
my $dx1 = $dx[($dir+$DDD*$step)%8];
|
|
277
|
0
|
|
|
|
|
0
|
my $dy1 = $dy[($dir+$DDD*$step)%8];
|
|
278
|
0
|
0
|
0
|
|
|
0
|
push @res, ['bend-sharp', $DDD, 1] and next if $cntmin->[$y+$dy1][$x+$dx1] <= 4 - ($dir & 0x1)
|
|
|
|
|
0
|
|
|
|
|
|
279
|
|
|
|
|
|
|
and $nearmin->[$y+$dy1][$x+$dx1][($dir+4-$DDD)%8];
|
|
280
|
|
|
|
|
|
|
}
|
|
281
|
0
|
0
|
0
|
|
|
0
|
push @res, ['?'] and next if $dir & 0x1;
|
|
282
|
0
|
|
|
|
|
0
|
for my $D (-1, 1) { # Allow a neighbor come from a ray of the protrusion
|
|
283
|
0
|
0
|
|
|
|
0
|
next unless $Nmin->[($dir+$D)%8];
|
|
284
|
0
|
|
|
|
|
0
|
$nnn1++, $DD = $D;
|
|
285
|
|
|
|
|
|
|
} # Here dy goes down!!! vvv
|
|
286
|
0
|
0
|
0
|
|
|
0
|
push @res, ['notch', $n0, 1, undef, [$x+$dx, $y+$dy]] and next
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
287
|
|
|
|
|
|
|
if $cNmin <= 2 and not($dir & 0x1) and $nnn1 and not $N0min->[($dir + $DD + 4)%8]; # Miss double-notch=arrow
|
|
288
|
0
|
0
|
0
|
|
|
0
|
push @res, ['Arrow'] and next # On shaft going to tip
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
289
|
|
|
|
|
|
|
if $cNmin == 3 and not($dir & 0x1 or $nnn1 or $across2 or $N->[($dir+2)%8] or $N->[($dir-2)%8]);
|
|
290
|
0
|
0
|
|
|
|
0
|
push @res, ['?'] and next;
|
|
291
|
|
|
|
|
|
|
} elsif (2 == $seen_next2) { # Only forks, Zh, K's here... # |. . /
|
|
292
|
|
|
|
|
|
|
my($c, $DDD) = 0; # ./- $c counts dots *-
|
|
293
|
|
|
|
|
|
|
$N0min->[($dir+$step*$_)%8] and $c++, $DDD=$_ for -1, 1; # *. . \
|
|
294
|
|
|
|
|
|
|
if ($c == 1 and not($dir & 0x1)) { # Work around ties between legs of K
|
|
295
|
|
|
|
|
|
|
my $x1 = $x + $dx[($dir+$DDD*$step)%8];
|
|
296
|
|
|
|
|
|
|
my $y1 = $y + $dy[($dir+$DDD*$step)%8];
|
|
297
|
|
|
|
|
|
|
my($NNN, $dir1) = ($near->[$y1][$x1], ($dir-$DDD)%8);
|
|
298
|
|
|
|
|
|
|
if ($NNN->[$dir] and $NNN->[$dir1]) {
|
|
299
|
|
|
|
|
|
|
push @res, ['elses-ray', -$DDD, 0, ['"', [$x1, $y1, $dir, $dir1]]];
|
|
300
|
|
|
|
|
|
|
next;
|
|
301
|
|
|
|
|
|
|
}
|
|
302
|
|
|
|
|
|
|
} elsif ($c == 1) {
|
|
303
|
|
|
|
|
|
|
push @res, ['Probable-curve', $DDD] and next;
|
|
304
|
|
|
|
|
|
|
} elsif ($c == 2 and $dir & 0x1 and $N0min->[($dir+4)%8] and not $N0min->[($dir+3)%8] and not $N0min->[($dir+5)%8]) {# K-joint of Ж; repeat what we do below with K-joint
|
|
305
|
|
|
|
|
|
|
# warn "K-joint: ($x,$y) $dir + $d\n";
|
|
306
|
|
|
|
|
|
|
my @R;
|
|
307
|
|
|
|
|
|
|
for my $d (-1, 1) {
|
|
308
|
|
|
|
|
|
|
my $x1 = $x + $dx[($dir+$d)%8];
|
|
309
|
|
|
|
|
|
|
my $y1 = $y + $dy[($dir+$d)%8];
|
|
310
|
|
|
|
|
|
|
my($NNN, $dir1) = ($near->[$y1][$x1], ($dir+2*$d)%8);
|
|
311
|
|
|
|
|
|
|
push @R, ['Zh-fake-curve', $d] if $NNN->[$dir] and not $NNN->[($dir1+1)%8] and not $NNN->[($dir1-1)%8];
|
|
312
|
|
|
|
|
|
|
}
|
|
313
|
|
|
|
|
|
|
push(@res, @R), next if @R == 1;
|
|
314
|
|
|
|
|
|
|
}
|
|
315
|
|
|
|
|
|
|
push @res, ['?'] and next if $c;
|
|
316
|
|
|
|
|
|
|
# $N->[($dir+2*$_)%8] and $c++ for -1, 1;
|
|
317
|
|
|
|
|
|
|
# push @res, ['fork4'] and next if $c and $c == 2;
|
|
318
|
|
|
|
|
|
|
my $opp = ($dir + 4)%8;
|
|
319
|
|
|
|
|
|
|
my @dep = grep { $_ ne $opp and $Nmin->[$_]} 0..7;
|
|
320
|
|
|
|
|
|
|
push @res, ['Fork', undef, 1, ['°', [$x+$dx, $y+$dy, @dep]]] and next; # join all forks with next2==2
|
|
321
|
|
|
|
|
|
|
} # Now have one secondary ray only, $d-curving: curve fake-curve diamond sharp fork3
|
|
322
|
0
|
|
|
|
|
0
|
my $baddir = ($dir - $step*$d)%8;
|
|
323
|
0
|
|
|
|
|
0
|
my $bad = $N0min->[$baddir]; # on fake-curve only
|
|
324
|
0
|
0
|
|
|
|
0
|
if ( $N0min->[($dir+$d)%8] ) { # Parallelogram - essentially, two curves with the same end (diamond sharp fork3)
|
|
325
|
0
|
0
|
0
|
|
|
0
|
if ($bad and $dir & 0x1 and $N0min->[($dir+4)%8] and not $N0min->[($dir+3)%8] and not $N0min->[($dir+5)%8]) { # may be a K-joint
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
326
|
0
|
|
|
|
|
0
|
my $x1 = $x + $dx[($dir+$d)%8];
|
|
327
|
0
|
|
|
|
|
0
|
my $y1 = $y + $dy[($dir+$d)%8];
|
|
328
|
0
|
|
|
|
|
0
|
my($NNN, $dir1) = ($near->[$y1][$x1], ($dir+2*$d)%8);
|
|
329
|
0
|
0
|
0
|
|
|
0
|
if ($NNN->[$dir] and not $NNN->[($dir1+1)%8] and not $NNN->[($dir1-1)%8]) {
|
|
|
|
|
0
|
|
|
|
|
|
330
|
0
|
|
|
|
|
0
|
push @res, ['K-fake-curve', $d];
|
|
331
|
0
|
|
|
|
|
0
|
next;
|
|
332
|
|
|
|
|
|
|
}
|
|
333
|
|
|
|
|
|
|
}
|
|
334
|
0
|
0
|
0
|
|
|
0
|
push @res, ['?'] and next if $bad or not ($dir & 0x1) and $N0min->[($dir + 2*$d)%8]; # Check last . on diamond and fork3
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
335
|
0
|
0
|
0
|
|
|
0
|
if ($dir & 0x1 and $Nmin->[($dir + 2*$d)%8] and not $Nmin->[($dir - 2*$d)%8]) { # Avoid the situation in K
|
|
|
|
|
0
|
|
|
|
|
|
336
|
0
|
|
|
|
|
0
|
my $dx1 = $dx[($dir + $d)%8];
|
|
337
|
0
|
|
|
|
|
0
|
my $dy1 = $dy[($dir + $d)%8];
|
|
338
|
0
|
|
|
|
|
0
|
my $last;
|
|
339
|
0
|
0
|
|
|
|
0
|
if ($pxmin->[$y+$dy + 2*$dy1][$x+$dx + 2*$dx1]) { # 1D469 𝑩 ; 1D483 𝒃;
|
|
340
|
|
|
|
|
|
|
# ++$marked,
|
|
341
|
0
|
0
|
0
|
|
|
0
|
push @res, ['Sharp', $d, 0, undef, undef, undef, ['L', $x+$dx, $y+$dy, ($dir+$d)%8, $d, 2, 2, 3, 2+$last], # 2nd 'S' optional now???
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
342
|
|
|
|
|
|
|
['S', $x+$dx, $y+$dy, ($dir+4)%8], ['S', $x+$dx1, $y+$dy1, ($dir+4+$d)%8], # Enforce line at dist=3
|
|
343
|
|
|
|
|
|
|
($px->[$y + 2*$dy + $dy1][$x + 2*$dx + $dx1]
|
|
344
|
|
|
|
|
|
|
? (['I1', $x + 2*$dx + $dx1, $y + 2*$dy + $dy1, ($dir+4)%8], ['II', $x + $dx + $dx1, $y + $dy + $dy1, $dir]) : ()),
|
|
345
|
|
|
|
|
|
|
['T',($dir + $d + 4)%8,$d]] and next
|
|
346
|
|
|
|
|
|
|
if $px->[$y+3*$dy1][$x+3*$dx1] # This is heavily hand-crafted to avoid false positives!
|
|
347
|
|
|
|
|
|
|
and ( $px->[$y + 2*$dy + 2*$dy1][$x + 2*$dx + 2*$dx1] xor $px->[$y + 2*$dy + $dy1][$x + 2*$dx + $dx1] ) # 1F590 🖐
|
|
348
|
|
|
|
|
|
|
and ( not $px->[$y + 2*$dy + $dy1][$x + 2*$dx + $dx1] or $cnt->[$y][$x] < 5 and $cnt->[$y+$dy1][$x+$dx1] < 6) # ऄ ፼
|
|
349
|
|
|
|
|
|
|
and not $px->[$y + $dy + 3*$dy1][$x + $dx + 3*$dx1]
|
|
350
|
|
|
|
|
|
|
and $cntmin->[$y][$x] < 6 # 0994 ঔ 210C ℌ
|
|
351
|
|
|
|
|
|
|
# and ( $cntmin->[$y][$x] + $cntmin->[$y+3*$dy1][$x+3*$dx1] < 10 ) # not needed: 1F5FD 🗽
|
|
352
|
|
|
|
|
|
|
and (not $px->[$y-$dy1][$x-$dx1] or $cntmin->[$y][$x] + $cntmin->[$y-$dy1][$x-$dx1] < 10) # 1D4CC 𝓌
|
|
353
|
|
|
|
|
|
|
and (not $px->[$y-$dy+$dy1][$x-$dx+$dx1] or $cnt->[$y][$x] + $cnt->[$y-$dy+$dy1][$x-$dx+$dx1] < 9) # 11C17 𑰗, 1F38E 🎎
|
|
354
|
|
|
|
|
|
|
and (not $px->[$y+4*$dy1][$x+4*$dx1] or not $px->[$y+5*$dy1][$x+5*$dx1]
|
|
355
|
|
|
|
|
|
|
or $cntmin->[$y][$x] + $cntmin->[$y+4*$dy1][$x+4*$dx1] + $cntmin->[$y+5*$dy1][$x+5*$dx1] < 15) # 1D4C9 𝓉
|
|
356
|
|
|
|
|
|
|
and grep !$px->[$y + $_*$dy1 - $dy][$x + $_*$dx1 - $dx], 2,3,4 # 16B6 ᚶ
|
|
357
|
|
|
|
|
|
|
and ( not $px->[$y + 2*$dy + $dy1][$x + 2*$dx + $dx1] # 114C7 𑓇
|
|
358
|
|
|
|
|
|
|
or ( ( grep $px->[$y + 4*$dy1 + $_*($dy-$dy1)][$x + 4*$dx1 + $_*($dx-$dx1)],0,1 # 1160F 𑘏
|
|
359
|
|
|
|
|
|
|
or $px->[$y - $dy1][$x - $dx1] ) # 1F590 🖐
|
|
360
|
|
|
|
|
|
|
and $cntmin->[$y+3*$dy1][$x+3*$dx1] < 6) # 1D752 𝝒
|
|
361
|
|
|
|
|
|
|
and $cnt->[$y+2*$dy+$dy1][$x+2*$dx+$dx1] > 2) # 1D7C5 𝟅
|
|
362
|
|
|
|
|
|
|
and ($last = !!$px->[$y + 4*$dy1][$x + 4*$dx1] or $px->[$y + 5*$dy1 - $dy][$x + 5*$dx1 - $dx]); # 1D491 𝒑
|
|
363
|
|
|
|
|
|
|
} else {
|
|
364
|
0
|
0
|
0
|
|
|
0
|
push @res, ['Sharp', $d, 0, undef, undef, undef, ['L', $x+$dx, $y+$dy, ($dir+$d)%8, $d, 1, 2, 2+$last], # 2nd 'S' optional now (N)
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
365
|
|
|
|
|
|
|
['S', $x+$dx, $y+$dy, ($dir+4)%8], ['S', $x+$dx1, $y+$dy1, ($dir+4+$d)%8],
|
|
366
|
|
|
|
|
|
|
['T',($dir + $d + 4)%8,$d]] and next # Enforce line at dist=2
|
|
367
|
|
|
|
|
|
|
if ($last = !!$px->[$y+3*$dy1][$x+3*$dx1] or $px->[$y + 4*$dy1 - $dy][$x + 4*$dx1 - $dx])
|
|
368
|
|
|
|
|
|
|
and $px->[$y + 2*$dy + $dy1][$x + 2*$dx + $dx1] and ($last or !$px->[$y + 3*$dy1 - $dy][$x + 3*$dx1 - $dx])
|
|
369
|
|
|
|
|
|
|
and ($last or $cntmin->[$y+$dy1][$x+$dx1] < 6 # with $last, not beneficial
|
|
370
|
|
|
|
|
|
|
and ($cntmin->[$y][$x] < 6
|
|
371
|
|
|
|
|
|
|
and ($cntmin->[$y][$x] < 5 or $px->[$y-$dy1][$x-$dx1] and $cntmin->[$y-$dy1][$x-$dx1] < 4))); # 1d54d 𝕍
|
|
372
|
|
|
|
|
|
|
}
|
|
373
|
|
|
|
|
|
|
} # Now general catch-all:
|
|
374
|
0
|
|
|
|
|
0
|
my $opp = ($dir + 4)%8;
|
|
375
|
0
|
0
|
|
|
|
0
|
my @dep = grep { $_ ne $opp and $Nmin->[$_]} 0..7;
|
|
|
0
|
|
|
|
|
0
|
|
|
376
|
|
|
|
|
|
|
# warn "generic: $x,$y, $dir, $d; (@dep);; $seen_next2;;; ", map $N->[$_] || 0, 0..7;
|
|
377
|
0
|
0
|
|
|
|
0
|
push @res, [($Nmin->[($dir - 2*$d)%8] ? '3fork3' : 'rhombus'), $d, 0, ['´', [$x + $dx, $y + $dy, @dep]]] and next;
|
|
|
|
0
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
}
|
|
379
|
|
|
|
|
|
|
# warn "maybe curve: ($x,$y,$dir): ", $bad||0,"\n";
|
|
380
|
0
|
0
|
0
|
|
|
0
|
push @res, ['curve', $d, undef, undef, undef, undef, ['C', $d]] and next unless $bad;
|
|
381
|
0
|
0
|
0
|
|
|
0
|
if ($N0min->[($dir + 4)%8] and $cN0min <= 4 and $dir & 0x1) { # check for double-arrow joint (21a0, 0239)
|
|
|
|
|
0
|
|
|
|
|
|
382
|
0
|
|
|
|
|
0
|
my($mirY, $mirX) = ($y + 2*$dy[$baddir], $x + 2*$dx[$baddir]);
|
|
383
|
0
|
0
|
0
|
|
|
0
|
push @res, ['arrow', -$d, 0, ['…', [$x+$dx, $y+$dy, ($dir + 4)%8, ($dir + 4 + $d)%8, ($dir + 4 + 2*$d)%8]], # On barb going to tip
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
384
|
|
|
|
|
|
|
undef, undef, ['a', -$d, 1]] and next # remove, unignore, @rest
|
|
385
|
|
|
|
|
|
|
if $px->[$mirY][$mirX] and $cntmin->[$mirY][$mirX] <= 4 and $cnt->[$mirY][$mirX] >= 4 and $nearmin->[$mirY][$mirX][($dir-2*$d)%8];
|
|
386
|
|
|
|
|
|
|
}
|
|
387
|
0
|
0
|
|
|
|
0
|
push @res, ['fake-curve', $d, 0, ['curve', [$x + $dx[$baddir], $y + $dy[$baddir], ($dir+$d)%8]]] and next;
|
|
388
|
|
|
|
|
|
|
}
|
|
389
|
6
|
|
|
|
|
22
|
return \@res;
|
|
390
|
|
|
|
|
|
|
} # dependent for a parallelogram (HV one) should be submitted only once...
|
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
sub clear_edge ($$$$) {
|
|
393
|
0
|
|
|
0
|
0
|
0
|
my ($e, $edge, $cntedge, $lastedge) = (shift, shift, shift, shift);
|
|
394
|
0
|
|
|
|
|
0
|
my($x, $y, $dir, $x1, $y1) = @$e;
|
|
395
|
|
|
|
|
|
|
# warn "clear $x, $y, $dir, $x1, $y1";
|
|
396
|
0
|
|
|
|
|
0
|
my $dir1 = ($dir+4)%8;
|
|
397
|
0
|
|
|
|
|
0
|
$edge->[$y][$x][$dir] = 0; $edge->[$y1][$x1][$dir1] = 0;
|
|
|
0
|
|
|
|
|
0
|
|
|
398
|
0
|
|
|
|
|
0
|
$cntedge->[$y][$x]--; $cntedge->[$y1][$x1]--;
|
|
|
0
|
|
|
|
|
0
|
|
|
399
|
0
|
|
|
|
|
0
|
for my $l ([$x, $y], [$x1, $y1]) {
|
|
400
|
0
|
0
|
|
|
|
0
|
next if $cntedge->[$l->[1]][$l->[0]] != 1;
|
|
401
|
0
|
|
|
|
|
0
|
my $D = -1;
|
|
402
|
0
|
|
0
|
|
|
0
|
$edge->[$l->[1]][$l->[0]][$_] and $D = $_, last for 0..7;
|
|
403
|
0
|
|
|
|
|
0
|
$lastedge->[$l->[1]][$l->[0]] = $D; # Good only for 1-edge pixels
|
|
404
|
|
|
|
|
|
|
}
|
|
405
|
|
|
|
|
|
|
}
|
|
406
|
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
sub add_edge ($$$$) {
|
|
408
|
4
|
|
|
4
|
0
|
12
|
my($e, $edge, $cntedge, $lastedge) = (shift, shift, shift, shift);
|
|
409
|
4
|
|
|
|
|
11
|
my($x, $y, $dir, $x1, $y1) = @$e;
|
|
410
|
|
|
|
|
|
|
# warn "adding $x, $y, $dir, $x1, $y1";
|
|
411
|
4
|
|
|
|
|
8
|
my $dir1 = ($dir+4)%8;
|
|
412
|
4
|
|
|
|
|
11
|
$edge->[$y][$x][$dir]++; $edge->[$y1][$x1][$dir1]++;
|
|
|
4
|
|
|
|
|
10
|
|
|
413
|
4
|
|
|
|
|
10
|
$cntedge->[$y][$x]++; $cntedge->[$y1][$x1]++;
|
|
|
4
|
|
|
|
|
16
|
|
|
414
|
4
|
|
|
|
|
9
|
$lastedge->[$y][$x] = $dir; $lastedge->[$y1][$x1] = $dir1;
|
|
|
4
|
|
|
|
|
33
|
|
|
415
|
|
|
|
|
|
|
}
|
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
sub add_longedge ($$$$$) {
|
|
418
|
0
|
|
|
0
|
0
|
0
|
my($e, $longedges, $seenlong, $midLong, $inLong) = (shift, shift, shift, shift, shift);
|
|
419
|
0
|
|
|
|
|
0
|
my($x, $y, $dir, $x1, $y1, $rot) = @$e;
|
|
420
|
0
|
|
|
|
|
0
|
push @$longedges, [$x, $y, $x1,$y1, scalar @$longedges, $dir, $rot];
|
|
421
|
0
|
|
|
|
|
0
|
$seenlong->{$x, $y, $x1,$y1} = $seenlong->{$x1,$y1,$x, $y} = $longedges->[-1];
|
|
422
|
0
|
|
|
|
|
0
|
$midLong->{$x+$x1,$y+$y1}++;
|
|
423
|
0
|
|
|
|
|
0
|
$inLong->{$x1,$y1}++;
|
|
424
|
0
|
|
|
|
|
0
|
$inLong->{$x,$y}++;
|
|
425
|
|
|
|
|
|
|
}
|
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
sub clear_longedge ($$$$$) {
|
|
428
|
0
|
|
|
0
|
0
|
0
|
my($e, $longedges, $seenlong, $midLong, $inLong) = (shift, shift, shift, shift, shift);
|
|
429
|
0
|
|
|
|
|
0
|
my($x, $y, $x1, $y1, $offset) = @$e;
|
|
430
|
0
|
|
|
|
|
0
|
$longedges->[$offset] = 'erased';
|
|
431
|
0
|
|
|
|
|
0
|
delete $seenlong->{$x,$y,$x1,$y1};
|
|
432
|
0
|
|
|
|
|
0
|
delete $seenlong->{$x1,$y1,$x,$y};
|
|
433
|
0
|
|
|
|
|
0
|
$midLong->{$x+$x1,$y+$y1}--;
|
|
434
|
0
|
|
|
|
|
0
|
$inLong->{$x1,$y1}--;
|
|
435
|
0
|
|
|
|
|
0
|
$inLong->{$x,$y}--;
|
|
436
|
|
|
|
|
|
|
}
|
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
sub post_inspect_ray ($$$$;$) { # Not finished (and it is not exactly clear for what it is best to check...)
|
|
439
|
0
|
|
0
|
0
|
0
|
0
|
my($x, $y, $dir, $rays, $basetype) = (shift, shift, shift, shift, shift || '');
|
|
440
|
0
|
|
|
|
|
0
|
$rays = $rays->[$y][$x];
|
|
441
|
0
|
0
|
|
|
|
0
|
my $ray = $rays->[$dir] or die "Panic: x=$x, y=$y, dir=$dir - missing ray in post_inspect_ray($basetype)";
|
|
442
|
0
|
0
|
0
|
|
|
0
|
return 1 if $ray->[0] =~ /^m/ and $basetype =~ /^m/;
|
|
443
|
0
|
|
|
|
|
0
|
for my $next (1,-1) {
|
|
444
|
0
|
|
|
|
|
0
|
warn "Checking x=$x, y=$y, dir=$dir in post_inspect_ray($basetype) 1=", $rays->[($dir+1)%8] && $rays->[($dir+1)%8][0],
|
|
445
|
|
|
|
|
|
|
" -1=", $rays->[($dir-1)%8] && $rays->[($dir-1)%8][0], "\n" if debug && $basetype =~ /^m/;
|
|
446
|
0
|
0
|
0
|
|
|
0
|
return if $rays->[($dir+$next)%8] and $rays->[($dir+$next)%8][0] =~ /^[Dr3\WP]/; # 'Probable-curve' put here as an experiment XXX
|
|
447
|
|
|
|
|
|
|
}
|
|
448
|
|
|
|
|
|
|
# warn "Checking2: $ray->[0]\n" if $basetype =~ /^3/;
|
|
449
|
|
|
|
|
|
|
# Putting 'elses-ray' into the allowed list is not a good idea
|
|
450
|
|
|
|
|
|
|
# (although it may help half-way with some, like ɫ, у, and helps with Ѭ); maybe allow the caller to permit it???
|
|
451
|
0
|
|
|
|
|
0
|
$ray->[0] =~ /^[dctB1fKZE]/; # doubleray, curve, (B)tail, 1Spur, [Zh/K-]fake-curve, Enforced with no ?/Dense/rhombus/3fork3 nearby
|
|
452
|
|
|
|
|
|
|
} # True if we want to keep the basetype
|
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
sub remove_px ($$$$$$) {
|
|
455
|
0
|
|
|
0
|
0
|
0
|
my($x, $y, $cnt, $px, $near, $off) = (shift, shift, shift, shift, shift, shift);
|
|
456
|
0
|
|
|
|
|
0
|
for my $dir ( @{ $off->[$y][$x] } ) {
|
|
|
0
|
|
|
|
|
0
|
|
|
457
|
0
|
|
|
|
|
0
|
my $dx = $dx[$dir];
|
|
458
|
0
|
|
|
|
|
0
|
my $dy = $dy[$dir];
|
|
459
|
0
|
|
|
|
|
0
|
$near->[$y+$dy][$x+$dx][($dir+4)%8] = 0;
|
|
460
|
0
|
|
|
|
|
0
|
$cnt->[$y+$dy][$x+$dx]--;
|
|
461
|
|
|
|
|
|
|
}
|
|
462
|
0
|
|
|
|
|
0
|
undef $px->[$y][$x];
|
|
463
|
|
|
|
|
|
|
}
|
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
sub force_line ($$) { # enforces 1 edge in this direction; inserts 'ignore' in $rem1 dirs at point, and in $rem2 dirs at next pt
|
|
466
|
0
|
|
|
0
|
0
|
0
|
my($how, $rays) = (shift, shift);
|
|
467
|
0
|
|
|
|
|
0
|
my($L, $x, $y, $dir, $rot, $len, @ROTS) = @$how; # $L ==eq== 'L' ignored
|
|
468
|
|
|
|
|
|
|
# warn "In force_line($L, $x, $y, $dir, $rot...)";
|
|
469
|
0
|
|
|
|
|
0
|
my $dx = $dx[$dir];
|
|
470
|
0
|
|
|
|
|
0
|
my $dy = $dy[$dir];
|
|
471
|
0
|
|
|
|
|
0
|
for my $i (0..($len - 1)) {
|
|
472
|
0
|
|
|
|
|
0
|
$rays->[$y+$i*$dy][$x+$i*$dx][$dir] = ['Enforced', 0, 1];
|
|
473
|
0
|
|
|
|
|
0
|
$rays->[$y+($i+1)*$dy][$x+($i+1)*$dx][($dir+4)%8] = ['Enforced', 0, 1];
|
|
474
|
|
|
|
|
|
|
}
|
|
475
|
0
|
0
|
|
|
|
0
|
return unless $rot;
|
|
476
|
0
|
|
|
|
|
0
|
my $dx1 = -$rot*$dy;
|
|
477
|
0
|
|
|
|
|
0
|
my $dy1 = $rot*$dx;
|
|
478
|
0
|
|
|
|
|
0
|
for my $i (0..$#ROTS) {
|
|
479
|
0
|
0
|
|
|
|
0
|
my @rot = ($i ? (2,3,1) : (1,2)); # Supported now: 0,1,2,3 (horizontal/vertical only)
|
|
480
|
0
|
|
|
|
|
0
|
@rot = @rot[0..($ROTS[$i]-1)];
|
|
481
|
0
|
|
|
|
|
0
|
for my $Rot (@rot) {
|
|
482
|
0
|
0
|
|
|
|
0
|
if ($Rot == 3) {
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
483
|
0
|
|
|
|
|
0
|
$rays->[$y+$i*$dy][$x+$i*$dx][($dir+3*$rot)%8][0] = 'ignore'; # 135° direction ($i > 0)
|
|
484
|
0
|
|
|
|
|
0
|
$rays->[$y+($i-1)*$dy+$dy1][$x+($i-1)*$dx+$dx1][($dir-$rot)%8][0] = 'ignore';
|
|
485
|
|
|
|
|
|
|
} elsif ($Rot == 2) {
|
|
486
|
0
|
|
|
|
|
0
|
$rays->[$y+$i*$dy][$x+$i*$dx][($dir+2*$rot)%8][0] = 'ignore'; # Perpendicular direction on ≥1
|
|
487
|
0
|
|
|
|
|
0
|
$rays->[$y+$i*$dy+$dy1][$x+$i*$dx+$dx1][($dir-2*$rot)%8][0] = 'ignore';
|
|
488
|
|
|
|
|
|
|
} elsif ($Rot == 1) {
|
|
489
|
0
|
|
|
|
|
0
|
$rays->[$y+$i*$dy][$x+$i*$dx][($dir+$rot)%8][0] = 'ignore'; # 45° direction on ≥3
|
|
490
|
0
|
|
|
|
|
0
|
$rays->[$y+($i+1)*$dy+$dy1][$x+($i+1)*$dx+$dx1][($dir-3*$rot)%8][0] = 'ignore';
|
|
491
|
0
|
|
|
|
|
0
|
} else { die "Rot=$Rot" }
|
|
492
|
|
|
|
|
|
|
}
|
|
493
|
|
|
|
|
|
|
}
|
|
494
|
|
|
|
|
|
|
}
|
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
sub doRays ($$$$$$$$$) {
|
|
497
|
2
|
|
|
2
|
0
|
36
|
my($bm,$width,$height, $offs, $cnt, $cntmin, $near, $nearmin) = (shift, shift, shift, shift, shift, shift, shift, shift, shift);
|
|
498
|
2
|
|
|
|
|
6
|
my(@offs, @cnt, @cntmin, @near, @nearmin);
|
|
499
|
2
|
|
|
|
|
6
|
@offs = @$offs; @cnt = @$cnt; @cntmin = @$cntmin; @near = @$near; @nearmin = @$nearmin;
|
|
|
2
|
|
|
|
|
7
|
|
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
6
|
|
|
|
2
|
|
|
|
|
5
|
|
|
500
|
2
|
|
|
|
|
43
|
my @pixelsmin = map [@$_], @$bm; # deep copy
|
|
501
|
2
|
|
|
|
|
9
|
my($ER, @rays, @longedges, %seenlong, %inLong, %midLong) = (['']);
|
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
DO_RAYS:
|
|
504
|
2
|
|
|
|
|
7
|
for my $ray_round (0,1) { # On the second round, some pixels may be decided to be insignificant, and removed
|
|
505
|
2
|
|
|
|
|
7
|
for my $y (1..$height) { # Inspect angular neighborhods in the directions from $offs
|
|
506
|
10
|
|
|
|
|
24
|
my(@r, @row) = [];
|
|
507
|
10
|
|
|
|
|
29
|
for my $x ( 1..$width ) {
|
|
508
|
82
|
100
|
50
|
|
|
300
|
push @r, [] and next unless my $o = $offs[$y][$x];
|
|
509
|
6
|
|
|
|
|
13
|
my $r = $rays[$y][$x];
|
|
510
|
6
|
50
|
|
|
|
14
|
my @o = grep { !$r->[$_] or $r->[$_][0] =~ /^[D\WP]/ } @$o; # 'Probable-curve' put here as an experiment XXX; Dense
|
|
|
8
|
|
|
|
|
31
|
|
|
511
|
6
|
|
|
|
|
11
|
my @rr;
|
|
512
|
6
|
50
|
|
|
|
18
|
@rr = @$r if $r;
|
|
513
|
6
|
|
|
|
|
26
|
@rr[ @o ] = @{ inspect_ray $x, $y, \@cnt, \@cntmin, $bm, \@pixelsmin, \@near, \@nearmin, \@o };
|
|
|
6
|
|
|
|
|
31
|
|
|
514
|
6
|
|
|
|
|
23
|
push @r, \@rr;
|
|
515
|
|
|
|
|
|
|
}
|
|
516
|
10
|
|
|
|
|
25
|
$rays[$y] = \@r;
|
|
517
|
|
|
|
|
|
|
}
|
|
518
|
2
|
|
|
|
|
6
|
my(@b_postpone, @g_postpone, @rem_postpone, @un_postpone, @protect, @rhombi, %rhombi, @extra_postpone);
|
|
519
|
2
|
|
|
|
|
6
|
for my $y (1..$height) { # 2nd order inspection: check identified dependencies (all dependencies must match;
|
|
520
|
10
|
|
|
|
|
43
|
for my $x ( 1..$width ) { # every dependency must be good in at least one direction)
|
|
521
|
82
|
|
|
|
|
127
|
for my $dir ( @{$offs[$y][$x]} ) {
|
|
|
82
|
|
|
|
|
204
|
|
|
522
|
8
|
|
50
|
|
|
24
|
my $ray = $rays[$y][$x][$dir] || next;
|
|
523
|
8
|
|
|
|
|
25
|
my($keep, $TO, $type, $rot, $checked, $DEP, $remove, $unignore, @rest) = (1, undef, @$ray);
|
|
524
|
8
|
100
|
66
|
|
|
82
|
next unless $DEP or @rest or $remove;
|
|
|
|
|
66
|
|
|
|
|
|
525
|
4
|
50
|
33
|
|
|
19
|
my @DEP = (($DEP and not ref $DEP->[0]) ? $DEP : ($DEP ? @$DEP : ()));
|
|
|
|
50
|
|
|
|
|
|
|
526
|
4
|
|
|
|
|
8
|
for my $ddep (@DEP) { # Switch to the first "alternative variant" with non-satisfied dependencies
|
|
527
|
|
|
|
|
|
|
# Preferred alternatives are grouped by a new-type $ddep->[0], and list of dependent points/dirs;
|
|
528
|
|
|
|
|
|
|
# the alternative is chosen out of those for which one of points has all dirs “good”,
|
|
529
|
0
|
|
|
|
|
0
|
$keep = 0;
|
|
530
|
0
|
|
|
|
|
0
|
for my $depPt (@$ddep[1..$#$ddep]) { # we are OR-ing over the dependencies: we keep if any one matches
|
|
531
|
0
|
|
|
|
|
0
|
my($KEEP,$X,$Y,@DIR) = (1, @$depPt);
|
|
532
|
0
|
|
|
|
|
0
|
for my $DIR (@DIR) { # we are AND-ing over the directions of a dependence: KEEP if all match
|
|
533
|
0
|
0
|
|
|
|
0
|
$KEEP = 0, last unless post_inspect_ray($X, $Y, $DIR, \@rays, $type); # Can be optimized by merging neighbors???
|
|
534
|
|
|
|
|
|
|
}
|
|
535
|
0
|
0
|
|
|
|
0
|
$keep = 1, last if $KEEP;
|
|
536
|
|
|
|
|
|
|
}
|
|
537
|
0
|
0
|
0
|
|
|
0
|
$TO = $ddep->[0] || '.', last unless $keep;
|
|
538
|
|
|
|
|
|
|
}
|
|
539
|
4
|
50
|
33
|
|
|
12
|
push @rem_postpone, $remove if $remove and $keep;
|
|
540
|
4
|
50
|
33
|
|
|
12
|
push @un_postpone, $unignore if $unignore and $keep;
|
|
541
|
4
|
50
|
33
|
|
|
29
|
push @g_postpone, [$y, $x, $dir, @rest] if @rest and $keep;
|
|
542
|
4
|
50
|
|
|
|
12
|
push @b_postpone, [$y, $x, $dir, $TO] unless $keep;
|
|
543
|
4
|
50
|
33
|
|
|
22
|
if ($keep and $type =~ /^[3r]/) {{ # 3fork3, rhombus
|
|
544
|
0
|
|
|
|
|
0
|
my($dx,$dy) = ($dx[$dir],$dy[$dir]);
|
|
|
0
|
|
|
|
|
0
|
|
|
545
|
0
|
0
|
|
|
|
0
|
next unless $rays[$y+$dy][$x+$dx][($dir+4)%8][0] =~ /^([dcfs])/; # part of a curve (maybe falsely fake) doubleray curve fake-curve serif
|
|
546
|
0
|
0
|
|
|
|
0
|
$rhombi{$x,$y,$dir}++ unless $1 eq 's'; # serif
|
|
547
|
0
|
|
|
|
|
0
|
push @rhombi, [$x, $y, $dir, $rot, $dx, $dy, "$1"];
|
|
548
|
|
|
|
|
|
|
}}
|
|
549
|
4
|
50
|
33
|
|
|
21
|
push @g_postpone, [$y, $x, $dir, [$TO]] if !$keep and $TO eq '´'; # 3fork3, rhombus; check for Q-joins
|
|
550
|
|
|
|
|
|
|
}
|
|
551
|
|
|
|
|
|
|
}
|
|
552
|
|
|
|
|
|
|
}
|
|
553
|
2
|
|
|
|
|
11
|
while (my $p = shift @rhombi) {
|
|
554
|
0
|
|
|
|
|
0
|
my($x, $y, $dir, $rot, $dx, $dy, $t) = @$p;
|
|
555
|
0
|
|
|
|
|
0
|
my $dir1 = ($dir+$rot)%8;
|
|
556
|
0
|
|
|
|
|
0
|
my($dx1,$dy1, @opp) = ($dx[$dir1],$dy[$dir1]);
|
|
557
|
0
|
0
|
|
|
|
0
|
unless (@opp = grep $rhombi{$x+$dx+$dx1,$y+$dy+$dy1,($_+4)%8}, $dir, $dir1) {
|
|
558
|
0
|
0
|
0
|
|
|
0
|
$rays[$y][$x][$dir][0] = 'Rhombus-force' if $t eq 's' and $cnt[$y+$dy1][$x+$dx1] <= 4; # As in д, and skip dense
|
|
559
|
0
|
|
|
|
|
0
|
next;
|
|
560
|
|
|
|
|
|
|
}
|
|
561
|
0
|
0
|
|
|
|
0
|
next if $t eq 's'; # Done for in-serif
|
|
562
|
|
|
|
|
|
|
# Should watch so that we do not break 0663,0d96,1ba7 ٣ ඖ ᮧ
|
|
563
|
0
|
0
|
0
|
|
|
0
|
if (!($dir%2) and $cnt[$y][$x] == 3 and ($rays[$y][$x][($dir+4)%8] || $ER)->[0] eq 'tail'
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
564
|
|
|
|
|
|
|
and $cnt[$y+$dy1][$x+$dx1] == 4 and ($rays[$y+$dy1][$x+$dx1][$dir] || $ER)->[0] eq 'doubleray'
|
|
565
|
|
|
|
|
|
|
and ($rays[$y+$dy+$dy1][$x+$dx+$dx1][$dir] || $ER)->[0] =~ /^[dc]/) { # Ддщц doubleray curve
|
|
566
|
0
|
|
|
|
|
0
|
$rays[$y][$x][$dir][0] = 'Rhombus-force';
|
|
567
|
0
|
|
|
|
|
0
|
next;
|
|
568
|
|
|
|
|
|
|
}
|
|
569
|
0
|
0
|
0
|
|
|
0
|
if ($cnt[$y+$dy+$dy1][$x+$dx+$dx1] == 3
|
|
|
|
|
0
|
|
|
|
|
|
570
|
|
|
|
|
|
|
and grep +(!($_%2) and ($rays[$y+$dy+$dy1][$x+$dx+$dx1][$_] || $ER)->[0] eq 'tail'), @opp) {
|
|
571
|
|
|
|
|
|
|
# Ддщц - on the other side. Detect which one is better. Should watch so that we do not break 0663,0d96,1ba7
|
|
572
|
0
|
|
|
|
|
0
|
my($good, $other) = ([$dir, $dx, $dy], [$dir1, $dx1, $dy1]); # default
|
|
573
|
0
|
|
|
|
|
0
|
for my $g ($other) { # no need to check for $good!
|
|
574
|
0
|
0
|
|
|
|
0
|
($good, $other) = ($g, $good) if $rays[$y+$g->[2]][$x+$g->[1]][($g->[0]+4)%8][0] eq 'doubleray';
|
|
575
|
|
|
|
|
|
|
}
|
|
576
|
0
|
0
|
0
|
|
|
0
|
if ($cnt[$y+$good->[2]][$x+$good->[1]] == 4
|
|
|
|
|
0
|
|
|
|
|
|
577
|
|
|
|
|
|
|
and $rays[$y+$good->[2]][$x+$good->[1]][($good->[0]+4)%8][0] eq 'doubleray'
|
|
578
|
|
|
|
|
|
|
and $rays[$y][$x][($good->[0]+4)%8][0] =~ /^[dc]/) { # doubleray curve
|
|
579
|
0
|
|
|
|
|
0
|
$rays[$y][$x][$other->[0]][0] = $rays[$y+$other->[2]][$x+$other->[1]][($other->[0]+4)%8][0] = 'ignore';
|
|
580
|
0
|
|
|
|
|
0
|
push @extra_postpone, ['R', $y, $x, $good->[0]]; # may be changed to ' very soon; postpone until this
|
|
581
|
0
|
|
|
|
|
0
|
next;
|
|
582
|
|
|
|
|
|
|
}
|
|
583
|
|
|
|
|
|
|
}
|
|
584
|
0
|
|
|
|
|
0
|
$rays[$y][$x][$dir][0] = '2fork3';
|
|
585
|
|
|
|
|
|
|
add_longedge([$x, $y, $dir, $x+$dx+$dx1, $y+$dy+$dy1, $rot], \@longedges, \%seenlong, \%midLong, \%inLong)
|
|
586
|
0
|
0
|
|
|
|
0
|
unless $seenlong{$x, $y, $x+$dx+$dx1,$y+$dy+$dy1};
|
|
587
|
|
|
|
|
|
|
}
|
|
588
|
2
|
|
|
|
|
7
|
while (my $p = shift @b_postpone) {
|
|
589
|
0
|
|
|
|
|
0
|
my($y, $x, $dir, $what) = @$p;
|
|
590
|
0
|
|
|
|
|
0
|
$rays[$y][$x][$dir][0] = $what;
|
|
591
|
|
|
|
|
|
|
}
|
|
592
|
2
|
|
|
|
|
8
|
while (my $p = shift @un_postpone) {
|
|
593
|
0
|
|
|
|
|
0
|
my($x, $y, $dir) = @$p;
|
|
594
|
0
|
|
|
|
|
0
|
$protect[$y][$x][$dir]++
|
|
595
|
|
|
|
|
|
|
}
|
|
596
|
2
|
|
|
|
|
7
|
while (my $p = shift @g_postpone) {
|
|
597
|
4
|
|
|
|
|
12
|
my($Y, $X, $DIR, @p) = @$p;
|
|
598
|
4
|
|
|
|
|
9
|
for my $pp (@p) {
|
|
599
|
|
|
|
|
|
|
# warn "rays0 $rays[8][5][0] @$pp ", $rays[8][5][0] && "<$rays[8][5][0][0]>";
|
|
600
|
|
|
|
|
|
|
# warn "In g_postpone: (@$p)";
|
|
601
|
4
|
50
|
|
|
|
13
|
if ($pp->[0] =~ /^I((I)|1)?$/) { # (only fix '?' if II, and only in one direction unless I
|
|
602
|
0
|
|
|
|
|
0
|
(undef, my ($x, $y, @pp)) = @$pp;
|
|
603
|
0
|
|
|
|
|
0
|
for my $dir (@pp) {
|
|
604
|
0
|
0
|
0
|
|
|
0
|
next if $protect[$y][$x][$dir] or $2 and $rays[$y][$x][$dir][0] ne '?';
|
|
|
|
|
0
|
|
|
|
|
|
605
|
0
|
|
|
|
|
0
|
$rays[$y][$x][$dir][0] = 'ignore';
|
|
606
|
0
|
0
|
|
|
|
0
|
next if $1;
|
|
607
|
0
|
|
|
|
|
0
|
my $dx = $dx[$dir];
|
|
608
|
0
|
|
|
|
|
0
|
my $dy = $dy[$dir];
|
|
609
|
0
|
|
|
|
|
0
|
$rays[$y+$dy][$x+$dx][($dir+4)%8][0] = 'ignore';
|
|
610
|
|
|
|
|
|
|
}
|
|
611
|
|
|
|
|
|
|
next
|
|
612
|
0
|
|
|
|
|
0
|
}
|
|
613
|
4
|
50
|
|
|
|
22
|
force_line($pp, \@rays), next if $pp->[0] eq 'L';
|
|
614
|
4
|
50
|
|
|
|
13
|
if ($pp->[0] =~ /^E([Ef])?(m)?$/) { # Enforce a sane type (only on '?' if EE, on f if Ef)
|
|
615
|
0
|
|
|
|
|
0
|
(my($T, $m), undef, my ($x, $y, $dir)) = ($1, !!$2, @$pp);
|
|
616
|
|
|
|
|
|
|
# warn "t[0]=", ord $rays[$y][$x][$dir][0], "; ", ($rays[$y][$x][$dir][0] =~ /^[\WP]/), "; T=$rays[$y][$x][$dir][0]" if $m;
|
|
617
|
0
|
0
|
|
|
|
0
|
next unless $rays[$y][$x][$dir][0] =~ ($T ? ($T eq 'f' ? qr/^f/ : qr/^[?P]/) : qr/^[\WP]/); # '?' fake-curve Probable-curve
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
618
|
0
|
|
|
|
|
0
|
$rays[$y][$x][$dir][0] = 'Enforce';
|
|
619
|
|
|
|
|
|
|
# warn " -> t[0]=", ord $rays[$y][$x][$dir][0], "; ", ($rays[$y][$x][$dir][0] =~ /^[\WP]/), "; T=$rays[$y][$x][$dir][0]" if $m;
|
|
620
|
|
|
|
|
|
|
# $marked++ if $m;
|
|
621
|
0
|
|
|
|
|
0
|
next;
|
|
622
|
|
|
|
|
|
|
}
|
|
623
|
4
|
50
|
|
|
|
13
|
if (lc $pp->[0] eq 'n') { # Enforce notch
|
|
624
|
0
|
|
|
|
|
0
|
(undef, my ($x, $y, $dir)) = @$pp;
|
|
625
|
0
|
|
|
|
|
0
|
my $C = 2 + ($pp->[0] eq 'n');
|
|
626
|
0
|
0
|
0
|
|
|
0
|
next unless $cnt[$y][$x] == $C and $rays[$y][$x][$dir][0] eq '?';
|
|
627
|
0
|
|
|
|
|
0
|
$rays[$y][$x][$dir][0] = 'Enforce';
|
|
628
|
0
|
|
|
|
|
0
|
next;
|
|
629
|
|
|
|
|
|
|
}
|
|
630
|
4
|
50
|
|
|
|
11
|
if ($pp->[0] eq 'S') { # Enforce Sharp
|
|
631
|
0
|
|
|
|
|
0
|
(undef, my ($x, $y, $dir)) = @$pp;
|
|
632
|
0
|
0
|
0
|
|
|
0
|
next unless $cnt[$y][$x] == 4 and $rays[$y][$x][$dir][0] =~ /^[?fP´r]/; # '?' Probable-curve fake-curve rhombus '´'
|
|
633
|
0
|
|
|
|
|
0
|
$rays[$y][$x][$dir][0] = 'Enforce';
|
|
634
|
0
|
|
|
|
|
0
|
next;
|
|
635
|
|
|
|
|
|
|
}
|
|
636
|
4
|
50
|
|
|
|
11
|
if ($pp->[0] eq 'T') { # Enforce tip (on M etc.)
|
|
637
|
0
|
|
|
|
|
0
|
(undef, my($DIR, $rot)) = @$pp;
|
|
638
|
|
|
|
|
|
|
# warn "$X,$Y,$DIR";
|
|
639
|
0
|
0
|
0
|
|
|
0
|
next unless $cnt[$Y][$X] == 3 and $rays[$Y][$X][$DIR] and ($rays[$Y][$X][$DIR][0] || '') =~ /^t/; # tail
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
640
|
0
|
|
|
|
|
0
|
my $x = $X + $dx[$DIR];
|
|
641
|
0
|
|
|
|
|
0
|
my $y = $Y + $dy[$DIR];
|
|
642
|
0
|
|
|
|
|
0
|
my $dir = ($DIR+4)%8;
|
|
643
|
0
|
0
|
0
|
|
|
0
|
next unless $cnt[$y][$x] == 1 and $rays[$y][$x][$dir] and ($rays[$y][$x][$dir][0] || '') =~ /^d/; # doubleray
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
644
|
0
|
|
|
|
|
0
|
@{$rays[$y][$x][$dir]}[0,1] = ('MFork',-$rot); # was doubleray ____ TM
|
|
|
0
|
|
|
|
|
0
|
|
|
645
|
0
|
|
|
|
|
0
|
$rays[$Y][$X][$DIR][0] = 'Tail'; # was tail _/
|
|
646
|
0
|
|
|
|
|
0
|
next;
|
|
647
|
|
|
|
|
|
|
}
|
|
648
|
4
|
50
|
|
|
|
14
|
if ($pp->[0] =~ 'a') { # Check arrow backwards
|
|
649
|
0
|
|
|
|
|
0
|
my $t = $rays[$Y][$X][($DIR+4)%8][0];
|
|
650
|
0
|
|
|
|
|
0
|
push @extra_postpone, ['a', $Y, $X, $DIR, @$pp[1,2], $t];
|
|
651
|
0
|
0
|
|
|
|
0
|
next if $t =~ /^[dctNC]/; # doubleray, curve, tail, Near-corner or Corner-curve
|
|
652
|
0
|
|
|
|
|
0
|
$rays[$Y][$X][$DIR][0] = '…';
|
|
653
|
0
|
|
|
|
|
0
|
next;
|
|
654
|
|
|
|
|
|
|
}
|
|
655
|
4
|
50
|
|
|
|
30
|
if ($pp->[0] =~ 't') { # tail; check cedilla
|
|
656
|
4
|
|
|
|
|
11
|
my $dx = $dx[$DIR];
|
|
657
|
4
|
|
|
|
|
8
|
my $dy = $dy[$DIR];
|
|
658
|
4
|
|
|
|
|
11
|
my $T = $rays[$Y+$dy][$X+$dx][($DIR+4)%8];
|
|
659
|
4
|
50
|
|
|
|
25
|
next unless $T->[0] =~ /^c/;
|
|
660
|
0
|
|
|
|
|
0
|
my $rot = $T->[1];
|
|
661
|
0
|
0
|
|
|
|
0
|
next unless $rays[$Y][$X][($DIR+$rot+4)%8][0] =~ /^d/; # doubleray
|
|
662
|
0
|
|
|
|
|
0
|
my $dx1 = $dx[($DIR+$rot+4)%8];
|
|
663
|
0
|
|
|
|
|
0
|
my $dy1 = $dy[($DIR+$rot+4)%8];
|
|
664
|
0
|
0
|
|
|
|
0
|
next unless $cnt[$Y+$dy1][$X+$dx1] == 4;
|
|
665
|
0
|
0
|
|
|
|
0
|
next unless $rays[$Y+$dy1][$X+$dx1][($DIR+$rot+4)%8][0] =~ /^d/; # doubleray
|
|
666
|
0
|
0
|
|
|
|
0
|
next unless $cnt[$Y+2*$dy1][$X+2*$dx1] == 3; # See 6a81
|
|
667
|
|
|
|
|
|
|
# warn "... <$ER> #=$#{$rays[$Y+$dy1][$X+$dx1]} [@{$rays[$Y+$dy1][$X+$dx1]}] <$rays[$Y+$dy1][$X+$dx1][($DIR+2*$rot+4)%8]> x=", $X+$dx1, ", y=", $Y+$dy, ", dir=", ($DIR+2*$rot+4)%8;
|
|
668
|
|
|
|
|
|
|
# warn($rays->[10][7][6] ? "### <$rays->[10][7][6]> [@{$rays->[10][7][6]}] " . (defined $rays->[10][7][6][0]?'d':'u'):"###### not yet");
|
|
669
|
0
|
0
|
0
|
|
|
0
|
next unless ($rays[$Y+$dy1][$X+$dx1][($DIR+2*$rot+4)%8] || $ER)->[0] =~ /^e/; # elses-ray (opp fake-curve)
|
|
670
|
0
|
0
|
0
|
|
|
0
|
next unless ($rays[$Y+$dy1][$X+$dx1][($DIR+3*$rot+4)%8] || $ER)->[0] =~ /^f/; # fake-curve
|
|
671
|
0
|
|
|
|
|
0
|
my $dx2 = $dx[($DIR+2*$rot+4)%8];
|
|
672
|
0
|
|
|
|
|
0
|
my $dy2 = $dy[($DIR+2*$rot+4)%8];
|
|
673
|
0
|
0
|
|
|
|
0
|
next unless $cnt[$Y+$dy1+$dy2][$X+$dx1+$dy2] == 3;
|
|
674
|
0
|
0
|
0
|
|
|
0
|
next unless ($rays[$Y+$dy1+$dy2][$X+$dx1+$dx2][($DIR+2*$rot)%8] || $ER)->[0] =~ /^f/; # fake-curve
|
|
675
|
0
|
|
|
|
|
0
|
$rays[$Y+$dy1][$X+$dx1][($DIR+$rot+4)%8][0] = $rays[$Y+2*$dy1][$X+2*$dx1][($DIR+$rot)%8][0] = 'ignore';
|
|
676
|
0
|
|
|
|
|
0
|
$rays[$Y+$dy1][$X+$dx1][($DIR+2*$rot+4)%8][0] = 'Enforced';
|
|
677
|
0
|
|
|
|
|
0
|
$rays[$Y+$dy1+$dy2][$X+$dx1+$dx2][($DIR+2*$rot)%8][0] = 'curve';
|
|
678
|
0
|
|
|
|
|
0
|
next;
|
|
679
|
|
|
|
|
|
|
}
|
|
680
|
0
|
0
|
|
|
|
0
|
if ($pp->[0] =~ 'C') { # Check spurious connectors
|
|
681
|
0
|
|
|
|
|
0
|
my $dx = $dx[$DIR];
|
|
682
|
0
|
|
|
|
|
0
|
my $dy = $dy[$DIR];
|
|
683
|
0
|
|
|
|
|
0
|
my $T = $rays[$Y+$dy][$X+$dx][($DIR+4)%8];
|
|
684
|
0
|
0
|
0
|
|
|
0
|
next if $T->[0] !~ /^(c)/i or $midLong{2*$X+$dx,2*$Y+$dy};
|
|
685
|
0
|
|
|
|
|
0
|
my($opp, $good) = ($1);
|
|
686
|
|
|
|
|
|
|
# For curves (in both directions), check that going their intendend continuation (which is long) in opposite direction
|
|
687
|
|
|
|
|
|
|
# has another choice (is a Fork) that this (spurious!) line.
|
|
688
|
0
|
0
|
|
|
|
0
|
if ($opp eq 'c') {
|
|
689
|
0
|
|
|
|
|
0
|
my($seen, $arrows, $deg_corner) = ('', 0);
|
|
690
|
0
|
|
|
|
|
0
|
for my $C ([$X,$Y,($DIR + 4 + $T->[1])%8, $T->[1]], [$X+$dx,$Y+$dy,($DIR+$pp->[1])%8,$pp->[1]]) {
|
|
691
|
0
|
|
|
|
|
0
|
my($XX,$YY,$DD,$R) = @$C;
|
|
692
|
0
|
|
|
|
|
0
|
my $dx1 = $dx[$DD];
|
|
693
|
0
|
|
|
|
|
0
|
my $dy1 = $dy[$DD];
|
|
694
|
|
|
|
|
|
|
# warn "$X, $Y, $DIR; $XX, $YY, $DD, $R; $rays[$YY+$dy][$XX+$dx][($DD+4)%8] $rays[$YY][$XX][$DD]"
|
|
695
|
|
|
|
|
|
|
# unless defined $rays[$YY+$dy][$XX+$dx][($DD+4)%8] and defined $rays[$YY][$XX][$DD];
|
|
696
|
|
|
|
|
|
|
# Combination of 1 (Tail) and F is good (2af7, 0593)
|
|
697
|
0
|
0
|
0
|
|
|
0
|
$good = 1 unless $rays[$YY+$dy1][$XX+$dx1][($DD+4)%8][0] =~ /^([F°])/
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
698
|
|
|
|
|
|
|
and ($1 eq 'F' or $rays[$YY][$XX][($DD-3*$R)%8][0] =~ /^C/ and $deg_corner=1) # see 2aa1, 2af7
|
|
699
|
|
|
|
|
|
|
and $rays[$YY][$XX][$DD][0] =~ /^([cF1d])/ # Inserting d here requires test for 22, and hurts ῳ
|
|
700
|
|
|
|
|
|
|
and not ((my $m1 = $1) eq 'd' and $rays[$YY+$dy1][$XX+$dx1][$DD][0] =~ /^d/);
|
|
701
|
0
|
0
|
|
|
|
0
|
$seen .= $m1 unless $good; # Matchess succeeded!
|
|
702
|
0
|
0
|
0
|
|
|
0
|
$arrows++ if $rays[$YY][$XX][$DD][0] =~ /^d/ # doubleray
|
|
|
|
|
0
|
|
|
|
|
|
703
|
|
|
|
|
|
|
and $rays[$YY+$dy1][$XX+$dx1][$DD][0] =~ /^a/
|
|
704
|
|
|
|
|
|
|
and $rays[$YY+$dy1][$XX+$dx1][$DD][1] == -$R; # arrow 21f6 but not 222e
|
|
705
|
|
|
|
|
|
|
}
|
|
706
|
0
|
0
|
0
|
|
|
0
|
$good = 1 if !$good and $seen =~ /F1|1F|(11)/ and ($1 or not $deg_corner);
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
707
|
0
|
0
|
|
|
|
0
|
$good = 0 if $arrows == 2;
|
|
708
|
|
|
|
|
|
|
} else { # E.g., 2a85
|
|
709
|
0
|
|
|
|
|
0
|
for my $C ([$X,$Y,($DIR + 4 + 2*$T->[1])%8, 1, $T->[1]], [$X+$dx,$Y+$dy,($DIR+$pp->[1])%8, 0, $pp->[1]]) {
|
|
710
|
0
|
|
|
|
|
0
|
my($XX,$YY,$DD,$rev,$R) = @$C;
|
|
711
|
0
|
|
|
|
|
0
|
my $dx1 = $dx[$DD];
|
|
712
|
0
|
|
|
|
|
0
|
my $dy1 = $dy[$DD];
|
|
713
|
0
|
0
|
|
|
|
0
|
if ($rev) { # Went in the direction of 'Corner-curve'
|
|
714
|
0
|
0
|
0
|
|
|
0
|
$good = 1 unless $rays[$YY+$dy1][$XX+$dx1][($DD+4)%8][0] =~ /^C/
|
|
|
|
|
0
|
|
|
|
|
|
715
|
|
|
|
|
|
|
and $rays[$YY][$XX][$DD][0] =~ /^c/ and $rays[$YY][$XX][$DD][1] == $R;
|
|
716
|
|
|
|
|
|
|
} else {
|
|
717
|
0
|
0
|
0
|
|
|
0
|
$good = 1 unless $rays[$YY+$dy1][$XX+$dx1][($DD+4)%8][0] =~ /^°/
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
718
|
|
|
|
|
|
|
and ($rays[$YY][$XX][($DD-3*$R)%8] || $ER)->[0] =~ /^([1])/ # cC break a lot of stuff
|
|
719
|
|
|
|
|
|
|
and ($1 ne 'C'
|
|
720
|
|
|
|
|
|
|
or $rays[$YY][$XX][($DD-3*$R)%8][1] == $R)
|
|
721
|
|
|
|
|
|
|
and $rays[$YY][$XX][$DD][0] =~ /^([cd])/;
|
|
722
|
|
|
|
|
|
|
}
|
|
723
|
|
|
|
|
|
|
}
|
|
724
|
|
|
|
|
|
|
}
|
|
725
|
0
|
0
|
|
|
|
0
|
unless ($good) {
|
|
726
|
0
|
|
|
|
|
0
|
$rays[$Y][$X][$DIR][0] = '¢';
|
|
727
|
0
|
|
|
|
|
0
|
$opp =~ tr/cC/¢₡/;
|
|
728
|
0
|
|
|
|
|
0
|
$rays[$Y+$dy][$X+$dx][($DIR+4)%8][0] = $opp;
|
|
729
|
|
|
|
|
|
|
}
|
|
730
|
0
|
|
|
|
|
0
|
next;
|
|
731
|
|
|
|
|
|
|
}
|
|
732
|
0
|
0
|
|
|
|
0
|
if ($pp->[0] eq '´') { # Q-join
|
|
733
|
0
|
|
|
|
|
0
|
my $dx = $dx[$DIR];
|
|
734
|
0
|
|
|
|
|
0
|
my $dy = $dy[$DIR];
|
|
735
|
0
|
|
|
|
|
0
|
my $T = $rays[$Y+$dy][$X+$dx][($DIR+4)%8]; # stop if the opposite ray is already Enforce⸣d:
|
|
736
|
0
|
|
0
|
|
|
0
|
my $B = $T->[1] || 0;
|
|
737
|
0
|
|
|
|
|
0
|
my $dx0 = $dx[($DIR+$B)%8];
|
|
738
|
0
|
|
|
|
|
0
|
my $dy0 = $dy[($DIR+$B)%8];
|
|
739
|
0
|
|
|
|
|
0
|
my $dx1 = $dx[($DIR-$B)%8];
|
|
740
|
0
|
|
|
|
|
0
|
my $dy1 = $dy[($DIR-$B)%8];
|
|
741
|
0
|
|
|
|
|
0
|
my ($extra, @LOOP) = $nearmin[$Y+$dy1][$X+$dx1][($DIR-2*$B)%8];
|
|
742
|
0
|
0
|
0
|
|
|
0
|
if ($T->[0] eq 'Probable-curve') {
|
|
|
|
0
|
|
|
|
|
|
|
743
|
0
|
0
|
0
|
|
|
0
|
next unless (not @rem_postpone or $ray_round == 1) # Otherwise: triggered on Ӿ
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
744
|
|
|
|
|
|
|
and $rays[$Y-$dy0][$X-$dx0][($DIR+$B)%8][0] eq '´' # The last condition: OK on ɚӿޗ; false positive: ೫.
|
|
745
|
|
|
|
|
|
|
and not ($nearmin[$Y+$dy][$X+$dx][($DIR+2*$B)%8] and $nearmin[$Y-$dy0][$X-$dx0][($DIR+4)%8]);
|
|
746
|
|
|
|
|
|
|
# warn "X=$X, Y=$Y, DIR=$DIR, invROT = $B";
|
|
747
|
0
|
|
|
|
|
0
|
$rays[$Y][$X][$DIR][0] = 'Enforce'; # Was '´'
|
|
748
|
0
|
|
|
|
|
0
|
$rays[$Y+$dy][$X+$dx][($DIR+4)%8][0] = 'Enforce'; # Was 'Probable-curve'
|
|
749
|
0
|
|
|
|
|
0
|
$rays[$Y][$X][($DIR+4+$B)%8][0] = 'Enforce'; # Often is 'f....'
|
|
750
|
0
|
|
|
|
|
0
|
$rays[$Y-$dy0][$X-$dx0][($DIR+$B)%8][0] = 'Enforce'; # Was '´'
|
|
751
|
|
|
|
|
|
|
# $rays[$Y][$X][($DIR+$B+4)%8][0] =~ s/^\W.*/Enforce/ if $cntmin[$Y-$B*$dx1][$X+$B*$dy1] < 3; # length=1; Ȼ
|
|
752
|
|
|
|
|
|
|
# $marked = 1;
|
|
753
|
0
|
0
|
|
|
|
0
|
next unless $rays[$Y+$dy+$dy1][$X+$dx+$dx1][($DIR+4-$B)%8][0] eq '´';
|
|
754
|
0
|
|
|
|
|
0
|
$rays[$Y+$dy][$X+$dx][($DIR-$B)%8][0] =~ s/^\W.*/Enforce/;
|
|
755
|
0
|
|
|
|
|
0
|
$rays[$Y+$dy+$dy1][$X+$dx+$dx1][($DIR+4-$B)%8][0] = 'Enforce'; # Was '´'
|
|
756
|
0
|
0
|
|
|
|
0
|
next unless $cntmin[$Y+$dy1][$X+$dx1] <= 4 + !!$extra;
|
|
757
|
0
|
0
|
|
|
|
0
|
$LOOP[0]++ if $nearmin[$Y][$X][($DIR+4-$B)%8];
|
|
758
|
0
|
0
|
|
|
|
0
|
$LOOP[1]++ if $nearmin[$Y+$dy][$X+$dx][($DIR+$B)%8];
|
|
759
|
|
|
|
|
|
|
} elsif ($T->[0] eq '´' and $B == -$rays[$Y][$X][$DIR][1]) { # part of a convex curve
|
|
760
|
|
|
|
|
|
|
# Allow extra spurs coming in from outside/inside (see `Q´).
|
|
761
|
0
|
|
|
|
|
0
|
my @Ex = ($nearmin[$Y][$X][($DIR+2*$B)%8], $nearmin[$Y+$dy][$X+$dx][($DIR+2*$B)%8]);
|
|
762
|
|
|
|
|
|
|
# warn "[@$T], inC=$cntmin[$Y+$dy1][$X+$dx1], afterTargC=$cntmin[$Y+$dy+$dy1][$X+$dx+$dx1], preC=$cntmin[$Y-$B*$dx1][$X+$B*$dy1]";
|
|
763
|
0
|
0
|
0
|
|
|
0
|
next unless $cntmin[$Y+$dy1][$X+$dx1] <= 4 + !!$extra
|
|
|
|
|
0
|
|
|
|
|
|
764
|
|
|
|
|
|
|
and $cntmin[$Y+$dy+$dy1][$X+$dx+$dx1] <= 3 and $cntmin[$Y-$B*$dx1][$X+$B*$dy1] <= 3;
|
|
765
|
0
|
0
|
0
|
|
|
0
|
next unless $cntmin[$Y][$X] <= 3 + !!$Ex[0] and $cntmin[$Y+$dy][$X+$dx] <= 3 + !!$Ex[1];
|
|
766
|
0
|
|
|
|
|
0
|
$rays[$Y][$X][$DIR][0] = 'Enforce';
|
|
767
|
0
|
|
|
|
|
0
|
$rays[$Y+$dy][$X+$dx][($DIR+4)%8][0] = 'Enforce';
|
|
768
|
0
|
|
|
|
|
0
|
$rays[$Y-$B*$dx1][$X+$B*$dy1][($DIR+$B)%8][0] = 'Enforce';
|
|
769
|
0
|
0
|
|
|
|
0
|
$rays[$Y][$X][($DIR+$B+4)%8][0] =~ s/^\W.*/Enforce/ if $cntmin[$Y-$B*$dx1][$X+$B*$dy1] < 3; # length=1; Ȼ
|
|
770
|
0
|
|
|
|
|
0
|
$rays[$Y+$dy+$dy1][$X+$dx+$dx1][($DIR-$B+4)%8][0] = 'Enforce';
|
|
771
|
0
|
0
|
|
|
|
0
|
$rays[$Y+$dy][$X+$dx][($DIR-$B)%8][0] =~ s/^\W.*/Enforce/ if $cntmin[$Y+$dy+$dy1][$X+$dx+$dx1] < 3; # length=1; Ȼ
|
|
772
|
0
|
0
|
|
|
|
0
|
next if $extra;
|
|
773
|
|
|
|
|
|
|
} else {
|
|
774
|
0
|
|
|
|
|
0
|
next;
|
|
775
|
|
|
|
|
|
|
} # Emulate a double-width stroke:
|
|
776
|
0
|
0
|
|
|
|
0
|
for my $semiEdge (($LOOP[0] ? ([$Y, $X, ($DIR-$B)%8], [$Y+$dy1, $X+$dx1, ($DIR+4-$B)%8])
|
|
|
|
0
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
: ([$Y+$dy1, $X+$dx1, ($DIR+4)%8], [$Y+$dy1-$dy, $X+$dx1-$dx, $DIR])),
|
|
778
|
|
|
|
|
|
|
($LOOP[1] ? ([$Y+$dy, $X+$dx, ($DIR+$B+4)%8], [$Y+$dy1, $X+$dx1, ($DIR+$B)%8])
|
|
779
|
|
|
|
|
|
|
: ([$Y+$dy1, $X+$dx1, $DIR], [$Y+$dy1+$dy, $X+$dx1+$dx, ($DIR+4)%8]))) {
|
|
780
|
|
|
|
|
|
|
# $rays[$semiEdge->[0]][$semiEdge->[1]][$semiEdge->[2]][0] =~ s/^\W.*/Enforce/;
|
|
781
|
0
|
|
|
|
|
0
|
$rays[$semiEdge->[0]][$semiEdge->[1]][$semiEdge->[2]][0] = 'Enforce'; # Otherwise would not be considered simple due to ??
|
|
782
|
|
|
|
|
|
|
}
|
|
783
|
0
|
0
|
|
|
|
0
|
for my $semiEdge (($LOOP[0] ? () : ([$Y, $X, ($DIR-$B)%8], [$Y+$dy1, $X+$dx1, ($DIR+4-$B)%8])),
|
|
|
|
0
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
($LOOP[1] ? () : ([$Y+$dy, $X+$dx, ($DIR+$B+4)%8], [$Y+$dy1, $X+$dx1, ($DIR+$B)%8]))) {
|
|
785
|
0
|
|
|
|
|
0
|
$rays[$semiEdge->[0]][$semiEdge->[1]][$semiEdge->[2]][0] =~ s/^\w.*/Ignored/
|
|
786
|
|
|
|
|
|
|
# and $marked++;
|
|
787
|
|
|
|
|
|
|
}
|
|
788
|
0
|
|
|
|
|
0
|
next;
|
|
789
|
|
|
|
|
|
|
}
|
|
790
|
0
|
|
|
|
|
0
|
die "Unknown postpone action: <$pp->[0]>"
|
|
791
|
|
|
|
|
|
|
}
|
|
792
|
|
|
|
|
|
|
# $rays[$y][$x][$dir][0] = $what;
|
|
793
|
|
|
|
|
|
|
}
|
|
794
|
2
|
|
|
|
|
7
|
while (my $p = shift @extra_postpone) {
|
|
795
|
0
|
|
|
|
|
0
|
my($type, $Y, $X, $DIR, @p) = @$p;
|
|
796
|
0
|
0
|
|
|
|
0
|
if ($type eq 'a') { # Check for 'a' on the other side of the arrow
|
|
797
|
0
|
|
|
|
|
0
|
my $x = $X + 2*$dx[($DIR+$p[0])%8];
|
|
798
|
0
|
|
|
|
|
0
|
my $y = $Y + 2*$dy[($DIR+$p[0])%8];
|
|
799
|
0
|
|
|
|
|
0
|
my $dir = ($DIR-2*$p[0])%8;
|
|
800
|
0
|
0
|
0
|
|
|
0
|
$rays[$Y][$X][$DIR][0] = ($p[1] ? 'x-arrow' : '…')
|
|
|
|
0
|
0
|
|
|
|
|
|
801
|
|
|
|
|
|
|
unless $rays[$y][$x][$dir][0] =~ /^a/ and (not $p[1] or $rays[$y][$x][($dir+4)%8][0] eq $p[2]);
|
|
802
|
|
|
|
|
|
|
}
|
|
803
|
0
|
0
|
|
|
|
0
|
if ($type eq 'R') { # Check for 'a' on the other side of the arrow
|
|
804
|
0
|
|
|
|
|
0
|
$rays[$Y][$X][$DIR][0] = 'Rhombus-force';
|
|
805
|
|
|
|
|
|
|
}
|
|
806
|
|
|
|
|
|
|
}
|
|
807
|
2
|
50
|
|
|
|
10
|
last DO_RAYS unless @rem_postpone;
|
|
808
|
0
|
|
|
|
|
0
|
my @SEEN;
|
|
809
|
0
|
|
|
|
|
0
|
while (my $r = shift @rem_postpone) {
|
|
810
|
0
|
|
|
|
|
0
|
my($x, $y) = @$r;
|
|
811
|
0
|
0
|
|
|
|
0
|
remove_px($x, $y, \@cntmin, \@pixelsmin, \@nearmin, \@offs) unless $SEEN[$y][$x]++;
|
|
812
|
|
|
|
|
|
|
}
|
|
813
|
|
|
|
|
|
|
} # end DO_RAYS
|
|
814
|
2
|
|
|
|
|
6
|
for my $y (1..$height) { # In a pair '?'/'f', change '?' to 'Ignore'
|
|
815
|
10
|
|
|
|
|
21
|
for my $x ( 1..$width ) {
|
|
816
|
82
|
50
|
|
|
|
192
|
next unless my $RAYS = $rays[$y][$x];
|
|
817
|
82
|
100
|
|
|
|
184
|
next unless @$RAYS;
|
|
818
|
6
|
|
|
|
|
19
|
for my $dir (@{$offs[$y][$x]}) {
|
|
|
6
|
|
|
|
|
14
|
|
|
819
|
8
|
50
|
|
|
|
30
|
next unless $RAYS->[$dir][0] =~ /^(f)/i; # fake-curve/Fork
|
|
820
|
0
|
0
|
0
|
|
|
0
|
next if (my $code = $1) eq 'F' and $cnt[$y][$x] != 1; # Sharp corners (as in V) may result in fork with |stem|=1
|
|
821
|
0
|
|
|
|
|
0
|
my $x1 = $x + $dx[$dir];
|
|
822
|
0
|
|
|
|
|
0
|
my $y1 = $y + $dy[$dir];
|
|
823
|
0
|
|
|
|
|
0
|
my $dir1 = ($dir+4)%8;
|
|
824
|
0
|
0
|
0
|
|
|
0
|
$rays[$y1][$x1][$dir1][0] =~ s/^[?1P].*/Tail/ and (@{$RAYS->[$dir]}[0,1] = ('MFork',0)), next # symmetric MFork
|
|
|
0
|
|
|
|
|
0
|
|
|
825
|
|
|
|
|
|
|
if $code eq 'F'; # '?', 'Probable-curve', '1Spur'
|
|
826
|
0
|
|
|
|
|
0
|
$rays[$y1][$x1][$dir1][0] =~ s/^[?P""].*/Ignore/; # '?', 'Probable-curve', '"'
|
|
827
|
|
|
|
|
|
|
}
|
|
828
|
|
|
|
|
|
|
}
|
|
829
|
|
|
|
|
|
|
}
|
|
830
|
2
|
50
|
33
|
|
|
24
|
die '$ER corrupted' unless @$ER == 1 and $ER->[0] eq '';
|
|
831
|
2
|
|
|
|
|
31
|
[\@rays, \@longedges, \%seenlong, \%inLong, \%midLong];
|
|
832
|
|
|
|
|
|
|
}
|
|
833
|
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
sub do_Simple_and_edges ($$$$$$$$$) {
|
|
835
|
2
|
|
|
2
|
0
|
30
|
my($ER, $width, $height, $RAYS, $offs, $cnt, $longedges, $seenlong, $inLong, $midLong)
|
|
836
|
|
|
|
|
|
|
= ([''], shift, shift, shift, shift, shift, shift, shift, shift, shift);
|
|
837
|
2
|
|
|
|
|
8
|
my @rays = @$RAYS;
|
|
838
|
2
|
|
|
|
|
28
|
my(@Simple, @simpleray, @edge, @cntedge, @lastedge, @update); # Simple points/rays; decided edges
|
|
839
|
2
|
|
|
|
|
8
|
for my $y (1..$height) { # Identify simple points/rays
|
|
840
|
10
|
|
|
|
|
23
|
for my $x ( 1..$width ) {
|
|
841
|
82
|
50
|
|
|
|
226
|
next unless my $RAYS = $rays[$y][$x];
|
|
842
|
82
|
100
|
|
|
|
185
|
next unless @$RAYS; # Contamination???
|
|
843
|
6
|
50
|
|
|
|
12
|
$Simple[$y][$x] = 1, next unless grep { $RAYS->[$_][0] =~ /^[D\WP]/ } @{$offs->[$y][$x]}; # Dense; junk
|
|
|
8
|
|
|
|
|
41
|
|
|
|
6
|
|
|
|
|
15
|
|
|
844
|
|
|
|
|
|
|
}
|
|
845
|
|
|
|
|
|
|
}
|
|
846
|
2
|
|
|
|
|
6
|
for my $y (1..$height) { # In a pair '?'/'e' with simple neighbor, change '?' to 'Ignore'
|
|
847
|
10
|
|
|
|
|
21
|
for my $x ( 1..$width ) {
|
|
848
|
82
|
50
|
|
|
|
177
|
next unless my $RAYS = $rays[$y][$x];
|
|
849
|
82
|
100
|
|
|
|
181
|
next unless @$RAYS;
|
|
850
|
6
|
50
|
|
|
|
17
|
next unless $Simple[$y][$x];
|
|
851
|
6
|
|
|
|
|
9
|
for my $dir (@{$offs->[$y][$x]}) {
|
|
|
6
|
|
|
|
|
16
|
|
|
852
|
8
|
50
|
|
|
|
26
|
next unless $RAYS->[$dir][0] =~ /^e/;
|
|
853
|
0
|
|
|
|
|
0
|
my $x1 = $x + $dx[$dir];
|
|
854
|
0
|
|
|
|
|
0
|
my $y1 = $y + $dy[$dir];
|
|
855
|
0
|
|
|
|
|
0
|
my $dir1 = ($dir+4)%8;
|
|
856
|
0
|
|
|
|
|
0
|
$rays[$y1][$x1][$dir1][0] =~ s/^[?P""].*/Ignore/; # 'Probable-curve', '?' '"'
|
|
857
|
|
|
|
|
|
|
}
|
|
858
|
|
|
|
|
|
|
}
|
|
859
|
|
|
|
|
|
|
}
|
|
860
|
|
|
|
|
|
|
#### warn "... <@{$rays[7][2][0]||['undef']}> <@{$rays[7][2][1]||['undef']}>";
|
|
861
|
2
|
|
|
|
|
6
|
for my $y (1..$height) { # Identify simple points/rays
|
|
862
|
10
|
|
|
|
|
21
|
for my $x ( 1..$width ) {
|
|
863
|
82
|
50
|
|
|
|
176
|
next unless my $RAYS = $rays[$y][$x];
|
|
864
|
82
|
100
|
|
|
|
207
|
next unless @$RAYS; # Contamination???
|
|
865
|
|
|
|
|
|
|
# next if $Simple[$y][$x];
|
|
866
|
|
|
|
|
|
|
# Recalc simple:
|
|
867
|
6
|
50
|
|
|
|
12
|
$Simple[$y][$x] = 1, next unless grep { $RAYS->[$_][0] =~ /^[D\WP]/ } @{$offs->[$y][$x]}; # Dense; junk; Probable-curve
|
|
|
8
|
|
|
|
|
31
|
|
|
|
6
|
|
|
|
|
13
|
|
|
868
|
|
|
|
|
|
|
FIND_GOOD:
|
|
869
|
0
|
|
|
|
|
0
|
for my $dir ( @{$offs->[$y][$x]} ) { # For non-simple vertices, find simple directions (it+neighbors non-dense/junk)
|
|
|
0
|
|
|
|
|
0
|
|
|
870
|
0
|
0
|
|
|
|
0
|
next if $RAYS->[$dir][0] =~ /^[D\WP]/;
|
|
871
|
0
|
|
|
|
|
0
|
for my $rot (1, -1) { # Skip if closest angular neighbor is bad (Dense/Probable)
|
|
872
|
|
|
|
|
|
|
#### warn "miss dir: (x,y,dir,rot)=($x,$y,$dir,$rot); lst=$#$RAYS, ", grep defined $RAYS->[$_], 0..$#$RAYS unless $RAYS->[($dir+$rot)%8];
|
|
873
|
0
|
0
|
0
|
|
|
0
|
next FIND_GOOD if ($RAYS->[($dir+$rot)%8] || $ER)->[0] =~ /^[D\WP]/ and not $RAYS->[$dir][0] =~ /^[BE]/; # Btail/Enforce are checked already!
|
|
|
|
|
0
|
|
|
|
|
|
874
|
|
|
|
|
|
|
}
|
|
875
|
0
|
|
|
|
|
0
|
$simpleray[$y][$x][$dir]++;
|
|
876
|
|
|
|
|
|
|
# warn "Simple RAY at x=$x y=$y dir=$dir\n";
|
|
877
|
|
|
|
|
|
|
}
|
|
878
|
|
|
|
|
|
|
}
|
|
879
|
|
|
|
|
|
|
}
|
|
880
|
|
|
|
|
|
|
# warn "Simple RAY ====\n";
|
|
881
|
2
|
|
|
|
|
15
|
for my $y (1..$height) { # Identify simple edges (should be simple in both directions, and of non-fake types)
|
|
882
|
10
|
|
|
|
|
21
|
for my $x ( 1..$width ) {
|
|
883
|
82
|
50
|
|
|
|
179
|
next unless my $RAYS = $rays[$y][$x];
|
|
884
|
82
|
100
|
|
|
|
209
|
my $ok = $Simple[$y][$x]
|
|
885
|
|
|
|
|
|
|
or my $smpl = $simpleray[$y][$x]; # Not cleared if $ok; we do not care
|
|
886
|
82
|
|
|
|
|
143
|
for my $dir ( @{$offs->[$y][$x]} ) {
|
|
|
82
|
|
|
|
|
197
|
|
|
887
|
8
|
100
|
|
|
|
22
|
last if $dir > 3; # Inspect one end only
|
|
888
|
4
|
|
|
|
|
7
|
my $semi_bad = 0;
|
|
889
|
4
|
0
|
33
|
|
|
17
|
next unless $ok or $smpl->[$dir] or 2 >= $cnt->[$y][$x] and $RAYS->[$dir][0] eq '°' and ++$semi_bad; # ° = not confirmed Fork
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
890
|
4
|
|
|
|
|
11
|
my $x1 = $x + $dx[$dir];
|
|
891
|
4
|
|
|
|
|
6
|
my $y1 = $y + $dy[$dir];
|
|
892
|
4
|
|
|
|
|
10
|
my $dir1 = ($dir+4)%8;
|
|
893
|
4
|
0
|
33
|
|
|
12
|
next unless $Simple[$y1][$x1] or $simpleray[$y1][$x1][$dir1]
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
894
|
|
|
|
|
|
|
or 2 >= $cnt->[$y1][$x1] and $rays[$y1][$x1][$dir1][0] eq '°' and ++$semi_bad < 2;
|
|
895
|
|
|
|
|
|
|
# warn "Candidate for °-ray at x=$x y=$y dir=$dir\n" if $semi_bad;
|
|
896
|
|
|
|
|
|
|
# doubleray, curve, notch, serif, (B)tail, 1Spur, [M]Fork, Enforced, Sharp, m-joint, Near-corner, Corner-curve, bend-sharp,
|
|
897
|
|
|
|
|
|
|
# Tail, A/arrow, Rhombus-force
|
|
898
|
|
|
|
|
|
|
# Omit: [Zh/K-]fake-curve, Ignore, rhombus, i, fork4, elses-ray, 3fork3, 2fork3, 4fork, xFork, Dense, x-arrow, \W-junk
|
|
899
|
4
|
50
|
|
|
|
26
|
next unless 2 - $semi_bad == grep /^[dcnstB1FMESmNCbTAaR]/, $RAYS->[$dir][0], $rays[$y1][$x1][$dir1][0];
|
|
900
|
4
|
|
|
|
|
16
|
add_edge([$x, $y, $dir, $x1, $y1], \@edge, \@cntedge, \@lastedge); # Good only for 1-edge pixels
|
|
901
|
|
|
|
|
|
|
}
|
|
902
|
|
|
|
|
|
|
}
|
|
903
|
|
|
|
|
|
|
}
|
|
904
|
2
|
|
|
|
|
5
|
my %candidates_way_out;
|
|
905
|
2
|
|
|
|
|
5
|
for my $y (1..$height) { # Identify singletons with a valid way out (one d in a group of d,e,f,K)
|
|
906
|
10
|
|
|
|
|
21
|
for my $x ( 1..$width ) {
|
|
907
|
82
|
100
|
50
|
|
|
245
|
next unless $Simple[$y][$x] and ($cntedge[$y][$x] || 0) <= 1; # If already have two edges, do not try to find complicated...
|
|
|
|
|
100
|
|
|
|
|
|
908
|
|
|
|
|
|
|
# die "x=$x, y=$y, Simple=$Simple[$y][$x], rays=<@{$rays[$y][$x]}>" unless defined $cnt->[$y][$x];
|
|
909
|
4
|
50
|
|
|
|
13
|
next if $cnt->[$y][$x] > 6; # Give up if too many neighbors
|
|
910
|
4
|
|
|
|
|
7
|
my @Neighbors = @{$offs->[$y][$x]}; # Deep copy
|
|
|
4
|
|
|
|
|
12
|
|
|
911
|
4
|
|
|
|
|
14
|
push @Neighbors, shift @Neighbors while $Neighbors[-1] == ($Neighbors[0] + 7)%8; # Rotate to start of a run
|
|
912
|
4
|
|
|
|
|
10
|
my(@res, $bad, @good, @maybe, %forced, $L, @Zh);
|
|
913
|
4
|
|
|
|
|
8
|
my $RAYS = $rays[$y][$x];
|
|
914
|
|
|
|
|
|
|
# Before, we assumed that at most one edge is present
|
|
915
|
4
|
50
|
|
|
|
13
|
$L = $lastedge[$y][$x] if $cntedge[$y][$x]; # Connect only as a curve continuation, and only if it continues back
|
|
916
|
4
|
|
|
|
|
10
|
for my $d ( 0..$#Neighbors ) {
|
|
917
|
4
|
|
|
|
|
8
|
my $dir = $Neighbors[$d];
|
|
918
|
4
|
50
|
50
|
|
|
57
|
(!$bad and ( @Zh == 1 and @good == 1 and push @res, [$Zh[0]]
|
|
919
|
|
|
|
|
|
|
or @good == 1 and push @res, [$good[0]]
|
|
920
|
|
|
|
|
|
|
or @maybe == 1 and push @res, [$maybe[0], 1])), # Finish previous group
|
|
921
|
|
|
|
|
|
|
$bad=0, @good = @maybe = @Zh = () # Start processing a new group
|
|
922
|
|
|
|
|
|
|
if $dir != ($Neighbors[$d-1] + 1)%8; # if $dir is after a gap
|
|
923
|
4
|
50
|
|
|
|
19
|
$bad++, next unless $RAYS->[$dir][0] =~ /^[dKZfIeiFMxR]/; # doubleray,[Zh/K-]fake-curve,Ignore,elses-ray,ignore,[M]Fork,x-arrow,Rhombus-force
|
|
924
|
4
|
|
33
|
|
|
19
|
my $sharp_angle = (defined $L and abs(4 - abs($L-$dir)) >= 2);
|
|
925
|
4
|
50
|
33
|
|
|
22
|
push(@good, $dir), next
|
|
926
|
|
|
|
|
|
|
if $RAYS->[$dir][0] =~ /^[dR]/ and not $sharp_angle; # Pick up doubleray, Rhombus-force
|
|
927
|
4
|
0
|
33
|
|
|
22
|
$forced{$dir}++, push(@maybe, $dir), next
|
|
|
|
|
33
|
|
|
|
|
|
928
|
|
|
|
|
|
|
if $RAYS->[$dir][0] =~ /^[FM]/ and defined $L and not $sharp_angle; # [M]Fork; pairs of Fc should end here...
|
|
929
|
4
|
50
|
|
|
|
14
|
$forced{$dir}++, push(@Zh, $dir), next if $RAYS->[$dir][0] =~ /^Z/; # special-case Zh-joint
|
|
930
|
4
|
|
|
|
|
8
|
my $x1 = $x + $dx[$dir];
|
|
931
|
4
|
|
|
|
|
38
|
my $y1 = $y + $dy[$dir];
|
|
932
|
4
|
50
|
|
|
|
13
|
my $rays1 = $rays[$y1][$x1] or next;
|
|
933
|
4
|
50
|
|
|
|
13
|
my $R = $rays1->[($dir+4)%8] or next;
|
|
934
|
4
|
50
|
33
|
|
|
16
|
$forced{$dir}++, push(@good, $dir), next if $RAYS->[$dir][0] =~ /^e/ and $R->[0] =~ /^d/; # reversed doublerays
|
|
935
|
4
|
50
|
33
|
|
|
18
|
$forced{$dir}++, push(@maybe, $dir), next if $RAYS->[$dir][0] =~ /^[fK]/ and $R->[0] =~ /^d/; # reversed doublerays
|
|
936
|
|
|
|
|
|
|
}
|
|
937
|
4
|
50
|
33
|
|
|
31
|
!$bad and ( @Zh == 1 and @good == 1 and push @res, [$Zh[0]]
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
938
|
|
|
|
|
|
|
or @good == 1 and push @res, [$good[0]]
|
|
939
|
|
|
|
|
|
|
or @maybe == 1 and push @res, [$maybe[0], 1]);
|
|
940
|
|
|
|
|
|
|
# warn "c=$c, x=$x, y=$y ==> ways out @res (out of @Neighbors): ", join '|', map $RAYS->[$_][0], @Neighbors;
|
|
941
|
4
|
50
|
|
|
|
11
|
next if @res > 2; # Do not get too carried away...
|
|
942
|
4
|
50
|
|
|
|
13
|
if ($cntedge[$y][$x]) { # Connect only as a curve continuation, and only if it continues back
|
|
943
|
4
|
|
|
|
|
7
|
@res = grep 1 >= abs(4 - abs($L-$_->[0])), @res; # i.e., almost opposite
|
|
944
|
|
|
|
|
|
|
# Maybe if two are left, chose by good vs maybe?
|
|
945
|
4
|
|
|
|
|
8
|
if (0 and @res > 1) {{ # does not actually change anything...
|
|
946
|
|
|
|
|
|
|
my @r = grep !$_->[1], @res or last;
|
|
947
|
|
|
|
|
|
|
@r < @res or last;
|
|
948
|
|
|
|
|
|
|
@res = @r;
|
|
949
|
|
|
|
|
|
|
}}
|
|
950
|
|
|
|
|
|
|
# warn "filtered: (@res)\n";
|
|
951
|
|
|
|
|
|
|
}
|
|
952
|
4
|
50
|
|
|
|
43
|
$candidates_way_out{$y,$x} = {map {+( $_->[0] => [$forced{$_->[0]}, @$_] )} @res} if @res;
|
|
|
0
|
|
|
|
|
0
|
|
|
953
|
|
|
|
|
|
|
}
|
|
954
|
|
|
|
|
|
|
}
|
|
955
|
2
|
|
|
|
|
6
|
for my $y (1..$height) { # Finish identifying singletons with a valid way out (one d in a group of d,e,f,K)
|
|
956
|
10
|
|
|
|
|
21
|
for my $x ( 1..$width ) {
|
|
957
|
82
|
50
|
|
|
|
226
|
next unless my $cand = $candidates_way_out{$y,$x};
|
|
958
|
0
|
|
|
|
|
0
|
my @ways = values %$cand;
|
|
959
|
0
|
|
|
|
|
0
|
my @res;
|
|
960
|
|
|
|
|
|
|
# Before, we assumed that at most one edge is present
|
|
961
|
|
|
|
|
|
|
# my $L = $cntedge[$y][$x] and $lastedge[$y][$x]; # Connect only as a curve continuation, and only if it continues back
|
|
962
|
0
|
0
|
|
|
|
0
|
if ($cntedge[$y][$x]) { # Connect only as a curve continuation, and only if it continues back
|
|
963
|
|
|
|
|
|
|
# Do not connect to something which is not a simple edge — or at least doubleray or curve!
|
|
964
|
0
|
|
|
|
|
0
|
for my $d (@ways) {
|
|
965
|
0
|
|
|
|
|
0
|
my $dir = $d->[1];
|
|
966
|
0
|
0
|
|
|
|
0
|
push(@res, $dir), next if $d->[0]; # forced
|
|
967
|
0
|
|
|
|
|
0
|
my $x1 = $x + $dx[$dir];
|
|
968
|
0
|
|
|
|
|
0
|
my $y1 = $y + $dy[$dir];
|
|
969
|
|
|
|
|
|
|
my $good = ($edge[$y1][$x1][$dir] or $rays[$y1][$x1][$dir][0] =~ /^[dcR]/ # doubleray curve Rhombus-force
|
|
970
|
0
|
|
0
|
|
|
0
|
or $candidates_way_out{$y1,$x1}{$dir});
|
|
971
|
0
|
0
|
|
|
|
0
|
unless ($good) {
|
|
972
|
0
|
|
|
|
|
0
|
my $x2 = $x + 2*$dx[$dir];
|
|
973
|
0
|
|
|
|
|
0
|
my $y2 = $y + 2*$dy[$dir];
|
|
974
|
0
|
|
|
|
|
0
|
my $dir2 = ($dir+4)%8;
|
|
975
|
0
|
|
|
|
|
0
|
$good = $candidates_way_out{$y2,$x2}{$dir2};
|
|
976
|
|
|
|
|
|
|
}
|
|
977
|
0
|
0
|
|
|
|
0
|
push @res, $dir if $good;
|
|
978
|
|
|
|
|
|
|
}
|
|
979
|
0
|
0
|
|
|
|
0
|
last if @res > 1;
|
|
980
|
|
|
|
|
|
|
} else {
|
|
981
|
0
|
|
|
|
|
0
|
@res = map $_->[1], @ways;
|
|
982
|
|
|
|
|
|
|
}
|
|
983
|
|
|
|
|
|
|
# warn "filtered:: (@res)\n";
|
|
984
|
0
|
|
|
|
|
0
|
push @update, [$x,$y,@res];
|
|
985
|
|
|
|
|
|
|
}
|
|
986
|
|
|
|
|
|
|
}
|
|
987
|
2
|
|
|
|
|
5
|
my %updated;
|
|
988
|
2
|
|
|
|
|
8
|
while (my $u = shift @update) {
|
|
989
|
0
|
|
|
|
|
0
|
my($x, $y, @res) = @$u;
|
|
990
|
0
|
|
|
|
|
0
|
for my $dir (@res) {
|
|
991
|
0
|
|
|
|
|
0
|
my $x1 = $x + $dx[$dir];
|
|
992
|
0
|
|
|
|
|
0
|
my $y1 = $y + $dy[$dir];
|
|
993
|
0
|
|
|
|
|
0
|
my $dir1 = ($dir+4)%8;
|
|
994
|
0
|
0
|
0
|
|
|
0
|
next if $updated{$x,$y,$dir}++ or $updated{$x1,$y1,$dir1}++;
|
|
995
|
0
|
|
|
|
|
0
|
add_edge([$x, $y, $dir, $x1, $y1], \@edge, \@cntedge, \@lastedge);
|
|
996
|
|
|
|
|
|
|
#warn "Update: $x,$y --> $x1,$y1\n";
|
|
997
|
|
|
|
|
|
|
}
|
|
998
|
|
|
|
|
|
|
}
|
|
999
|
2
|
|
|
|
|
6
|
for my $e (@$longedges) { # If a prefered way is found elsewhere, replace longedge by the prefered way
|
|
1000
|
0
|
0
|
0
|
|
|
0
|
next if not ref $e and $e eq 'erased';
|
|
1001
|
0
|
|
|
|
|
0
|
my($x, $y, $x1,$y1, $offset, $dir, $rot) = @$e;
|
|
1002
|
0
|
|
|
|
|
0
|
my $dir0 = ($dir+$rot)%8;
|
|
1003
|
0
|
|
|
|
|
0
|
my @atBEG = grep $edge[$y][$x][$_], $dir, $dir0;
|
|
1004
|
0
|
|
|
|
|
0
|
my @atEND = grep $edge[$y1][$x1][($_+4)%8], $dir, $dir0;
|
|
1005
|
0
|
0
|
0
|
|
|
0
|
next unless @atBEG or @atEND;
|
|
1006
|
0
|
|
|
|
|
0
|
my @add; # Had a longedge since couldn’t choose 1 of 2 ways around a rhombus; looks like something made a preference…
|
|
1007
|
0
|
0
|
0
|
|
|
0
|
unless (@atBEG and @atEND) { # If have a joiner on both sides, may just drop the longedge altogether
|
|
1008
|
0
|
|
|
|
|
0
|
my @have = (@atBEG, @atEND); # actually, one of them
|
|
1009
|
0
|
0
|
|
|
|
0
|
next if @have == 2; # XXX It is not clear what to add, so do not drop! ???
|
|
1010
|
0
|
|
0
|
|
|
0
|
my $DIR = ($dir + (($have[0] == $dir) && $rot))%8; # Add $dir+$rot on the OTHER side.
|
|
1011
|
0
|
|
|
|
|
0
|
my($dx,$dy) = ($dx[$DIR],$dy[$DIR]);
|
|
1012
|
0
|
0
|
|
|
|
0
|
if (@atEND) {
|
|
1013
|
0
|
|
|
|
|
0
|
@add = [$x, $y, $DIR, $x + $dx, $y + $dy];
|
|
1014
|
|
|
|
|
|
|
} else {
|
|
1015
|
0
|
|
|
|
|
0
|
@add = [$x1, $y1, ($DIR+4)%8, $x1 - $dx, $y1 - $dy];
|
|
1016
|
|
|
|
|
|
|
}
|
|
1017
|
|
|
|
|
|
|
}
|
|
1018
|
0
|
|
|
|
|
0
|
add_edge($_, \@edge, \@cntedge, \@lastedge) for @add;
|
|
1019
|
0
|
|
|
|
|
0
|
clear_longedge([$x, $y, $x1, $y1, $offset], $longedges, $seenlong, $midLong, $inLong);
|
|
1020
|
|
|
|
|
|
|
}
|
|
1021
|
2
|
50
|
33
|
|
|
18
|
die '$ER corrupted' if $ER and (@$ER != 1 or $ER->[0] ne '');
|
|
|
|
|
33
|
|
|
|
|
|
1022
|
2
|
|
|
|
|
43
|
[\@edge, \@cntedge, \@lastedge, \@rays, $longedges, $seenlong, $midLong, $inLong, \@Simple];
|
|
1023
|
|
|
|
|
|
|
}
|
|
1024
|
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
sub find_blobs ($$$$$$;$$) {
|
|
1026
|
4
|
|
|
4
|
0
|
16
|
my($blob, $width, $height, $pixels, $cntedge, $offs, $lastedge, $skip, $c) = (shift, shift, shift, shift, shift, shift, shift, shift, 0);
|
|
1027
|
4
|
|
|
|
|
13
|
$blob->[0] = [];
|
|
1028
|
4
|
|
|
|
|
12
|
for my $y (1..$height) {
|
|
1029
|
20
|
|
|
|
|
49
|
$blob->[$y] = [];
|
|
1030
|
20
|
|
|
|
|
41
|
for my $x ( 1..$width ) {
|
|
1031
|
164
|
100
|
|
|
|
373
|
next unless $pixels->[$y][$x];
|
|
1032
|
12
|
50
|
|
|
|
30
|
$blob->[$y][$x] = 1, $c++ unless $cntedge->[$y][$x];
|
|
1033
|
|
|
|
|
|
|
}
|
|
1034
|
|
|
|
|
|
|
}
|
|
1035
|
4
|
|
|
|
|
11
|
push @$blob, [];
|
|
1036
|
4
|
|
|
|
|
8
|
my @doblob;
|
|
1037
|
4
|
100
|
|
|
|
11
|
if ($lastedge) { # Add "better consider the same as blob" non-blob pixels
|
|
1038
|
2
|
|
|
|
|
5
|
for my $y (1..$height) {
|
|
1039
|
10
|
|
|
|
|
18
|
for my $x ( 1..$width ) {
|
|
1040
|
82
|
100
|
100
|
|
|
404
|
next if $blob->[$y][$x] or ($cntedge->[$y][$x] || 0) != 1;
|
|
|
|
|
66
|
|
|
|
|
|
1041
|
4
|
|
|
|
|
11
|
my $D = ($lastedge->[$y][$x] + 4)%8;
|
|
1042
|
4
|
50
|
|
|
|
17
|
next unless $blob->[$y + $dy[$D]][$x + $dx[$D]];
|
|
1043
|
0
|
|
|
|
|
0
|
my($C, $CC);
|
|
1044
|
0
|
0
|
|
|
|
0
|
for my $rot ( 1, -1, ($D % 2 ? (): (-2,2)) ) { # 22b6 ⊶
|
|
1045
|
0
|
0
|
|
|
|
0
|
$CC++, last if $blob->[$y + $dy[($D+$rot)%8]][$x + $dx[($D+$rot)%8]];
|
|
1046
|
|
|
|
|
|
|
}
|
|
1047
|
0
|
|
|
|
|
0
|
for my $dir ( @{$offs->[$y][$x]} ) {
|
|
|
0
|
|
|
|
|
0
|
|
|
1048
|
0
|
0
|
0
|
|
|
0
|
$C++, last if $dir != $D and $blob->[$y + $dy[$dir]][$x + $dx[$dir]];
|
|
1049
|
|
|
|
|
|
|
}
|
|
1050
|
0
|
0
|
0
|
|
|
0
|
push(@doblob, [$y,$x]), $c++, ($CC or $marked++) if $C and not $skip->{$y,$x};
|
|
|
|
|
0
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
}
|
|
1052
|
|
|
|
|
|
|
}
|
|
1053
|
|
|
|
|
|
|
}
|
|
1054
|
4
|
|
|
|
|
9
|
$blob->[$_->[0]][$_->[1]]++ for @doblob;
|
|
1055
|
4
|
|
|
|
|
12
|
for my $y (1..$height) { # Replace 1 by 1 + count of neighbor blobs
|
|
1056
|
20
|
50
|
|
|
|
45
|
next unless $blob->[$y];
|
|
1057
|
20
|
|
|
|
|
36
|
for my $x ( 1..$width ) {
|
|
1058
|
164
|
50
|
|
|
|
376
|
next unless $blob->[$y][$x];
|
|
1059
|
0
|
|
|
|
|
0
|
for my $dir ( @{$offs->[$y][$x]} ) {
|
|
|
0
|
|
|
|
|
0
|
|
|
1060
|
0
|
0
|
|
|
|
0
|
$blob->[$y][$x]++ if $blob->[$y + $dy[$dir]][$x + $dx[$dir]];
|
|
1061
|
|
|
|
|
|
|
}
|
|
1062
|
|
|
|
|
|
|
}
|
|
1063
|
|
|
|
|
|
|
}
|
|
1064
|
4
|
|
|
|
|
13
|
$c;
|
|
1065
|
|
|
|
|
|
|
}
|
|
1066
|
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
sub nnn_do_Simple_and_edges ($$$$$$$) {
|
|
1068
|
2
|
|
|
2
|
0
|
28
|
my($width, $height, $offs, $pixels, $edge, $cntedge,,$lastedge)
|
|
1069
|
|
|
|
|
|
|
= (shift, shift, shift, shift, shift, shift, shift);
|
|
1070
|
2
|
|
|
|
|
7
|
my($do_more, @blob, @clearEdge, %suspectShaft, %skipExtraBlob) = 1;
|
|
1071
|
2
|
|
|
|
|
9
|
my $blobs = find_blobs(\@blob, $width, $height, $pixels, $cntedge, $offs);
|
|
1072
|
2
|
|
33
|
|
|
8
|
while ($blobs and $do_more) { # clear edges with two “noisy” surroundings
|
|
1073
|
0
|
|
|
|
|
0
|
for my $y (0..$#$edge) {
|
|
1074
|
0
|
0
|
|
|
|
0
|
next unless $edge->[$y];
|
|
1075
|
0
|
|
|
|
|
0
|
for my $x ( 0..$#{ $edge->[$y] } ) {
|
|
|
0
|
|
|
|
|
0
|
|
|
1076
|
0
|
0
|
|
|
|
0
|
next unless $edge->[$y][$x];
|
|
1077
|
0
|
|
|
|
|
0
|
for my $dir ( 0..3 ) { # Do only once per edge
|
|
1078
|
0
|
0
|
|
|
|
0
|
next unless $edge->[$y][$x][$dir ];
|
|
1079
|
0
|
|
|
|
|
0
|
my $x1 = $x + (my $dx = $dx[$dir]);
|
|
1080
|
0
|
|
|
|
|
0
|
my $y1 = $y + (my $dy = $dy[$dir]);
|
|
1081
|
0
|
0
|
|
|
|
0
|
if ($dir % 2) {
|
|
1082
|
0
|
|
|
|
|
0
|
my(@CC, $CC, $clear);
|
|
1083
|
0
|
|
|
|
|
0
|
for my $rot ( -1, 1 ) { # Three big, bad blobs on the same side of an edge
|
|
1084
|
0
|
|
|
|
|
0
|
my($dx1, $dy1) = ($rot*$dy, -$rot*$dx);
|
|
1085
|
0
|
0
|
|
|
|
0
|
my($dx2, $dy2, $c, $C) = (($dy==$dy1 ? (0, $dy1) : ($dx1, 0)), 0, 0); # dot product with $dxy
|
|
1086
|
|
|
|
|
|
|
# Go in the natural order of 3 neighbors (projection on $dxy):
|
|
1087
|
0
|
0
|
|
|
|
0
|
my @DD = (($dy==$dy1 ? ([$y, $x+$dx1], [$y+$dy1,$x]) : ([$y+$dy1,$x], [$y, $x+$dx1])), [$y1+$dy2,$x1+$dx2]);
|
|
1088
|
0
|
|
|
|
|
0
|
for my $DD (0..2) {
|
|
1089
|
0
|
|
|
|
|
0
|
my $D = $DD[$DD];
|
|
1090
|
0
|
0
|
0
|
|
|
0
|
$CC[$DD]++, $C++ if ($blob[$D->[0]][$D->[1]] || 0) >= 3 - ($DD==1); # More forgiving for middle; 0909
|
|
1091
|
0
|
0
|
|
|
|
0
|
$c++ if $pixels->[$D->[0]][$D->[1]];
|
|
1092
|
|
|
|
|
|
|
}
|
|
1093
|
0
|
0
|
0
|
|
|
0
|
$clear++ and last if $c == 3 and $C >= 2;
|
|
|
|
|
0
|
|
|
|
|
|
1094
|
0
|
|
|
|
|
0
|
$CC += $C
|
|
1095
|
|
|
|
|
|
|
} # This gives reasonable (?) results
|
|
1096
|
|
|
|
|
|
|
# warn "$c: diag blob? x,y=$x,$y,$dir $clear CC=$CC <@CC>" if $CC >= 2;
|
|
1097
|
0
|
0
|
0
|
|
|
0
|
push @clearEdge, [$x, $y, $dir, $x+$dx, $y+$dy] and last if $clear or $CC >= 3 and $CC[1] and ($CC[0] or $CC[2]);
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
# if ($blob[$y][$x+$dx1] || 0) >= 3 and ($blob[$y+$dy1][$x] || 0) >= 3 and ($blob[$y1+$dy2][$x1+$dx2] || 0) >= 3;
|
|
1099
|
|
|
|
|
|
|
} else {
|
|
1100
|
0
|
|
|
|
|
0
|
my($tot, $done, %neigh, $lastN, $lastR, $lastDx, $lastDy) = (0);
|
|
1101
|
0
|
|
|
|
|
0
|
for my $rot ( -1, 1 ) { # Two big, bad blobs on the same side of an edge
|
|
1102
|
0
|
|
|
|
|
0
|
my($dx1, $dy1) = (-$rot*$dy, $rot*$dx);
|
|
1103
|
0
|
|
0
|
|
|
0
|
$neigh{$rot} = [($blob[$y+$dy1][$x+$dx1] || 0) >= 3, ($blob[$y1+$dy1][$x1+$dx1] || 0) >= 3];
|
|
|
|
|
0
|
|
|
|
|
|
1104
|
0
|
|
0
|
|
|
0
|
$neigh{$rot}[$_] and ++$tot and ($lastN, $lastR, $lastDx, $lastDy) = ($_, $rot, $dx1, $dy1) for 0, 1;
|
|
|
|
|
0
|
|
|
|
|
|
1105
|
0
|
0
|
0
|
|
|
0
|
++$done and push @clearEdge, [$x, $y, $dir, $x+$dx, $y+$dy] and last if $neigh{$rot}[0] and $neigh{$rot}[1];
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
}
|
|
1107
|
0
|
0
|
0
|
|
|
0
|
if (!$done and $cntedge->[$y][$x] == 1 and $cntedge->[$y1][$x1] == 1) { # Detect bold arrow tips/barbs
|
|
|
|
|
0
|
|
|
|
|
|
1108
|
0
|
0
|
0
|
|
|
0
|
++$done and push @clearEdge, [$x, $y, $dir, $x+$dx, $y+$dy] if grep 2 == $neigh{1}[$_] + $neigh{-1}[$_], 0, 1;
|
|
1109
|
0
|
0
|
0
|
|
|
0
|
if (!$done and $tot == 1) { # fake serifs near blobs
|
|
1110
|
0
|
|
|
|
|
0
|
my($X, $Y, $D) = ($x, $y);
|
|
1111
|
0
|
0
|
|
|
|
0
|
if ($lastN) {
|
|
1112
|
0
|
|
|
|
|
0
|
($x, $y, $D) = ($x1, $y1, $dir);
|
|
1113
|
|
|
|
|
|
|
} else {
|
|
1114
|
0
|
|
|
|
|
0
|
($dx, $dy, $D, $lastR) = (-$dx, -$dy, ($dir+4)%8, -$lastR);
|
|
1115
|
|
|
|
|
|
|
}
|
|
1116
|
0
|
0
|
0
|
|
|
0
|
if (($blob[$y+$dy][$x+$dx] || 0) >= 3 and ($blob[$y+2*$lastDy][$x+2*$lastDx] || 0) >= 3
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
and ($blob[$y+$dy+$lastDy][$x+$dx+$lastDx] || 0) >= 3 and not $blob[$y+$dy-$lastDy][$x+$dx-$lastDx]) {
|
|
1118
|
0
|
|
|
|
|
0
|
push @clearEdge, [$X, $Y, $dir, $x1, $y1];
|
|
1119
|
|
|
|
|
|
|
# warn sprintf "barb: $X, $Y, $dir, $x1, $y1 (%d %d %d) $lastN $lastR", $y+2*$lastDy,$x+2*$lastDx,($D+3*$lastR)%8;
|
|
1120
|
0
|
|
|
|
|
0
|
$suspectShaft{$y+2*$lastDy,$x+2*$lastDx,($D+3*$lastR)%8}++;
|
|
1121
|
|
|
|
|
|
|
}
|
|
1122
|
|
|
|
|
|
|
}
|
|
1123
|
|
|
|
|
|
|
}
|
|
1124
|
|
|
|
|
|
|
}
|
|
1125
|
|
|
|
|
|
|
}
|
|
1126
|
|
|
|
|
|
|
}
|
|
1127
|
|
|
|
|
|
|
}
|
|
1128
|
|
|
|
|
|
|
# warn "remove: @$_[0..2]" for @clearEdge;
|
|
1129
|
|
|
|
|
|
|
# @clearEdge = ();
|
|
1130
|
0
|
|
|
|
|
0
|
$do_more = @clearEdge;
|
|
1131
|
0
|
|
|
|
|
0
|
clear_edge($_,$edge,$cntedge,$lastedge) for @clearEdge; # [$x, $y, $dir, $x1, $y1]
|
|
1132
|
0
|
|
|
|
|
0
|
@clearEdge = ();
|
|
1133
|
|
|
|
|
|
|
# my $rep;
|
|
1134
|
0
|
|
|
|
|
0
|
for my $K (keys %suspectShaft) {
|
|
1135
|
0
|
0
|
|
|
|
0
|
last if $suspectShaft{$K} != 2;
|
|
1136
|
0
|
|
|
|
|
0
|
my($y, $x, $dir) = split /$;/o, $K;
|
|
1137
|
|
|
|
|
|
|
# warn "rep shafts" unless $rep++;
|
|
1138
|
|
|
|
|
|
|
# warn "shaft ($c) $x $y, $dir <$K> $suspectShaft{$K}";
|
|
1139
|
0
|
|
|
|
|
0
|
my($x1, $y1) = ($x+$dx[$dir], $y+$dy[$dir]);
|
|
1140
|
0
|
0
|
0
|
|
|
0
|
last if ($cntedge->[$y1][$x1] || 0) != 1 or $lastedge->[$y1][$x1] != $dir;
|
|
|
|
|
0
|
|
|
|
|
|
1141
|
0
|
|
|
|
|
0
|
add_edge([$x, $y, $dir, $x1, $y1], $edge, $cntedge, $lastedge);
|
|
1142
|
0
|
|
|
|
|
0
|
$do_more++;
|
|
1143
|
0
|
|
|
|
|
0
|
$skipExtraBlob{$y,$x}++;
|
|
1144
|
|
|
|
|
|
|
}
|
|
1145
|
0
|
|
|
|
|
0
|
%suspectShaft = ();
|
|
1146
|
0
|
|
|
|
|
0
|
$blobs = find_blobs(\@blob, $width, $height, $pixels, $cntedge, $offs);
|
|
1147
|
|
|
|
|
|
|
# warn "blobs: $blobs ($do_more edges removed)";
|
|
1148
|
|
|
|
|
|
|
}
|
|
1149
|
2
|
|
|
|
|
15
|
[$edge, $cntedge, $lastedge, $blobs, \@blob, \%skipExtraBlob];
|
|
1150
|
|
|
|
|
|
|
}
|
|
1151
|
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
sub nnn0_do_Simple_and_edges ($$$$$$$$) {
|
|
1153
|
2
|
|
|
2
|
0
|
27
|
my($width, $height, $edge, $cntedge,,$lastedge, $rays, $inLong, $blob)
|
|
1154
|
|
|
|
|
|
|
= (shift, shift, shift, shift, shift, shift, shift, shift);
|
|
1155
|
2
|
|
|
|
|
5
|
my @edgeAdd;
|
|
1156
|
2
|
|
|
|
|
8
|
for my $y (1..$height) {
|
|
1157
|
10
|
|
|
|
|
21
|
for my $x ( 1..$width ) {
|
|
1158
|
82
|
100
|
100
|
|
|
345
|
next unless 1 == ($cntedge->[$y][$x] || 0) and not $inLong->{$x,$y};
|
|
|
|
|
66
|
|
|
|
|
|
1159
|
4
|
|
|
|
|
10
|
my $dir = $lastedge->[$y][$x];
|
|
1160
|
4
|
|
|
|
|
10
|
my $x1 = $x - (my $dx = $dx[$dir]);
|
|
1161
|
4
|
|
|
|
|
9
|
my $y1 = $y - (my $dy = $dy[$dir]);
|
|
1162
|
4
|
50
|
|
|
|
15
|
next if $inLong->{$x1,$y1};
|
|
1163
|
4
|
100
|
|
|
|
11
|
if ($dir < 4) { # symmetric operations
|
|
1164
|
2
|
50
|
50
|
|
|
13
|
push @edgeAdd, [$x1, $y1, $dir, $x, $y]
|
|
|
|
|
33
|
|
|
|
|
|
1165
|
|
|
|
|
|
|
if 1 == ($cntedge->[$y1][$x1] || 0) and $lastedge->[$y1][$x1] == ($dir+4)%8; # end-to-end edges
|
|
1166
|
|
|
|
|
|
|
}
|
|
1167
|
4
|
|
|
|
|
8
|
my $r;
|
|
1168
|
4
|
0
|
50
|
|
|
29
|
push @edgeAdd, [$x1, $y1, $dir, $x, $y] # Not good for 210a 2274 fffd
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
if 1 == ($blob->[$y1][$x1] || 0) # blob singleton
|
|
1170
|
|
|
|
|
|
|
or !($dir & 0x1) and 1 == ($cntedge->[$y1][$x1] || 0) # 04fa
|
|
1171
|
|
|
|
|
|
|
and 2 == abs(($lastedge->[$y1][$x1] - $dir)%8 - 4) # perpendicular
|
|
1172
|
|
|
|
|
|
|
and $rays->[$y][$x][$dir][0] =~ /^([tBdcN])/ # (B)tail doubleray curve Near-corner
|
|
1173
|
|
|
|
|
|
|
and ($1 ne 'c' or ($r = $rays->[$y][$x][$dir][1] # curve's curving (04fe)
|
|
1174
|
|
|
|
|
|
|
and not $rays->[$y][$x][($dir+4+$r)%8]));
|
|
1175
|
|
|
|
|
|
|
}
|
|
1176
|
|
|
|
|
|
|
}
|
|
1177
|
|
|
|
|
|
|
# warn("adding @$_"),
|
|
1178
|
2
|
|
|
|
|
4
|
add_edge($_, $edge, $cntedge, $lastedge) for @edgeAdd;
|
|
1179
|
2
|
|
|
|
|
11
|
[$edge, $cntedge, $lastedge];
|
|
1180
|
|
|
|
|
|
|
}
|
|
1181
|
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
sub calc_Blobby ($$$$$$) { # (Re-)Count neighbors in blobs
|
|
1183
|
10
|
|
|
10
|
0
|
29
|
my ($height, $width, $cntedge, $offs, $cntBlobby, $lastBlobby) = (shift, shift, shift, shift, shift, shift);
|
|
1184
|
10
|
|
|
|
|
29
|
@$cntBlobby = ();
|
|
1185
|
10
|
|
|
|
|
25
|
for my $y (1..$height) {
|
|
1186
|
50
|
|
|
|
|
94
|
for my $x ( 1..$width ) {
|
|
1187
|
410
|
|
|
|
|
657
|
my ($c,$l);
|
|
1188
|
410
|
|
|
|
|
654
|
for my $dir (@{$offs->[$y][$x]}) {
|
|
|
410
|
|
|
|
|
861
|
|
|
1189
|
40
|
|
|
|
|
114
|
my $x1 = $x + $dx[$dir];
|
|
1190
|
40
|
|
|
|
|
67
|
my $y1 = $y + $dy[$dir];
|
|
1191
|
40
|
50
|
|
|
|
119
|
$c++, $l=$dir unless $cntedge->[$y1][$x1];
|
|
1192
|
|
|
|
|
|
|
}
|
|
1193
|
410
|
|
|
|
|
825
|
$cntBlobby->[$y][$x] = $c;
|
|
1194
|
410
|
|
|
|
|
838
|
$lastBlobby->[$y][$x] = $l;
|
|
1195
|
|
|
|
|
|
|
}
|
|
1196
|
|
|
|
|
|
|
}
|
|
1197
|
|
|
|
|
|
|
}
|
|
1198
|
|
|
|
|
|
|
|
|
1199
|
|
|
|
|
|
|
sub nnn1_do_Simple_and_edges ($$$$$$$$$$$$$) {
|
|
1200
|
2
|
|
|
2
|
0
|
172
|
my($width, $height, $edge, $cntedge,,$lastedge, $rays, $inLong, $midLong, $seenlong, $longedges, $blob, $offs, $cnt)
|
|
1201
|
|
|
|
|
|
|
= (shift, shift, shift, shift, shift, shift, shift, shift, shift, shift, shift, shift, shift);
|
|
1202
|
2
|
|
|
|
|
5
|
my @edgeAdd;
|
|
1203
|
2
|
|
|
|
|
5
|
my(@questEdges,@dblCoordEdges,@cntBlobby,@lastBlobby,@outType,@outCont,@ignore,@toClear,%toClear2,@to4fork,@maybe3Fr);
|
|
1204
|
2
|
|
|
|
|
10
|
calc_Blobby($height, $width, $cntedge, $offs, \@cntBlobby, \@lastBlobby);
|
|
1205
|
2
|
|
|
|
|
7
|
for my $y (1..$height) { # Detect more rhombi (pairs 3↔P, 3↔I, 3→´)
|
|
1206
|
10
|
|
|
|
|
21
|
for my $x ( 1..$width ) {
|
|
1207
|
82
|
50
|
|
|
|
180
|
next unless my $RAYS = $rays->[$y][$x];
|
|
1208
|
82
|
100
|
|
|
|
202
|
next unless @$RAYS;
|
|
1209
|
6
|
|
|
|
|
24
|
for my $dir (@{$offs->[$y][$x]}) {
|
|
|
6
|
|
|
|
|
23
|
|
|
1210
|
8
|
50
|
|
|
|
27
|
if ($RAYS->[$dir][0] =~ /^3/) {
|
|
1211
|
0
|
|
|
|
|
0
|
my $d = ($dir + (my $rot = $RAYS->[$dir][1]))%8;
|
|
1212
|
0
|
|
|
|
|
0
|
my $x1 = $x + $dx[$dir] + $dx[$d];
|
|
1213
|
0
|
|
|
|
|
0
|
my $y1 = $y + $dy[$dir] + $dy[$d];
|
|
1214
|
|
|
|
|
|
|
# warn "$char: 3 vs ";
|
|
1215
|
|
|
|
|
|
|
# $marked++ if
|
|
1216
|
0
|
0
|
|
|
|
0
|
next unless $rays->[$y1][$x1][($d+4)%8][0] =~ /^([PI´Fr])/; # Probably-curve; Ignore(d); '´' does not actually appear Fork, rhombus
|
|
1217
|
0
|
|
|
|
|
0
|
my $Opp = $1;
|
|
1218
|
|
|
|
|
|
|
# $marked = ($1 eq "´"), next;
|
|
1219
|
0
|
0
|
|
|
|
0
|
if ($cnt->[$y1][$x1] > 4) { # XXXX tmp!!!!!!
|
|
1220
|
0
|
|
0
|
|
|
0
|
my $blobs = !$cntedge->[$y1][$x1] + ($cntBlobby[$y1][$x1] || 0); # may step outside???
|
|
1221
|
0
|
0
|
|
|
|
0
|
next unless $blobs < 2;
|
|
1222
|
|
|
|
|
|
|
# Detect ⧣ ⧤ ⧥ (also 㗬); Assume horizontal/vertical $dir
|
|
1223
|
0
|
|
|
|
|
0
|
my($D,$cntE) = (($dir - $RAYS->[$dir][1])%8);
|
|
1224
|
0
|
0
|
0
|
|
|
0
|
next unless grep !$cnt->[$y + $_*(2*$dy[$dir] - $dy[$d])][$x + $_*(2*$dx[$dir] - $dx[$d])], -1, 1, 2
|
|
|
|
|
0
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
or grep !$cnt->[$y1 + $_*(2*$dy[$dir] - $dy[$d])][$x1 + $_*(2*$dx[$dir] - $dx[$d])], -1, 1
|
|
1226
|
|
|
|
|
|
|
or grep($cntE += !!$rays->[$y + $_*(2*$dy[$dir] - $dy[$d])][$x + $_*(2*$dx[$dir] - $dx[$d])][$D], -1, 0, 1),
|
|
1227
|
|
|
|
|
|
|
grep($cntE += !!$rays->[$y1 + $_*(2*$dy[$dir] - $dy[$d])][$x1 + $_*(2*$dx[$dir] - $dx[$d])][$D], -1, 0),
|
|
1228
|
|
|
|
|
|
|
$cntE < 2; # 㩄, 㗬: 2
|
|
1229
|
|
|
|
|
|
|
# $marked++
|
|
1230
|
|
|
|
|
|
|
# , warn "edgeTarg=$cntedge->[$y1][$x1]; blobTarg=$cntBlobby[$y1][$x1] (last=$lastBlobby[$y1][$x1]); $x,$y,$dir"
|
|
1231
|
|
|
|
|
|
|
}
|
|
1232
|
0
|
|
|
|
|
0
|
my $converted;
|
|
1233
|
0
|
0
|
0
|
|
|
0
|
next if $Opp =~ /^[Fr]/ and $edge->[$y1][$x1][($d+4)%8]; # May improve??? XXXX 0468 Ѩ 114E ᅎ
|
|
1234
|
0
|
0
|
0
|
|
|
0
|
if ($Opp =~ /^[Fr]/ and $rays->[$y + $dy[$dir]][$x + $dx[$dir]][($dir + (2+($dir%2))*$rot)%8]
|
|
|
|
|
0
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
and $rays->[$y + $dy[$dir]][$x + $dx[$dir]][($dir + (2+($dir%2))*$rot)%8][0] =~ /^¢/ ) { # ₨ 㚌
|
|
1236
|
0
|
|
|
|
|
0
|
push @edgeAdd, [$x + $dx[$dir],$y + $dy[$dir],$d,$x1,$y1];
|
|
1237
|
|
|
|
|
|
|
next
|
|
1238
|
0
|
|
|
|
|
0
|
}
|
|
1239
|
0
|
0
|
0
|
|
|
0
|
if (($cntedge->[$y1][$x1] || 0) == 1 and ($cntedge->[$y][$x] || 0) == 1
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
and $edge->[$y1][$x1][$dir] and $edge->[$y][$x][($dir-$RAYS->[$dir][1]+4)%8]) { # tilde: ≁; enforce curve
|
|
1241
|
0
|
|
|
|
|
0
|
my $x2 = $x + $dx[$dir];
|
|
1242
|
0
|
|
|
|
|
0
|
my $y2 = $y + $dy[$dir];
|
|
1243
|
|
|
|
|
|
|
# push(@maybe3Fr, [$x,$y,$dir,$x1,$y1,$d]), next if $Opp =~ /^[Fr]/;
|
|
1244
|
0
|
|
|
|
|
0
|
push @edgeAdd, [$x,$y,$dir,$x2,$y2], [$x2,$y2,$d,$x1,$y1];
|
|
1245
|
|
|
|
|
|
|
# $marked++,
|
|
1246
|
0
|
|
|
|
|
0
|
$converted++, next;
|
|
1247
|
|
|
|
|
|
|
}
|
|
1248
|
|
|
|
|
|
|
# push(@maybe3Fr, [$x,$y,$dir,$x1,$y1,$d]), next if $Opp =~ /^[Fr]/;
|
|
1249
|
0
|
0
|
0
|
|
|
0
|
if ($rays->[$y1][$x1][($dir+4)%8][0] =~ /^F/ and $rays->[$y+$dy[$d]][$x+$dx[$d]][$dir][0] =~ /^d/) { # Fork doubleray
|
|
1250
|
|
|
|
|
|
|
# $marked++, next;
|
|
1251
|
0
|
|
|
|
|
0
|
push @ignore, $rays->[$y1][$x1][($dir+4)%8], $rays->[$y+$dy[$d]][$x+$dx[$d]][$dir];
|
|
1252
|
0
|
|
|
|
|
0
|
push @toClear, [$x1, $y1, ($dir+4)%8, $x+$dx[$d], $y+$dy[$d]];
|
|
1253
|
|
|
|
|
|
|
}
|
|
1254
|
|
|
|
|
|
|
# Detect when there are extra dd-edges to remove (near 3)
|
|
1255
|
0
|
|
|
|
|
0
|
for my $Side ([-1, qr/^(?:f|([cd]))/], [1, qr/^\?/, 1]) { # fake-curve '?' curve doubleray
|
|
1256
|
0
|
|
|
|
|
0
|
my $D = ($dir + $Side->[0]*$RAYS->[$dir][1])%8;
|
|
1257
|
0
|
|
|
|
|
0
|
my $x2 = $x + $dx[$D];
|
|
1258
|
0
|
|
|
|
|
0
|
my $y2 = $y + $dy[$D];
|
|
1259
|
0
|
|
|
|
|
0
|
my $rays2 = $rays->[$y2][$x2];
|
|
1260
|
0
|
0
|
0
|
|
|
0
|
next unless $rays2->[($D+4)%8] and $rays2->[($D+4)%8][0] =~ /^d/
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1261
|
|
|
|
|
|
|
and $RAYS->[$D] and $RAYS->[$D][0] =~ /^(?:(d)|\?)/; # doubleray '?'
|
|
1262
|
0
|
0
|
|
|
|
0
|
if ($1) {
|
|
1263
|
|
|
|
|
|
|
# warn "r2=$rays2->[$D][0]; side=$Side->[1]; allow2=$Side->[2]";
|
|
1264
|
0
|
0
|
0
|
|
|
0
|
next unless $rays2->[$D] and $rays2->[$D][0] =~ $Side->[1];
|
|
1265
|
0
|
0
|
|
|
|
0
|
if ($1) { # Only ੴ for the choice $d; for $D many: ⱔ, etc; not good for ㉽ 䉸 {
|
|
1266
|
|
|
|
|
|
|
# warn "N=$cntedge->[$y2][$x2]; s=$cntedge->[$y][$x]";
|
|
1267
|
0
|
0
|
0
|
|
|
0
|
next unless ($cntedge->[$y2][$x2] || 0) == 3 and ($cntedge->[$y][$x] || 0) == 2
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
and $edge->[$y2][$x2][($d-2*$RAYS->[$dir][1])%8] and grep $edge->[$y][$x][($_+4)%8], $d, $D;
|
|
1269
|
|
|
|
|
|
|
# $marked++;
|
|
1270
|
|
|
|
|
|
|
}
|
|
1271
|
0
|
0
|
|
|
|
0
|
} else { next unless not $rays2->[$D]
|
|
1272
|
|
|
|
|
|
|
# and ++$marked
|
|
1273
|
|
|
|
|
|
|
}
|
|
1274
|
|
|
|
|
|
|
# $marked = 1;
|
|
1275
|
0
|
|
0
|
|
|
0
|
my $cont = ($Side->[2] and $rays->[$y + 2*$dy[$D]][$x + 2*$dx[$D]][($D+4)%8]);
|
|
1276
|
0
|
0
|
|
|
|
0
|
push @ignore, $RAYS->[$D], $rays2->[($D+4)%8], ( $cont ? $cont : () );
|
|
1277
|
0
|
0
|
|
|
|
0
|
push @toClear, [$x, $y, $D, $x2, $y2] if $edge->[$y][$x][$D];
|
|
1278
|
|
|
|
|
|
|
}
|
|
1279
|
|
|
|
|
|
|
# 3↔P may be repeated opposite to 3↔P or 3↔I: see C481 쒁, F91B 亂. We remove extra edges for duplicates too.
|
|
1280
|
0
|
0
|
|
|
|
0
|
push @to4fork, [$x, $y, $dir, $x1, $y1, $RAYS->[$dir][1]] unless $converted;
|
|
1281
|
|
|
|
|
|
|
}
|
|
1282
|
|
|
|
|
|
|
}
|
|
1283
|
|
|
|
|
|
|
}
|
|
1284
|
|
|
|
|
|
|
}
|
|
1285
|
2
|
|
|
|
|
5
|
$_->[0] = 'ignore' for @ignore;
|
|
1286
|
2
|
|
|
|
|
6
|
for my $e (@toClear) {
|
|
1287
|
0
|
|
|
|
|
0
|
my($x, $y, $dir, $x1, $y1) = @$e;
|
|
1288
|
0
|
0
|
|
|
|
0
|
clear_edge($e,$edge,$cntedge,$lastedge) unless $toClear2{$x+$x1,$y+$y1}++; # [$x, $y, $dir, $x1, $y1]
|
|
1289
|
|
|
|
|
|
|
}
|
|
1290
|
2
|
|
|
|
|
4
|
for my $e (@to4fork) {
|
|
1291
|
0
|
|
|
|
|
0
|
my($x, $y, $dir, $x1, $y1, $rot) = @$e;
|
|
1292
|
0
|
0
|
|
|
|
0
|
next if $seenlong->{$x, $y, $x1, $y1};
|
|
1293
|
0
|
|
|
|
|
0
|
$rays->[$y][$x][$dir][0] = '4fork';
|
|
1294
|
0
|
|
|
|
|
0
|
my $D = ($dir + $rot + 4)%8;
|
|
1295
|
0
|
|
|
|
|
0
|
$rays->[$y1][$x1][$D][0] = 'xFork';
|
|
1296
|
0
|
|
|
|
|
0
|
add_longedge($e, $longedges, $seenlong, $midLong, $inLong);
|
|
1297
|
|
|
|
|
|
|
}
|
|
1298
|
2
|
|
|
|
|
6
|
add_edge($_, $edge, $cntedge, $lastedge) for @edgeAdd;
|
|
1299
|
2
|
|
|
|
|
11
|
calc_Blobby($height, $width, $cntedge, $offs, \@cntBlobby, \@lastBlobby); # In fact, this has no practical effect on the next block
|
|
1300
|
2
|
|
|
|
|
5
|
@edgeAdd = ();
|
|
1301
|
2
|
|
|
|
|
6
|
for my $y (1..$height) { # Upgrade suitable pairs ´´ to edges
|
|
1302
|
10
|
|
|
|
|
19
|
for my $x ( 1..$width ) {
|
|
1303
|
82
|
50
|
|
|
|
180
|
next unless my $RAYS = $rays->[$y][$x];
|
|
1304
|
82
|
100
|
|
|
|
189
|
next unless @$RAYS; # Contamination???
|
|
1305
|
6
|
|
|
|
|
10
|
for my $dir (grep { $RAYS->[$_][0] =~ /^[´]/ } @{$offs->[$y][$x]}) { # '´'
|
|
|
8
|
|
|
|
|
29
|
|
|
|
6
|
|
|
|
|
37
|
|
|
1306
|
0
|
0
|
|
|
|
0
|
next if $dir > 3; # symmetric
|
|
1307
|
0
|
|
|
|
|
0
|
my $x1 = $x + $dx[$dir];
|
|
1308
|
0
|
|
|
|
|
0
|
my $y1 = $y + $dy[$dir];
|
|
1309
|
0
|
|
|
|
|
0
|
my $dir1 = ($dir+4)%8;
|
|
1310
|
0
|
0
|
|
|
|
0
|
next unless $rays->[$y1][$x1][$dir1][0] =~ /^([´])/; # '´'
|
|
1311
|
0
|
0
|
|
|
|
0
|
next if $cnt->[$y][$x] + $cnt->[$y1][$x1] > 8; # 04FE Ӿ
|
|
1312
|
0
|
0
|
0
|
|
|
0
|
next if grep +($cntBlobby[$_->[1]][$_->[0]] || 0) > 3, [$x,$y], [$x1,$y1];
|
|
1313
|
0
|
|
|
|
|
0
|
my $cX = grep !$cntedge->[$_->[1]][$_->[0]], [$x,$y], [$x1,$y1];
|
|
1314
|
0
|
0
|
0
|
|
|
0
|
next if grep !$cntedge->[$_->[1]][$_->[0]] && ($cntBlobby[$_->[1]][$_->[0]] || 0) > 1 + ($cX == 2), [$x,$y], [$x1,$y1];
|
|
1315
|
0
|
|
0
|
|
|
0
|
my $cXX = grep !$cntedge->[$_->[1]][$_->[0]] && ($cntBlobby[$_->[1]][$_->[0]] || 0) > ($cX == 2), [$x,$y], [$x1,$y1];
|
|
1316
|
0
|
|
|
|
|
0
|
my $rot = $RAYS->[$dir][1];
|
|
1317
|
0
|
0
|
|
|
|
0
|
next unless $rays->[$y1][$x1][$dir1][1] == -$rot;
|
|
1318
|
0
|
|
|
|
|
0
|
my $d = ($dir+$rot)%8;
|
|
1319
|
0
|
|
|
|
|
0
|
my $in = $rays->[$y+$dy[$d]][$x+$dx[$d]];
|
|
1320
|
0
|
0
|
0
|
|
|
0
|
next if 2 == $cXX and not ($in and !$cntedge->[$y+$dy[$d]][$x+$dx[$d]] and $cnt->[$y+$dy[$d]][$x+$dx[$d]] < 6); # 0904 ऄ
|
|
|
|
|
0
|
|
|
|
|
|
1321
|
0
|
|
|
|
|
0
|
push @edgeAdd, [$x,$y,$dir,$x1,$y1];
|
|
1322
|
0
|
|
|
|
|
0
|
for my $opp (0, 1) {
|
|
1323
|
0
|
0
|
|
|
|
0
|
my($x,$y,$dir,$rot) = ($opp ? ($x1,$y1,$dir1,-$rot) : ($x,$y,$dir,$rot));
|
|
1324
|
0
|
|
|
|
|
0
|
my $out = $RAYS->[($dir-2*$rot)%8]; # Now: the next condition works for ≥1 end
|
|
1325
|
0
|
0
|
|
|
|
0
|
next if $cnt->[$y][$x] - !!$in - !!$out > 2; # Now can create the neighbor edges
|
|
1326
|
0
|
|
|
|
|
0
|
my $D = ($dir-$rot+4)%8;
|
|
1327
|
0
|
0
|
0
|
|
|
0
|
push @edgeAdd, [$x,$y,$D,$x+$dx[$D],$y+$dy[$D]] if $rays->[$y][$x][$D] and not $edge->[$y][$x][$D];
|
|
1328
|
|
|
|
|
|
|
}
|
|
1329
|
|
|
|
|
|
|
# $marked++;
|
|
1330
|
|
|
|
|
|
|
}}}
|
|
1331
|
2
|
|
|
|
|
5
|
add_edge($_, $edge, $cntedge, $lastedge) for @edgeAdd;
|
|
1332
|
2
|
|
|
|
|
8
|
calc_Blobby($height, $width, $cntedge, $offs, \@cntBlobby, \@lastBlobby); # In fact, this has no practical effect on the next block
|
|
1333
|
2
|
|
|
|
|
5
|
@edgeAdd = ();
|
|
1334
|
2
|
|
|
|
|
6
|
for my $qRound (0,1) {
|
|
1335
|
|
|
|
|
|
|
# warn($rays->[7][2][7] ? "### <$rays->[7][2][7]> [@{$rays->[7][2][7]}] " . (defined $rays->[7][2][7][0]?'d':'u'):"###### not yet");
|
|
1336
|
4
|
|
|
|
|
9
|
for my $y (1..$height) { # Upgrade suitable pairs ?c ?d ?° to edges
|
|
1337
|
20
|
|
|
|
|
36
|
for my $x ( 1..$width ) {
|
|
1338
|
164
|
50
|
|
|
|
363
|
next unless my $RAYS = $rays->[$y][$x];
|
|
1339
|
164
|
100
|
|
|
|
367
|
next unless @$RAYS; # Contamination???
|
|
1340
|
12
|
|
|
|
|
21
|
for my $dir (grep { $RAYS->[$_][0] =~ /^[?]/ } @{$offs->[$y][$x]}) {
|
|
|
16
|
|
|
|
|
46
|
|
|
|
12
|
|
|
|
|
27
|
|
|
1341
|
0
|
|
|
|
|
0
|
my $x1 = $x + $dx[$dir];
|
|
1342
|
0
|
|
|
|
|
0
|
my $y1 = $y + $dy[$dir];
|
|
1343
|
0
|
|
|
|
|
0
|
my $dir1 = ($dir+4)%8;
|
|
1344
|
0
|
0
|
0
|
|
|
0
|
next unless $rays->[$y1][$x1][$dir1][0] =~ /^([dc°])/ # 'doubleray', 'curve', disabled-Fork
|
|
1345
|
|
|
|
|
|
|
and not $edge->[$y][$x][$dir];
|
|
1346
|
|
|
|
|
|
|
# warn "0 $1 [$y][$x][$dir] [$y1][$x1][$dir1]\n";
|
|
1347
|
0
|
|
|
|
|
0
|
my($Opp, $inBlob) = "$1";
|
|
1348
|
|
|
|
|
|
|
# $marked++;
|
|
1349
|
|
|
|
|
|
|
CHECK_BLOB:
|
|
1350
|
0
|
|
|
|
|
0
|
for my $DD (1, -1) { # abort if near a blob
|
|
1351
|
0
|
0
|
|
|
|
0
|
my @Shear = ($dir % 2 ? (-1,1) : 0);
|
|
1352
|
0
|
|
|
|
|
0
|
for my $shear (@Shear) {
|
|
1353
|
0
|
|
|
|
|
0
|
my $D = (2+$shear)*$DD;
|
|
1354
|
0
|
|
|
|
|
0
|
my($dir2, $badD, $bad0, $blobby, $smallBlobby) = (($dir+$D)%8, 0, 0, 0, 0);
|
|
1355
|
0
|
|
|
|
|
0
|
for my $P ([$x,$y], [$x1,$y1]) {
|
|
1356
|
0
|
|
|
|
|
0
|
$bad0 += !$cntedge->[$P->[1]][$P->[0]];
|
|
1357
|
0
|
0
|
|
|
|
0
|
next unless $rays->[$P->[1]][$P->[0]][$dir2];
|
|
1358
|
0
|
|
|
|
|
0
|
my $x2 = $P->[0] + $dx[$dir2];
|
|
1359
|
0
|
|
|
|
|
0
|
my $y2 = $P->[1] + $dy[$dir2];
|
|
1360
|
|
|
|
|
|
|
# $badD++ if $cntmin[$y2][$x2] and $cntmin[$y2][$x2] > 5 and not $cntedge->[$y2][$x2]; # Less strict than for `Dense´
|
|
1361
|
|
|
|
|
|
|
# $badD += !!$blob[$y2][$x2]; # Less strict than for `Dense´
|
|
1362
|
|
|
|
|
|
|
# $bad0 += !!$blob[$P->[1]][$P->[0]];
|
|
1363
|
0
|
|
|
|
|
0
|
$badD += !$cntedge->[$y2][$x2];
|
|
1364
|
0
|
|
|
|
|
0
|
my $back = $rays->[$P->[1]][$P->[0]][($dir2+4)%8];
|
|
1365
|
0
|
0
|
0
|
|
|
0
|
$blobby++ if (!$cntedge->[$P->[1]][$P->[0]] or ($cnt->[$P->[1]][$P->[0]] + !$back) > 5) # इ
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
and (!$cntedge->[$y2][$x2] and (($cntBlobby[$y2][$x2]||0) > 2 # ध:4; ሯ:3; ऄ इ: 2
|
|
1367
|
|
|
|
|
|
|
or $cntBlobby[$y2][$x2] and $cnt->[$y2][$x2] > 5) # 㜰
|
|
1368
|
|
|
|
|
|
|
or ($cntBlobby[$y2][$x2]||0) > 2 and $cnt->[$y2][$x2] > 5); # ⓲
|
|
1369
|
0
|
0
|
0
|
|
|
0
|
$smallBlobby++ if !$cntedge->[$P->[1]][$P->[0]] and !$cntedge->[$y2][$x2] and $cnt->[$y2][$x2] < 5; # ᎍ
|
|
|
|
|
0
|
|
|
|
|
|
1370
|
|
|
|
|
|
|
}
|
|
1371
|
|
|
|
|
|
|
# warn "[$x, $y]->$dir: rot=$D: edge:$bad0, near:$badD, blobby:$blobby sm:$smallBlobby (",$cntedge->[$y][$x]||0, " ", $cntedge->[$y1][$x1]||0,")" if not $qRound and $Opp eq 'c';
|
|
1372
|
0
|
|
|
|
|
0
|
my $bad00 = ($bad0 >= 2 - !$shear); # be stricter on diagonal lines
|
|
1373
|
0
|
0
|
0
|
|
|
0
|
$inBlob++, last CHECK_BLOB if $bad00 and $badD > 1 # ᢜ
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
or $blobby or $smallBlobby and $badD >= 2;
|
|
1375
|
|
|
|
|
|
|
}
|
|
1376
|
|
|
|
|
|
|
}
|
|
1377
|
0
|
0
|
0
|
|
|
0
|
next if $inBlob and $qRound;
|
|
1378
|
|
|
|
|
|
|
# warn "1 $Opp [$y][$x][$dir] [$y1][$x1][$dir1] inblob=",!!$inBlob,"\n";
|
|
1379
|
0
|
0
|
0
|
|
|
0
|
next if grep +($rays->[$y][$x][($dir+$_)%8] and $rays->[$y][$x][($dir+$_)%8][0] =~ /^A/), 2, -2; # Arrow
|
|
1380
|
0
|
0
|
|
|
|
0
|
if ($qRound) {
|
|
1381
|
|
|
|
|
|
|
# warn "--> $Opp [$y][$x][$dir] [$y1][$x1][$dir1] [",join(',', map !!$questEdges[$y][$x][($dir+$_)%8], -1,1),"] [",join(',', map !!$questEdges[$y1][$x1][($dir1+$_)%8], -1,1),"]\n";
|
|
1382
|
|
|
|
|
|
|
next if grep +($questEdges[$y1][$x1][($dir1+$_)%8] or $questEdges[$y][$x][($dir+$_)%8]), 1, -1
|
|
1383
|
0
|
0
|
0
|
|
|
0
|
or $dblCoordEdges[$y+$y1][$x+$x1] > 1 or $midLong->{$x+$x1,$y+$y1};
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1384
|
|
|
|
|
|
|
# Check nearby double-edges
|
|
1385
|
0
|
|
|
|
|
0
|
my(@e2, $e2);
|
|
1386
|
0
|
|
|
|
|
0
|
for my $D (1,-1) {
|
|
1387
|
0
|
|
|
|
|
0
|
push @e2, scalar grep $midLong->{$x+$x1+$D*$dx[($dir+$_)%8],$y+$y1+$D*$dy[($dir+$_)%8]}, -1, 1;
|
|
1388
|
0
|
|
|
|
|
0
|
$e2 += $e2[-1];
|
|
1389
|
|
|
|
|
|
|
}
|
|
1390
|
|
|
|
|
|
|
# $marked++ if $e2;
|
|
1391
|
0
|
|
|
|
|
0
|
if (0 and $e2) { # DOES NOT APPEAR (at least with 2-2 type only)
|
|
1392
|
|
|
|
|
|
|
for my $Pt ([$x1,$y1,0],[$x,$y,1]) { # The far end
|
|
1393
|
|
|
|
|
|
|
next unless $e2[$Pt->[2]] and $cnt->[$Pt->[1]][$Pt->[0]] > 4;
|
|
1394
|
|
|
|
|
|
|
my $blobs = !$cntedge->[$Pt->[1]][$Pt->[0]] + ($cntBlobby[$Pt->[1]][$Pt->[0]] || 0); # may step outside???
|
|
1395
|
|
|
|
|
|
|
# $marked++ unless $blobs < 2;
|
|
1396
|
|
|
|
|
|
|
}
|
|
1397
|
|
|
|
|
|
|
}
|
|
1398
|
0
|
|
|
|
|
0
|
my $compete;
|
|
1399
|
0
|
|
|
|
|
0
|
for my $D (-1, 1) {
|
|
1400
|
0
|
0
|
|
|
|
0
|
next unless $outType[$y][$x][($dir+2*$D)%8];
|
|
1401
|
0
|
0
|
0
|
|
|
0
|
$compete++ unless $edge->[$y1][$x1][($dir+$D)%8] and not $outCont[$y][$x][($dir+2*$D)%8][1+$D];
|
|
1402
|
|
|
|
|
|
|
}
|
|
1403
|
0
|
0
|
|
|
|
0
|
next if $compete;
|
|
1404
|
|
|
|
|
|
|
# warn "d->[$y][$x][$dir]" and
|
|
1405
|
0
|
|
|
|
|
0
|
(debug and $rays->[$y1][$x1][$dir1][0] =~ s/^d/ⓓ/),
|
|
1406
|
|
|
|
|
|
|
# $marked++; # if $Opp eq 'd'; # For doubleray, too many (???) false positives now
|
|
1407
|
|
|
|
|
|
|
push @edgeAdd, [$x,$y,$dir,$x1,$y1]; # unless $Opp eq 'd'; # For doubleray, too many (???) false positives now
|
|
1408
|
|
|
|
|
|
|
} else {
|
|
1409
|
|
|
|
|
|
|
# warn "$Opp [$y][$x][$dir] [$y1][$x1][$dir1]\n";
|
|
1410
|
0
|
|
|
|
|
0
|
$questEdges[$y1][$x1][$dir1]++;
|
|
1411
|
0
|
|
|
|
|
0
|
$questEdges[$y][$x][$dir]++;
|
|
1412
|
0
|
|
|
|
|
0
|
$dblCoordEdges[$y+$y1][$x+$x1]++; # Mark (doubled) midpoint
|
|
1413
|
0
|
|
|
|
|
0
|
$outType[$y][$x][$dir] = $Opp;
|
|
1414
|
0
|
|
|
|
|
0
|
$outCont[$y][$x][$dir][1+$_] = $edge->[$y1][$x1][($dir-$_)%8] for 1, -1;
|
|
1415
|
|
|
|
|
|
|
}
|
|
1416
|
|
|
|
|
|
|
}
|
|
1417
|
|
|
|
|
|
|
}
|
|
1418
|
|
|
|
|
|
|
}
|
|
1419
|
|
|
|
|
|
|
}
|
|
1420
|
2
|
|
|
|
|
6
|
add_edge($_, $edge, $cntedge, $lastedge) for @edgeAdd;
|
|
1421
|
2
|
|
|
|
|
25
|
[$edge, $cntedge, $lastedge, $rays, $longedges, $seenlong, $midLong, $inLong];
|
|
1422
|
|
|
|
|
|
|
}
|
|
1423
|
|
|
|
|
|
|
|
|
1424
|
|
|
|
|
|
|
sub scan_degree_rays ($$$$$$$$$) {
|
|
1425
|
2
|
|
|
2
|
0
|
66
|
my($width, $height, $edge, $cntedge,,$lastedge, $rays, $midLong, $offs, $cnt)
|
|
1426
|
|
|
|
|
|
|
= (shift, shift, shift, shift, shift, shift, shift, shift, shift);
|
|
1427
|
2
|
|
|
|
|
6
|
my($cntBlobby, $lastBlobby) = ([], []);
|
|
1428
|
2
|
|
|
|
|
44
|
calc_Blobby($height, $width, $cntedge, $offs, $cntBlobby, $lastBlobby);
|
|
1429
|
2
|
|
|
|
|
5
|
my(@todoDegree,%candDegree);
|
|
1430
|
2
|
|
|
|
|
6
|
for my $y (1..$height) { # Detect candidates °c °f °d
|
|
1431
|
10
|
|
|
|
|
22
|
for my $x ( 1..$width ) {
|
|
1432
|
82
|
50
|
|
|
|
180
|
next unless my $RAYS = $rays->[$y][$x];
|
|
1433
|
82
|
100
|
|
|
|
202
|
next unless @$RAYS;
|
|
1434
|
6
|
|
|
|
|
10
|
for my $dir (@{$offs->[$y][$x]}) {
|
|
|
6
|
|
|
|
|
15
|
|
|
1435
|
8
|
50
|
|
|
|
40
|
if ($RAYS->[$dir][0] =~ /^°/) { # '°'
|
|
1436
|
0
|
|
|
|
|
0
|
my $x1 = $x + $dx[$dir];
|
|
1437
|
0
|
|
|
|
|
0
|
my $y1 = $y + $dy[$dir];
|
|
1438
|
0
|
|
|
|
|
0
|
my($dir1, @rot) = ($dir+4)%8;
|
|
1439
|
|
|
|
|
|
|
my $goodNearLong = sub ($$$$$$) { # returns false if the edge is a bad candidate
|
|
1440
|
0
|
|
|
0
|
|
0
|
my($x,$y,$dir,$rot,$x1,$y1)=(shift,shift,shift,shift,shift,shift);
|
|
1441
|
0
|
|
|
|
|
0
|
my $x2 = $x1 + $dx[($dir+$rot)%8];
|
|
1442
|
0
|
|
|
|
|
0
|
my $y2 = $y1 + $dy[($dir+$rot)%8];
|
|
1443
|
0
|
|
|
|
|
0
|
my $r = $rays->[$y2][$x2][($dir+4)%8];
|
|
1444
|
0
|
0
|
0
|
|
|
0
|
return 1 unless $r and $r->[0] =~ /^[4x2]/; # longEdge is going in the inspected direction
|
|
1445
|
0
|
0
|
|
|
|
0
|
return 1 unless grep $edge->[$y2][$x2][($dir+$rot*$_)%8], -1..2; # XXX Is this needed??? Good way out of long
|
|
1446
|
0
|
0
|
0
|
|
|
0
|
return 1 if 1 == ($cntedge->[$y1][$x1] || 0) and grep $edge->[$y1][$x1][($dir-$rot*$_)%8], 0, 1, 2; # Good way out
|
|
|
|
|
0
|
|
|
|
|
|
1447
|
0
|
|
|
|
|
0
|
return;
|
|
1448
|
0
|
|
|
|
|
0
|
};
|
|
1449
|
0
|
|
|
|
|
0
|
my(@cont, $Opp);
|
|
1450
|
|
|
|
|
|
|
my $goodCont = sub ($$$$$$) { # returns ROTATION if the edge has a good continuation, undef/empty otherwise
|
|
1451
|
0
|
|
|
0
|
|
0
|
my($x,$y,$dir,$x1,$y1,$dir1)=(shift,shift,shift,shift,shift,shift);
|
|
1452
|
|
|
|
|
|
|
return @rot && $rot[0] if
|
|
1453
|
|
|
|
|
|
|
@rot = grep $edge->[$y] [$x] [($dir1+$_)%8], 0,-1, 1 # has a way out (doing exactly 1 is worse)
|
|
1454
|
0
|
0
|
0
|
|
|
0
|
or @rot = grep $midLong->{2*$x+$dx[$dir1]+$dx[($dir1+$_)%8],2*$y+$dy[$dir1]+$dy[($dir1+$_)%8]}, -1, 1
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
# not beneficial on diagonal lines:
|
|
1456
|
|
|
|
|
|
|
or !($dir%2) and (2 == grep $edge->[$y][$x][($dir+$_)%8], -2, 2 # ends on a stroke ⇽ 🉡
|
|
1457
|
|
|
|
|
|
|
or !$cntedge->[$y][$x] and 2 == grep $edge->[2*$y-$y1][2*$x-$x1][($dir+$_)%8], -2, 2
|
|
1458
|
|
|
|
|
|
|
and !grep $edge->[2*$y-$y1][2*$x-$x1][($dir+$_)%8], 1, 0, -1
|
|
1459
|
|
|
|
|
|
|
and push @cont, [$x,$y,$dir1,2*$x-$x1,2*$y-$y1]); # a stroke at dist=1
|
|
1460
|
0
|
0
|
0
|
|
|
0
|
if (!$cntedge->[$y][$x] and 1 == ($cntBlobby->[$y][$x] || 0)) {{ # ⾘ XXXX but easier???
|
|
|
|
|
0
|
|
|
|
|
|
1461
|
0
|
|
|
|
|
0
|
my $D = $lastBlobby->[$y][$x];
|
|
|
0
|
|
|
|
|
0
|
|
|
1462
|
0
|
0
|
|
|
|
0
|
last unless @rot = (grep $D == ($dir1 + $_)%8, -1, 0, 1);
|
|
1463
|
0
|
|
|
|
|
0
|
my $x2 = $x + $dx[$D];
|
|
1464
|
0
|
|
|
|
|
0
|
my $y2 = $y + $dy[$D];
|
|
1465
|
|
|
|
|
|
|
# Not beneficial on 𐃶; CONT is not beneficial on ⪵. Do not CONT if $midLong???
|
|
1466
|
|
|
|
|
|
|
push @cont, [$x,$y,$D,$x2,$y2] and return $rot[0]
|
|
1467
|
0
|
0
|
0
|
|
|
0
|
if 1 == ($cntBlobby->[$y2][$x2] || 0) and (!$midLong->{$x+$x2,$y+$y2} # we will be “connected” to Pt2 anyway
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1468
|
|
|
|
|
|
|
or $cnt->[$y2][$x2] == 3); # Apparently, always true with midLong!
|
|
1469
|
|
|
|
|
|
|
}}
|
|
1470
|
0
|
|
|
|
|
0
|
return undef;
|
|
1471
|
0
|
|
|
|
|
0
|
};
|
|
1472
|
|
|
|
|
|
|
my $goodConts = sub () { # returns ROTATION if the edge has a good continuation, undef/empty otherwise
|
|
1473
|
0
|
|
|
0
|
|
0
|
my($x,$y,$dir,$x1,$y1,$dir1) = ($x,$y,$dir,$x1,$y1,$dir1);
|
|
1474
|
0
|
|
|
|
|
0
|
my(@out, $rot) = ($goodCont->($x,$y,$dir,$x1,$y1,$dir1), $goodCont->($x1,$y1,$dir1,$x,$y,$dir));
|
|
1475
|
0
|
0
|
0
|
|
|
0
|
return @out unless 1 == (($rot, my $junk) = grep defined, @out) and $rot; # 1 way out found, not straight
|
|
1476
|
|
|
|
|
|
|
# Now try to punch through at slope 2 or ½ at the other end.
|
|
1477
|
0
|
|
|
|
|
0
|
my($try) = grep !$out[$_], 0, 1; # Have exactly one defined; it is not 0
|
|
1478
|
0
|
0
|
|
|
|
0
|
($x,$y,$dir,$x1,$y1,$dir1) = ($x1,$y1,$dir1,$x,$y,$dir) unless $out[0];
|
|
1479
|
0
|
|
|
|
|
0
|
my $D = ($dir+$rot)%8;
|
|
1480
|
0
|
|
|
|
|
0
|
my $x2 = $x1 + $dx[$D];
|
|
1481
|
0
|
|
|
|
|
0
|
my $y2 = $y1 + $dy[$D];
|
|
1482
|
|
|
|
|
|
|
# warn "($x,$y,$dir,$x1,$y1,$dir1) $out[0],$out[1]: $x2,$y2,$D [$edge->[$y2][$x2][$dir],$edge->[$y2][$x2][($dir+4)%8]]";
|
|
1483
|
0
|
0
|
0
|
|
|
0
|
push @cont, [$x1,$y1,$D,$x2,$y2] and $out[$try] = $rot if ($edge->[$y2][$x2] and $edge->[$y2][$x2][$dir]
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1484
|
|
|
|
|
|
|
and not grep $edge->[$y2][$x2][($dir+$_)%8], 4); # Having 3,5 here is not beneficial.
|
|
1485
|
|
|
|
|
|
|
@out
|
|
1486
|
0
|
|
|
|
|
0
|
}; # Below: °e is not beneficial, °C does not appear
|
|
|
0
|
|
|
|
|
0
|
|
|
1487
|
|
|
|
|
|
|
#warn "$x,$y,$dir ($edge->[$y] [$x] [($dir+$_)%8], ";
|
|
1488
|
0
|
0
|
0
|
|
|
0
|
next unless not $edge->[$y][$x][$dir] and $rays->[$y1][$x1][$dir1][0] =~ /^([cdfF])/ # doubleray (fake-)curve Fork
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1489
|
|
|
|
|
|
|
# and not ((grep $edge->[$y] [$x] [($dir1+$_)%8], -1, 0, 1 # has a way out (doing exactly 1 is worse)
|
|
1490
|
|
|
|
|
|
|
# or grep $midLong{2*$x+$dx[$dir1]+$dx[($dir1+$_)%8],2*$y+$dy[$dir1]+$dy[($dir1+$_)%8]}, -1, 1)
|
|
1491
|
|
|
|
|
|
|
# and (grep $edge->[$y1][$x1][($dir+$_)%8], -1, 0, 1
|
|
1492
|
|
|
|
|
|
|
# or grep $midLong{2*$x1+$dx[$dir]+$dx[($dir+$_)%8],2*$y1+$dy[$dir]+$dy[($dir+$_)%8]}, -1, 1))
|
|
1493
|
|
|
|
|
|
|
# and 2 == grep defined, (@out = $goodConts->(\$cont,\$cont1))
|
|
1494
|
|
|
|
|
|
|
and $Opp = $1
|
|
1495
|
|
|
|
|
|
|
and not grep $edge->[$y] [$x] [($dir+$_)%8], -1, 1 # no nearby edges
|
|
1496
|
|
|
|
|
|
|
and not grep $edge->[$y1][$x1][($dir1+$_)%8], -1, 1 # XXX Չ ڼ
|
|
1497
|
|
|
|
|
|
|
# and (warn("10 $x,$y,$dir (2+!$cntedge->[$y1][$x1] > $cntBlobby->[$y][$x])"),1)
|
|
1498
|
|
|
|
|
|
|
and 2 + !$cntedge->[$y1][$x1] > ($cntBlobby->[$y][$x] || 0) # not near blobs (Not beneficial at all).
|
|
1499
|
|
|
|
|
|
|
# and (warn("20 $x,$y,$dir (2+!$cntedge->[$y][$x] > $cntBlobby->[$y1][$x1])"),1)
|
|
1500
|
|
|
|
|
|
|
and 2 + !$cntedge->[$y][$x] > ($cntBlobby->[$y1][$x1] || 0) # (not counting the other side of this edge!)
|
|
1501
|
|
|
|
|
|
|
# and (warn("30 $x,$y,$dir"),1)
|
|
1502
|
|
|
|
|
|
|
and not grep $rays->[$y][$x][($dir+$_)%8] && (($rays->[$y][$x][($dir+$_)%8][0] || '') =~ /^[4x2]/) # not near long edges
|
|
1503
|
|
|
|
|
|
|
&& !$goodNearLong->($x,$y,$dir,$_,$x1,$y1), -1, 1 # 4fork does not appear
|
|
1504
|
|
|
|
|
|
|
# and (warn("40 $x,$y,$dir"),1)
|
|
1505
|
|
|
|
|
|
|
and not grep $rays->[$y1][$x1][($dir1+$_)%8] && (($rays->[$y1][$x1][($dir1+$_)%8][0] || '') =~ /^[4x2]/)
|
|
1506
|
|
|
|
|
|
|
&& !$goodNearLong->($x1,$y1,$dir1,$_,$x,$y), -1, 1; # 2fork3 xFork (!3fork2!!!)
|
|
1507
|
|
|
|
|
|
|
# and (warn("50 $x,$y,$dir"),1);
|
|
1508
|
0
|
|
|
|
|
0
|
my @out = $goodConts->();
|
|
1509
|
0
|
|
|
|
|
0
|
push @todoDegree, [$x,$y,$dir,$x1,$y1,$dir1,$Opp,@out,@cont];
|
|
1510
|
0
|
|
|
|
|
0
|
$candDegree{$x,$y,$dir} = $candDegree{$x1,$y1,$dir1} = $Opp;
|
|
1511
|
|
|
|
|
|
|
}}}}
|
|
1512
|
|
|
|
|
|
|
# warn($edge->[8][7][5] ? "### <$edge->[8][7][5]>" : "###### not yet ($#todoDegree [@{$todoDegree[0]||[]}] [@{$todoDegree[1]||[]}] [@{$todoDegree[2]||[]}])");
|
|
1513
|
2
|
|
|
|
|
7
|
for my $cand (@todoDegree) { # °f-candidates are not good as continuations; some candidates which work as continuation would not be revived
|
|
1514
|
0
|
|
|
|
|
0
|
my($x,$y,$dir,$x1,$y1,$dir1,$Opp,$out,$out1,@cont,$c) = @$cand; # below: 3: U+10054 (OK); 2: 𝔉 (not OK)
|
|
1515
|
0
|
0
|
0
|
|
|
0
|
$out++ if not defined $out and $c = grep 'f' ne ($candDegree{$x,$y,($dir1+$_)%8} || 'f'), -1, 0, 1 and $c != 2;
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1516
|
0
|
0
|
0
|
|
|
0
|
$out1++ if not defined $out1 and $c = grep 'f' ne ($candDegree{$x1,$y1,($dir+$_)%8} || 'f'), -1, 0, 1 and $c != 2;
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1517
|
0
|
0
|
0
|
|
|
0
|
next unless defined $out and defined $out1;
|
|
1518
|
|
|
|
|
|
|
# $marked++;
|
|
1519
|
0
|
|
0
|
|
|
0
|
$edge->[$_->[1]][$_->[0]][$_->[2]] or add_edge($_, $edge, $cntedge, $lastedge) for [$x,$y,$dir,$x1,$y1], @cont;
|
|
1520
|
|
|
|
|
|
|
# warn "($x,$y,$dir,$x1,$y1,$dir1,$Opp)";
|
|
1521
|
|
|
|
|
|
|
}
|
|
1522
|
|
|
|
|
|
|
# warn($edge->[8][7][5] ? "### <$edge->[8][7][5]>" : "###### not yet ($#todoDegree [@{$todoDegree[0]||[]}] [@{$todoDegree[1]||[]}] [@{$todoDegree[2]||[]}])");
|
|
1523
|
2
|
|
|
|
|
22
|
[$edge, $cntedge, $lastedge];
|
|
1524
|
|
|
|
|
|
|
}
|
|
1525
|
|
|
|
|
|
|
|
|
1526
|
|
|
|
|
|
|
sub nnn3_do_Simple_and_edges ($$$$$$$$$$) {
|
|
1527
|
2
|
|
|
2
|
0
|
47
|
my($width, $height, $edge, $cntedge,,$lastedge, $longedges, $seenlong, $midLong, $inLong, $cnt)
|
|
1528
|
|
|
|
|
|
|
= (shift, shift, shift, shift, shift, shift, shift, shift, shift, shift);
|
|
1529
|
2
|
|
|
|
|
6
|
for my $e (@$longedges) { # De-longedge if there is a loners nearby. Probably, it would be better to do earlier;
|
|
1530
|
0
|
0
|
0
|
|
|
0
|
next if not ref $e and $e eq 'erased'; # however, this would break tuneups which historically came first.
|
|
1531
|
0
|
|
|
|
|
0
|
my($x, $y, $x1,$y1, $offset, $dir, $rot) = @$e;
|
|
1532
|
0
|
|
|
|
|
0
|
my $dir0 = ($dir+$rot)%8;
|
|
1533
|
0
|
|
|
|
|
0
|
my @atBEG = grep $edge->[$y][$x][$_], $dir, $dir0;
|
|
1534
|
0
|
|
|
|
|
0
|
my @atEND = grep $edge->[$y1][$x1][($_+4)%8], $dir, $dir0;
|
|
1535
|
|
|
|
|
|
|
# next unless @atBEG or @atEND;
|
|
1536
|
0
|
|
|
|
|
0
|
my @add; # Had a longedge since couldn’t choose 1 of 2 ways around a rhombus; looks like something made a preference…
|
|
1537
|
0
|
0
|
0
|
|
|
0
|
if (not (@atBEG or @atEND)) { # Check for loner singletons on one side
|
|
1538
|
0
|
|
|
|
|
0
|
my @DIR = grep 3 == $cnt->[$y+$dy[$_]][$x+$dx[$_]], $dir, $dir0;
|
|
1539
|
0
|
0
|
|
|
|
0
|
next unless 1 == @DIR;
|
|
1540
|
0
|
|
|
|
|
0
|
my($dx,$dy) = ($dx[$DIR[0]],$dy[$DIR[0]]);
|
|
1541
|
|
|
|
|
|
|
# $marked++;
|
|
1542
|
|
|
|
|
|
|
# next;
|
|
1543
|
0
|
|
|
|
|
0
|
@add = ([$x, $y, $DIR[0], $x + $dx, $y + $dy], [$x1, $y1, ($dir + $dir0 - $DIR[0] + 4)%8, $x + $dx, $y + $dy]);
|
|
1544
|
|
|
|
|
|
|
}
|
|
1545
|
0
|
|
|
|
|
0
|
add_edge($_, $edge, $cntedge, $lastedge) for @add;
|
|
1546
|
0
|
|
|
|
|
0
|
clear_longedge([$x, $y, $x1, $y1, $offset], $longedges, $seenlong, $midLong, $inLong);
|
|
1547
|
|
|
|
|
|
|
}
|
|
1548
|
|
|
|
|
|
|
# warn($edge->[8][7][5] ? "### <$edge->[8][7][5]>" : "###### not yet");
|
|
1549
|
2
|
|
|
|
|
11
|
[$edge, $cntedge, $lastedge, $longedges, $seenlong, $midLong, $inLong];
|
|
1550
|
|
|
|
|
|
|
}
|
|
1551
|
|
|
|
|
|
|
|
|
1552
|
|
|
|
|
|
|
sub nnn4_do_Simple_and_edges ($$$$$$$$) {
|
|
1553
|
2
|
|
|
2
|
0
|
96
|
my($width, $height, $edge, $cntedge,,$lastedge, $rays, $offs, $cnt)
|
|
1554
|
|
|
|
|
|
|
= (shift, shift, shift, shift, shift, shift, shift, shift);
|
|
1555
|
2
|
|
|
|
|
6
|
my(@edgeAdd, $tailEdge);
|
|
1556
|
2
|
|
|
|
|
9
|
for my $y (0..$#$edge) { # Force the edges near tips
|
|
1557
|
11
|
100
|
|
|
|
28
|
next unless $edge->[$y];
|
|
1558
|
6
|
|
|
|
|
11
|
for my $x ( 0..$#{ $edge->[$y] } ) {
|
|
|
6
|
|
|
|
|
14
|
|
|
1559
|
39
|
|
|
|
|
65
|
for my $dir ( 0..$#{ $edge->[$y][$x] } ) {
|
|
|
39
|
|
|
|
|
94
|
|
|
1560
|
40
|
50
|
66
|
|
|
123
|
next unless $edge->[$y][$x][$dir] and $rays->[$y][$x][$dir][0] eq 'Tail'; # don't include in the end/nextEdge, special-case later
|
|
1561
|
0
|
|
|
|
|
0
|
my $X = $x + $dx[$dir];
|
|
1562
|
0
|
|
|
|
|
0
|
my $Y = $y + $dy[$dir];
|
|
1563
|
0
|
|
|
|
|
0
|
my $DIR = ($dir+4)%8;
|
|
1564
|
0
|
0
|
|
|
|
0
|
next unless $rays->[$Y][$X][$DIR][0] eq 'MFork';
|
|
1565
|
|
|
|
|
|
|
# next if grep !$edge->[$y][$x][($dir+$_)%8], 3,5; # A branch of a fork may be non-recognized
|
|
1566
|
0
|
|
|
|
|
0
|
$tailEdge->{$x,$y} = [$x, $y, $dir, my $rot = $rays->[$Y][$X][$DIR][1]];
|
|
1567
|
|
|
|
|
|
|
# next unless $edge->[$y][$x] and $tailEdge->{$x,$y};
|
|
1568
|
|
|
|
|
|
|
# warn "tail @($x,$y)";
|
|
1569
|
|
|
|
|
|
|
# my $dir = $tailEdge->{$x,$y}[2];
|
|
1570
|
0
|
0
|
0
|
|
|
0
|
next unless $cnt->[$y][$x] == 3 and $cntedge->[$y][$x] < 3;
|
|
1571
|
|
|
|
|
|
|
# warn "tail \@($x,$y,$dir)";
|
|
1572
|
0
|
|
|
|
|
0
|
my @bends;
|
|
1573
|
0
|
|
|
|
|
0
|
for my $d (grep $_ != $dir, @{$offs->[$y][$x]}) {
|
|
|
0
|
|
|
|
|
0
|
|
|
1574
|
|
|
|
|
|
|
next if # $edge->[$y][$x][$d] or
|
|
1575
|
0
|
0
|
0
|
|
|
0
|
($cntedge->[$y+$dy[$d]][$x+$dx[$d]] || 0) != 1 + !!$edge->[$y][$x][$d];
|
|
1576
|
0
|
0
|
0
|
|
|
0
|
my ($l) = $edge->[$y][$x][$d] ? grep((($_-$d+4)%8 and $edge->[$y+$dy[$d]][$x+$dx[$d]][$_]), 0..7)
|
|
|
|
|
0
|
|
|
|
|
|
1577
|
|
|
|
|
|
|
: ($lastedge->[$y+$dy[$d]][$x+$dx[$d]] || 0);
|
|
1578
|
0
|
|
|
|
|
0
|
my $b = ($l - $d + 4)%8 - 4;
|
|
1579
|
|
|
|
|
|
|
# warn "tail \@($x,$y,$dir): $d, $b";
|
|
1580
|
0
|
0
|
|
|
|
0
|
next if 1 < abs $b;
|
|
1581
|
0
|
|
|
|
|
0
|
my $d0 = ($d - $dir)%8 - 4;
|
|
1582
|
0
|
0
|
0
|
|
|
0
|
next if $b and grep $edge->[$y+$dy[$d]+$dy[$l]][$x+$dx[$d]+$dx[$l]][($l + $_*$d0)%8], 1, 2; # ཹ ᰑ ᶒ 1D06C 𝁬 11184 𑆄; but: 㨓
|
|
1583
|
0
|
|
|
|
|
0
|
push @bends, [$d, $b, $d0*$b, $d0];
|
|
1584
|
|
|
|
|
|
|
# warn "bends: ($d0,$b) \@($x,$y,$dir)";
|
|
1585
|
|
|
|
|
|
|
}
|
|
1586
|
0
|
0
|
0
|
|
|
0
|
if ( @bends == 2 and 2 == grep $_->[3], @bends and 1 == (my @O = grep $_->[2] == 1, @bends)
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1587
|
|
|
|
|
|
|
and !grep $_->[2] == -1, @bends ) { # connect the two continuations; ¤ µ; not good: ᰑ ᶒ 㨓 11184 𑆄
|
|
1588
|
0
|
|
|
|
|
0
|
my $d = $O[0][0];
|
|
1589
|
0
|
|
|
|
|
0
|
my $D = ($d + $O[0][1] + 4)%8;
|
|
1590
|
0
|
|
|
|
|
0
|
push @edgeAdd, [$x+$dx[$d], $y+$dy[$d], $D, $x+$dx[$d]+$dx[$D], $y+$dy[$d]+$dy[$D]];
|
|
1591
|
|
|
|
|
|
|
# $marked++;
|
|
1592
|
|
|
|
|
|
|
} else { # extend extendable
|
|
1593
|
0
|
|
|
|
|
0
|
for my $B (@bends) {
|
|
1594
|
0
|
|
|
|
|
0
|
my $d = $B->[0];
|
|
1595
|
0
|
0
|
|
|
|
0
|
push @edgeAdd, [$x, $y, $d, $x+$dx[$d], $y+$dy[$d]] unless $edge->[$y][$x][$d];
|
|
1596
|
|
|
|
|
|
|
}
|
|
1597
|
|
|
|
|
|
|
}
|
|
1598
|
|
|
|
|
|
|
}
|
|
1599
|
|
|
|
|
|
|
}
|
|
1600
|
|
|
|
|
|
|
}
|
|
1601
|
2
|
|
|
|
|
5
|
add_edge($_, $edge, $cntedge, $lastedge) for @edgeAdd;
|
|
1602
|
|
|
|
|
|
|
# warn($edge->[8][7][5] ? "### <$edge->[8][7][5]>" : "###### not yet");
|
|
1603
|
2
|
|
|
|
|
15
|
[$edge, $cntedge, $lastedge, $tailEdge];
|
|
1604
|
|
|
|
|
|
|
}
|
|
1605
|
|
|
|
|
|
|
|
|
1606
|
|
|
|
|
|
|
sub nnn5_do_Simple_and_edges ($$$$$$$$$$) {
|
|
1607
|
2
|
|
|
2
|
0
|
62
|
my($width, $height, $edge, $cntedge,,$lastedge, $rays, $offs, $inLong, $cnt, $near)
|
|
1608
|
|
|
|
|
|
|
= ( shift, shift, shift, shift, shift, shift, shift, shift, shift, shift);
|
|
1609
|
|
|
|
|
|
|
# warn "... reached ($#$near)";
|
|
1610
|
2
|
|
|
|
|
8
|
my($cntBlobby, $lastBlobby, @edgeAdd) = ([], []);
|
|
1611
|
2
|
|
|
|
|
10
|
calc_Blobby($height, $width, $cntedge, $offs, $cntBlobby, $lastBlobby); # In fact, seems like may use the older version???
|
|
1612
|
2
|
|
|
|
|
6
|
for my $y (1..$height) { # Last round of: Identify singletons with a valid way out (one d in a group of d,e,f,K)
|
|
1613
|
10
|
|
|
|
|
22
|
for my $x ( 1..$width ) {
|
|
1614
|
|
|
|
|
|
|
next unless $cnt->[$y][$x] # If already have two edges, do not try to find complicated...
|
|
1615
|
82
|
100
|
50
|
|
|
270
|
and ($cntedge->[$y][$x] || 0) <= 1 and !$inLong->{$x,$y};
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
1616
|
|
|
|
|
|
|
# next if not $Simple[$y][$x] and $cntedge->[$y][$x];
|
|
1617
|
|
|
|
|
|
|
# warn "... reached ($x,$y) c=$cnt->[$y][$x] cB=", $cntBlobby->[$y][$x];
|
|
1618
|
4
|
50
|
50
|
|
|
22
|
next if $cnt->[$y][$x] + ($cntBlobby->[$y][$x] || 0) > 6; # Give up if too many neighbors (count bad neighbors as 2)
|
|
1619
|
|
|
|
|
|
|
# Before, we assumed that at most one edge is present
|
|
1620
|
4
|
50
|
|
|
|
13
|
my $L = $cntedge->[$y][$x] ? $lastedge->[$y][$x] : 100; # Connect only as a curve continuation, and only if it continues back
|
|
1621
|
4
|
50
|
|
|
|
23
|
next if 1 + ($L!=100) > (my @Neighbors = @{$offs->[$y][$x]});
|
|
|
4
|
|
|
|
|
19
|
|
|
1622
|
|
|
|
|
|
|
# next if grep $_ == ($L-1)%8, @Neighbors;
|
|
1623
|
0
|
|
|
|
|
0
|
@Neighbors = grep $rays->[$y][$x][$_][0] !~ /^([i¢₡])/, @Neighbors; # , warn "($x,$y,$L)" ignore, ¢urve, ₡urve
|
|
1624
|
0
|
|
|
|
|
0
|
push @Neighbors, shift @Neighbors while $Neighbors[-1] == ($Neighbors[0] + 7)%8; # Rotate to start of a run
|
|
1625
|
0
|
|
|
|
|
0
|
my $e = 0;
|
|
1626
|
0
|
|
0
|
|
|
0
|
$e++ while $e < $#Neighbors and $Neighbors[$e+1] == ($Neighbors[$e] + 1)%8;
|
|
1627
|
|
|
|
|
|
|
# warn "($x,$y) $e [@Neighbors] <$cntedge->[$y][$x]>";
|
|
1628
|
0
|
|
|
|
|
0
|
my $premark;
|
|
1629
|
|
|
|
|
|
|
# warn "... reached";
|
|
1630
|
0
|
0
|
|
|
|
0
|
if (!$cntedge->[$y][$x]) {
|
|
1631
|
|
|
|
|
|
|
# warn "... reached e=$e #N=$#Neighbors ($x,$y)";
|
|
1632
|
0
|
0
|
0
|
|
|
0
|
next unless $e == $#Neighbors and $e == 2 and not grep !$cntedge->[$y+$dy[$_]][$x+$dx[$_]], @Neighbors;
|
|
|
|
|
0
|
|
|
|
|
|
1633
|
|
|
|
|
|
|
# warn "... reached e=$e #N=$#Neighbors";
|
|
1634
|
|
|
|
|
|
|
} else {
|
|
1635
|
0
|
|
|
|
|
0
|
my $e1 = $e++;
|
|
1636
|
0
|
|
0
|
|
|
0
|
$e++ while $e < $#Neighbors and $Neighbors[$e+1] == ($Neighbors[$e] + 1)%8; # find second run
|
|
1637
|
0
|
0
|
|
|
|
0
|
next unless $e == $#Neighbors; # Now: have exactly 2 groups
|
|
1638
|
0
|
0
|
|
|
|
0
|
if (grep $_ == $L, @Neighbors[0..$e1]) {
|
|
1639
|
0
|
|
|
|
|
0
|
splice @Neighbors, 0, $e1 + 1;
|
|
1640
|
|
|
|
|
|
|
} else {
|
|
1641
|
0
|
|
|
|
|
0
|
splice @Neighbors, $e1 + 1, @Neighbors - $e1 - 1;
|
|
1642
|
|
|
|
|
|
|
} # Now only the non-entry group remains
|
|
1643
|
0
|
0
|
0
|
|
|
0
|
next if @Neighbors > 3
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1644
|
|
|
|
|
|
|
or grep !$cntedge->[$y+$dy[$_]][$x+$dx[$_]], @Neighbors # See ֍
|
|
1645
|
|
|
|
|
|
|
and (@Neighbors > 1 or grep $near->[$y+$dy[$Neighbors[0]]][$x+$dx[$Neighbors[0]]][($Neighbors[0]+$_)%8], 2,3,-2,-3);
|
|
1646
|
|
|
|
|
|
|
# $premark++ if @Neighbors == 1 and grep !$cntedge->[$y+$dy[$_]][$x+$dx[$_]], @Neighbors;
|
|
1647
|
|
|
|
|
|
|
}
|
|
1648
|
|
|
|
|
|
|
# warn "... reached";
|
|
1649
|
|
|
|
|
|
|
# $marked++ if grep $rays->[$y][$x][$_][0] =~ /^([i¢₡])/, @Neighbors; # , warn "($x,$y,$L)" ignore, ¢urve, ₡urve
|
|
1650
|
0
|
|
|
|
|
0
|
my @cont = grep $edge->[$y+$dy[$_]][$x+$dx[$_]][$_], @Neighbors;
|
|
1651
|
0
|
|
|
|
|
0
|
my $mid = $Neighbors[int(@Neighbors/2)];
|
|
1652
|
|
|
|
|
|
|
# warn(" ($x,$y,$L): <@cont> <@Neighbors>");
|
|
1653
|
0
|
0
|
|
|
|
0
|
if (@cont >= 2) { # Use only if 2-neighbors are in this 45° sector
|
|
|
|
0
|
|
|
|
|
|
|
1654
|
0
|
0
|
0
|
|
|
0
|
next if @cont > 2 or @Neighbors > 2 or grep $cnt->[$y+$dy[$_]][$x+$dx[$_]] > 4, @Neighbors
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1655
|
|
|
|
|
|
|
or $L < 8 and grep 1 < abs(($L-$_)%8 - 4), @cont;
|
|
1656
|
|
|
|
|
|
|
# next if !$cntedge->[$y][$x];
|
|
1657
|
|
|
|
|
|
|
} elsif (@cont) { # Use only if perp to a stroke, or continues incoming
|
|
1658
|
|
|
|
|
|
|
# warn "... reached";
|
|
1659
|
0
|
0
|
|
|
|
0
|
if (@Neighbors == 3) { # The only case compatible with no-incoming-edge
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1660
|
0
|
0
|
0
|
|
|
0
|
next if $mid%2 or grep !$edge->[$y+$dy[$mid]][$x+$dx[$mid]][($mid+$_)%8], 2, -2; # Now: we are next to a stroke
|
|
1661
|
0
|
0
|
0
|
|
|
0
|
next unless $mid == $cont[0] and ($L > 7 or 2 > abs(($L-$cont[0])%8 - 4)) or ($cont[0] + 4)%8 == $L;
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1662
|
|
|
|
|
|
|
} elsif (@Neighbors == 2) { # Use only if extends incoming, or is close, and incoming can't go straight
|
|
1663
|
0
|
|
|
|
|
0
|
my $ang = abs(($cont[0] - $L)%8 - 4);
|
|
1664
|
0
|
0
|
0
|
|
|
0
|
next if $ang and ($ang > 1 or $near->[$y][$x][($L+4)%8]); # “Being close” is not beneficial for ƈ ۜ
|
|
|
|
|
0
|
|
|
|
|
|
1665
|
|
|
|
|
|
|
# } elsif (@Neighbors == 1 and $mid%2) { # OK, just use @cont
|
|
1666
|
|
|
|
|
|
|
# next unless grep !$edge->[$y+$dy[$mid]][$x+$dx[$mid]][($mid+$_)%8], 2, -2; # ֍ but not ą
|
|
1667
|
|
|
|
|
|
|
} elsif (@Neighbors == 1) { # OK, just use @cont
|
|
1668
|
0
|
0
|
0
|
|
|
0
|
next if $L < 8 and 1 < abs(($L-$cont[0])%8 - 4);
|
|
1669
|
|
|
|
|
|
|
}
|
|
1670
|
|
|
|
|
|
|
} else {
|
|
1671
|
|
|
|
|
|
|
# warn "... reached";
|
|
1672
|
0
|
0
|
|
|
|
0
|
if (@Neighbors == 3) { # The only case compatible with no-incoming-edge
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1673
|
|
|
|
|
|
|
# warn "... reached";
|
|
1674
|
0
|
0
|
0
|
|
|
0
|
next if $mid%2 or grep !$edge->[$y+$dy[$mid]][$x+$dx[$mid]][($mid+$_)%8], 2, -2; # Now: we are next to a stroke
|
|
1675
|
0
|
0
|
|
|
|
0
|
@cont = ($L > 7 ? $mid : grep $_ == ($L+4)%8, @Neighbors);
|
|
1676
|
|
|
|
|
|
|
# next unless $mid == $cont[0] or ($cont[0] + 4)%8 == $L;
|
|
1677
|
|
|
|
|
|
|
} elsif (@Neighbors == 2) { # Use only if extends incoming
|
|
1678
|
|
|
|
|
|
|
# warn "... reached";
|
|
1679
|
0
|
|
|
|
|
0
|
@cont = grep $_ == ($L+4)%8, @Neighbors;
|
|
1680
|
|
|
|
|
|
|
# next unless ($cont[0] + 4)%8 == $L;
|
|
1681
|
|
|
|
|
|
|
} elsif (@Neighbors == 1) {
|
|
1682
|
|
|
|
|
|
|
# warn "... reached";
|
|
1683
|
0
|
0
|
|
|
|
0
|
if ($mid%2) { # ֍ but not ą; what about γ, צ???
|
|
1684
|
|
|
|
|
|
|
# warn "... reached";
|
|
1685
|
0
|
|
|
|
|
0
|
my @NN = grep $_ != ($mid+4)%8, @{$offs->[$y+$dy[$mid]][$x+$dx[$mid]]};
|
|
|
0
|
|
|
|
|
0
|
|
|
1686
|
0
|
|
|
|
|
0
|
my @ed = grep $near->[$y+$dy[$mid]][$x+$dx[$mid]][$_], map +($mid + $_)%8, 2, -2;
|
|
1687
|
0
|
0
|
|
|
|
0
|
next if grep !$edge->[$y+$dy[$mid]][$x+$dx[$mid]][$_], @ed; # The present perpendicular directions must be edges
|
|
1688
|
|
|
|
|
|
|
# $marked++ if @ed and ($L > 7 or abs(($L-$mid)%8 - 4) < 2);
|
|
1689
|
|
|
|
|
|
|
# warn "... reached ($#$near,$#{$near->[$y+$dy[$mid]]}) near0=[@$near] near1=[@{$near->[$y+$dy[$mid]]}], x=", $x+$dx[$mid], ", y=", $y+$dy[$mid];
|
|
1690
|
|
|
|
|
|
|
# warn "... reached NN=[@NN] mid=$mid ed=(@ed) L=$L CNT=$cnt->[$y][$x] near=[@{$near->[$y+$dy[$mid]][$x+$dx[$mid]]}]";
|
|
1691
|
0
|
0
|
0
|
|
|
0
|
next if grep abs(($_+4-$mid)%8 - 4) > 1, @NN
|
|
|
|
|
0
|
|
|
|
|
|
1692
|
|
|
|
|
|
|
and not (@ed and ($L > 7 or abs(($L-$mid)%8 - 4) < 2) and $cnt->[$y][$x] < 3);
|
|
1693
|
|
|
|
|
|
|
# warn "... reached";
|
|
1694
|
|
|
|
|
|
|
} else {
|
|
1695
|
|
|
|
|
|
|
# warn "... reached";
|
|
1696
|
|
|
|
|
|
|
# $marked++ if grep $edge->[$y+$dy[$mid]][$x+$dx[$mid]][($mid+$_)%8], 2, -2 # Allow one edge (Ԃ) if there is no neighbor in other direction
|
|
1697
|
|
|
|
|
|
|
# and not grep +($near->[$y+$dy[$mid]][$x+$dx[$mid]][($mid+$_)%8] and not $edge->[$y+$dy[$mid]][$x+$dx[$mid]][($mid+$_)%8]), 2, -2;
|
|
1698
|
0
|
0
|
0
|
|
|
0
|
next if grep !$edge->[$y+$dy[$mid]][$x+$dx[$mid]][($mid+$_)%8], 2, -2
|
|
|
|
|
0
|
|
|
|
|
|
1699
|
|
|
|
|
|
|
and not ( grep $edge->[$y+$dy[$mid]][$x+$dx[$mid]][($mid+$_)%8], 2, -2 # Allow one edge (Ԃ) if there is no neighbor in other direction
|
|
1700
|
|
|
|
|
|
|
and not grep +($near->[$y+$dy[$mid]][$x+$dx[$mid]][($mid+$_)%8]
|
|
1701
|
|
|
|
|
|
|
and not $edge->[$y+$dy[$mid]][$x+$dx[$mid]][($mid+$_)%8]), 2, -2 );
|
|
1702
|
|
|
|
|
|
|
}
|
|
1703
|
|
|
|
|
|
|
# warn "... reached";
|
|
1704
|
|
|
|
|
|
|
# next if ($mid%2) or grep !$edge->[$y+$dy[$mid]][$x+$dx[$mid]][($mid+$_)%8], 2, -2; # ֍ but not ą
|
|
1705
|
0
|
0
|
0
|
|
|
0
|
@cont = @Neighbors if $L < 8 and 2 > abs(($L-$Neighbors[0])%8 - 4);
|
|
1706
|
|
|
|
|
|
|
}
|
|
1707
|
|
|
|
|
|
|
}
|
|
1708
|
|
|
|
|
|
|
# $marked++ if $premark;
|
|
1709
|
|
|
|
|
|
|
# $marked++
|
|
1710
|
|
|
|
|
|
|
# , next
|
|
1711
|
|
|
|
|
|
|
# if @cont and not $Simple[$y][$x] and $cntedge->[$y][$x];
|
|
1712
|
|
|
|
|
|
|
# next;
|
|
1713
|
|
|
|
|
|
|
# warn(": ($x,$y,$_,$x+$dx[$_],$y+$dy[$_])"),
|
|
1714
|
0
|
|
|
|
|
0
|
push @edgeAdd, [$x,$y,$_,$x+$dx[$_],$y+$dy[$_]] for @cont;
|
|
1715
|
|
|
|
|
|
|
}
|
|
1716
|
|
|
|
|
|
|
}
|
|
1717
|
|
|
|
|
|
|
# warn($edge->[12][3][5] ? "### <$edge->[12][3][5]>" : "###### not yet"); # ($#todoDegree [@{$todoDegree[0]||[]}] [@{$todoDegree[1]||[]}] [@{$todoDegree[2]||[]}])
|
|
1718
|
|
|
|
|
|
|
# warn($edge->[8][7][5] ? "### <$edge->[8][7][5]>" : "###### not yet #=$#edgeAdd [@{$edgeAdd[0]||['N/A']}]");
|
|
1719
|
2
|
|
|
|
|
4
|
add_edge($_, $edge, $cntedge, $lastedge) for @edgeAdd;
|
|
1720
|
|
|
|
|
|
|
# warn($edge->[12][3][5] ? "### <$edge->[12][3][5]>" : "###### not yet"); # ($#todoDegree [@{$todoDegree[0]||[]}] [@{$todoDegree[1]||[]}] [@{$todoDegree[2]||[]}])
|
|
1721
|
|
|
|
|
|
|
# warn($edge->[8][7][5] ? "### <$edge->[8][7][5]>" : "###### not yet");
|
|
1722
|
2
|
|
|
|
|
20
|
[$edge, $cntedge, $lastedge];
|
|
1723
|
|
|
|
|
|
|
}
|
|
1724
|
|
|
|
|
|
|
|
|
1725
|
|
|
|
|
|
|
# How to recognize rasterization of 1px-wide line?
|
|
1726
|
|
|
|
|
|
|
# Going along a line, there are only two types of delta (neighbors, one
|
|
1727
|
|
|
|
|
|
|
# diagonal, one coordinate).
|
|
1728
|
|
|
|
|
|
|
# For slopes <= 1/2 diagonal delta cannot come in pairs; for slopes above 1/2
|
|
1729
|
|
|
|
|
|
|
# horizontal cannot come in pairs. Hence one gets stretches of one type
|
|
1730
|
|
|
|
|
|
|
# of delta, separated by single deltas of the other type.
|
|
1731
|
|
|
|
|
|
|
|
|
1732
|
|
|
|
|
|
|
# Be more specific: which stretches may appear? Use continuous fractions!
|
|
1733
|
|
|
|
|
|
|
|
|
1734
|
|
|
|
|
|
|
# May assume slope M >= 1, take intersection of the line with the vertical grid
|
|
1735
|
|
|
|
|
|
|
# line. Make a path between two copies of the line shifted +- 1/2
|
|
1736
|
|
|
|
|
|
|
# horizontally; color squares with centers inside the path.
|
|
1737
|
|
|
|
|
|
|
# Hence the diagonal-UR delta appears after a square with center (A + .5,B + .5)
|
|
1738
|
|
|
|
|
|
|
# if y-coordinate of the intersection with x = A+1 is between B+0.5 and B+1.5.
|
|
1739
|
|
|
|
|
|
|
|
|
1740
|
|
|
|
|
|
|
# Hence stretches are determined by closest integers to Mn + b, n in Z. Hence
|
|
1741
|
|
|
|
|
|
|
# they are related if M' = +-M + M0 with integer M0. Hence may reduce to
|
|
1742
|
|
|
|
|
|
|
# 0 < M <= 0.5. Hence stretches are of (max) 2 lengths (differing by 1);
|
|
1743
|
|
|
|
|
|
|
# one of the lengths appears single, the other comes in groups (2-stretches).
|
|
1744
|
|
|
|
|
|
|
|
|
1745
|
|
|
|
|
|
|
# Which 2-stretches may appear? Boundaries are determined by when the line
|
|
1746
|
|
|
|
|
|
|
# intersects y = n + 0.5 with n in Z. Now exchanging x and y and doing M=1/M
|
|
1747
|
|
|
|
|
|
|
# reduces to the previous step.
|
|
1748
|
|
|
|
|
|
|
|
|
1749
|
|
|
|
|
|
|
# How deep one may go on 24x24 grid? The shortest non-constant stretch is 1,2;
|
|
1750
|
|
|
|
|
|
|
# such 3-stretch gives the shortest 2-stretch 2,1,1; this gives the shortest
|
|
1751
|
|
|
|
|
|
|
# stretch 1,1,2,1,2,1,2 which is -/-/--/-/--/-/-- which is 19-long. Hence
|
|
1752
|
|
|
|
|
|
|
# 3-stretches may appear... On the other hand, it may be interpreted as a part
|
|
1753
|
|
|
|
|
|
|
# of 2,1 repeated indefinitely (prepend -); is avoided by prepending /...
|
|
1754
|
|
|
|
|
|
|
|
|
1755
|
|
|
|
|
|
|
# xxx
|
|
1756
|
|
|
|
|
|
|
# xx
|
|
1757
|
|
|
|
|
|
|
# xxx
|
|
1758
|
|
|
|
|
|
|
# xx
|
|
1759
|
|
|
|
|
|
|
# xxx
|
|
1760
|
|
|
|
|
|
|
# xx
|
|
1761
|
|
|
|
|
|
|
# xx
|
|
1762
|
|
|
|
|
|
|
# x
|
|
1763
|
|
|
|
|
|
|
|
|
1764
|
|
|
|
|
|
|
# This also can take into account that the line may be cut into interval
|
|
1765
|
|
|
|
|
|
|
# somewhere inside a stretch...
|
|
1766
|
|
|
|
|
|
|
|
|
1767
|
|
|
|
|
|
|
# These transformation may also define "the best" b in y=Mx+b. When we
|
|
1768
|
|
|
|
|
|
|
# reduce to 0 < M <= 0.5 with constant stretches (pattern ----/ repeated),
|
|
1769
|
|
|
|
|
|
|
# the best line passes through middles of /-deltas.
|
|
1770
|
|
|
|
|
|
|
|
|
1771
|
|
|
|
|
|
|
# On the next layer: if 2-stretches are constant (so stretches are n,m with
|
|
1772
|
|
|
|
|
|
|
# m single, and n coming in groups of N), the line passes through the middle
|
|
1773
|
|
|
|
|
|
|
# of m-stretches.
|
|
1774
|
|
|
|
|
|
|
|
|
1775
|
|
|
|
|
|
|
|
|
1776
|
|
|
|
|
|
|
# http://www.sourcecodebrowser.com/autotrace/0.31.1/pxl-outline_8h.html
|
|
1777
|
|
|
|
|
|
|
# http://tug.org/texinfohtml/fontu.html#Limn
|
|
1778
|
|
|
|
|
|
|
# http://stuff.mit.edu/afs/athena/astaff/project/tex/fontutil/fontutils-0.6/limn/fit.c
|
|
1779
|
|
|
|
|
|
|
|
|
1780
|
|
|
|
|
|
|
# ??? After we found "long strokes", remove them, but keep pixels which
|
|
1781
|
|
|
|
|
|
|
# on both vertical (or horizontal; or diag?) sides have "remaining pixels".
|
|
1782
|
|
|
|
|
|
|
# Try to find strokes in remaining+kept pixels... -- works for "#"
|
|
1783
|
|
|
|
|
|
|
|
|
1784
|
|
|
|
|
|
|
# ??? Try to find long vert/hor strokes by brute force. Exclude those who
|
|
1785
|
|
|
|
|
|
|
# have too many pixels on neighboring lines. -- works for "$".
|
|
1786
|
|
|
|
|
|
|
# Considering striked-snakes (such as $): k neighbors for 2k-1 is not "too many"
|
|
1787
|
|
|
|
|
|
|
|
|
1788
|
|
|
|
|
|
|
# Currency ¤ is tricky... -- too many "extendable" lines.
|
|
1789
|
|
|
|
|
|
|
|
|
1790
|
|
|
|
|
|
|
# Ec E 3/4 Note that ^ in 4 is genuine one, but v is fake...
|
|
1791
|
|
|
|
|
|
|
# > c
|
|
1792
|
|
|
|
|
|
|
# < /
|
|
1793
|
|
|
|
|
|
|
# xx
|
|
1794
|
|
|
|
|
|
|
# Ex x
|
|
1795
|
|
|
|
|
|
|
# x *
|
|
1796
|
|
|
|
|
|
|
# x CC
|
|
1797
|
|
|
|
|
|
|
# / x f
|
|
1798
|
|
|
|
|
|
|
# c Lxx
|
|
1799
|
|
|
|
|
|
|
# E v
|
|
1800
|
|
|
|
|
|
|
|
|
1801
|
|
|
|
|
|
|
# Input encodes a sequence of rectangles made of grid squares; rectangles share UR/LL corners:
|
|
1802
|
|
|
|
|
|
|
# □ is encoded as 2,4,4,3,1 (all positive)
|
|
1803
|
|
|
|
|
|
|
# □□□
|
|
1804
|
|
|
|
|
|
|
# □□□□
|
|
1805
|
|
|
|
|
|
|
# □□□□
|
|
1806
|
|
|
|
|
|
|
# □□
|
|
1807
|
|
|
|
|
|
|
# We want to find a line which rasterizes to these squares, i.e., intersect the (“red”) vertical disector of every square.
|
|
1808
|
|
|
|
|
|
|
#
|
|
1809
|
|
|
|
|
|
|
# It is the same as intersecting a (“green”) horizontal line of length=1 centered at the shared corners,
|
|
1810
|
|
|
|
|
|
|
# plus intersecting the red lines of the leftmost and the rightmost square. Suppose that non-on-edge rectanles
|
|
1811
|
|
|
|
|
|
|
# are only of two sizes, s and s+1. Swapping x and y axis, and subtracting y'=y-sx moves the green lines to a
|
|
1812
|
|
|
|
|
|
|
# collection of red lines of a new configuration of rectanges. This gives a step of recursion. Above configuration is moved to
|
|
1813
|
|
|
|
|
|
|
# □□
|
|
1814
|
|
|
|
|
|
|
# □
|
|
1815
|
|
|
|
|
|
|
# □
|
|
1816
|
|
|
|
|
|
|
# On this picture, the “old” red lines (“pink”, two at edges) become sloped lines with slope -s, with horizontal projection 1
|
|
1817
|
|
|
|
|
|
|
# (ending on horizontal grid lines, below the center of first square, and below the center of the last square.
|
|
1818
|
|
|
|
|
|
|
|
|
1819
|
|
|
|
|
|
|
# The right end of the left pink line is below the new-red line of the leftmost new square; hence it is below any fitting
|
|
1820
|
|
|
|
|
|
|
# line. One must only check that the left end of the left pink line is above the fitting line. If this left end is on level
|
|
1821
|
|
|
|
|
|
|
# (or above) the top of the left square, everything is OK.
|
|
1822
|
|
|
|
|
|
|
|
|
1823
|
|
|
|
|
|
|
# If it is on the level or below the bottom of the left square, then draw a new green line: horizontal line of lenght 1 going right
|
|
1824
|
|
|
|
|
|
|
# from the left end of the pink like. Obviously, the fitting line intersects the pink line iff it intersects the new green line.
|
|
1825
|
|
|
|
|
|
|
|
|
1826
|
|
|
|
|
|
|
# There are several cases when we may exclude the new green line being below the bottom of the left square:
|
|
1827
|
|
|
|
|
|
|
# • if all rectangles are actually squares, one could replace s by s+1 above, and have one rectangle instead; exclude this;
|
|
1828
|
|
|
|
|
|
|
# • if there is one rectangle longer than 2 (or two of length 2) the slope of the fitting line is < 1, so intersection with
|
|
1829
|
|
|
|
|
|
|
# such green line is impossible;
|
|
1830
|
|
|
|
|
|
|
# • If there is one rectangle of length 2, and the rest are squares, the green line may be 1 unit below the bottom (and it
|
|
1831
|
|
|
|
|
|
|
# is unique; this may be repeated on both ends).
|
|
1832
|
|
|
|
|
|
|
|
|
1833
|
|
|
|
|
|
|
# Only two cases remain: the rectangles consist of 1 square (total), and that with a pink line which may be either forgotten,
|
|
1834
|
|
|
|
|
|
|
# or replaced by an “additional” green line. (The additional green lines have no associated red lines, so on the NEXT step of induction
|
|
1835
|
|
|
|
|
|
|
# they would give no pink lines.)
|
|
1836
|
|
|
|
|
|
|
|
|
1837
|
|
|
|
|
|
|
# In the first (“trivial”) case, the preceding step is of two rectangles with no added green lines.
|
|
1838
|
|
|
|
|
|
|
|
|
1839
|
|
|
|
|
|
|
# So induction step: We start with n rectangles with k≤2 added green lines; coordinate change gives rectangles of total length
|
|
1840
|
|
|
|
|
|
|
# n-1+k with 2-k pink lines. A pink line is either forgotten, or impossible, or gives a unique solution, or is convertible to
|
|
1841
|
|
|
|
|
|
|
# a green line. So either we exclude a configuration, or find a unique solution or a trivial case, or get rectangles with n-1+k
|
|
1842
|
|
|
|
|
|
|
# squares and ≤2-k added green lines. The only cases when the number of squares did not decrease is:
|
|
1843
|
|
|
|
|
|
|
# All rectangles at start are squares except one of length 2; we had 2 added green lines.
|
|
1844
|
|
|
|
|
|
|
# But then on the next step we have no added green lines, so the next step is the trivial one.
|
|
1845
|
|
|
|
|
|
|
|
|
1846
|
|
|
|
|
|
|
# (To avoid the trivial step [which is tricky] we ensure that we call recursively, there are at least two squares.
|
|
1847
|
|
|
|
|
|
|
# This means at least two green intervals on the previous stage.)
|
|
1848
|
|
|
|
|
|
|
# Provided that this case is handled in the caller, the additional green lines appear when the length of the start/end rectangle
|
|
1849
|
|
|
|
|
|
|
# is s+1; if it is above s+1, this is an impossible situation.
|
|
1850
|
|
|
|
|
|
|
|
|
1851
|
|
|
|
|
|
|
# In particular, every case is reduced to a “unique solution” one, or the “trivial” one. The last one is equivalent to
|
|
1852
|
|
|
|
|
|
|
# having 3 equidistant paralle lines with an an interval [AB] on the middle one (the preimage [AB] of the last red line), and
|
|
1853
|
|
|
|
|
|
|
# opposite to each other rays XA' and YB' on the other two lines. The fitting line must intersect all 3 of them.
|
|
1854
|
|
|
|
|
|
|
# Oone of ray may be the whole line). It is easy to see that this is equivalent to the line intersecting intervals [XA], [AB]
|
|
1855
|
|
|
|
|
|
|
# and [BY]. If the quadrilateral XAYB is not convex, it may be decreased (so that X,A,B,Y are 3 vertices of a △, and a point
|
|
1856
|
|
|
|
|
|
|
# on a side. If it is convex, then intersecting [AB] is a corollary of other two. ???
|
|
1857
|
|
|
|
|
|
|
|
|
1858
|
|
|
|
|
|
|
# Possibly unknown squares: the 2nd and 3rd row “share” a x-coordinate; assume that intersecting a red line in any of them is OK.
|
|
1859
|
|
|
|
|
|
|
# □ This is equivalent to having the red line of double length, which is equivalent to
|
|
1860
|
|
|
|
|
|
|
# □□□ the green line of double length at this position (assuming it is not at edge).
|
|
1861
|
|
|
|
|
|
|
# □□□□ Hence this allows the induction step as well.
|
|
1862
|
|
|
|
|
|
|
# □□□□□ (Encode ignoring the extra square at second line, with second line marked as: $extended->{1}=1.)
|
|
1863
|
|
|
|
|
|
|
# □□
|
|
1864
|
|
|
|
|
|
|
|
|
1865
|
|
|
|
|
|
|
# Returns empty or a,b,db of the line y=ax+b-db which rasterizes to the rectangles of widths @$CC (connected UR ↔ LL corners);
|
|
1866
|
|
|
|
|
|
|
# The other two arguments as as above. LL corner of the leftmost of bottom squares is at 0,0.
|
|
1867
|
|
|
|
|
|
|
sub encodes_line ($;$$$); # Refused degenerated cases when there is a unique solution — with choices in rasterization
|
|
1868
|
|
|
|
|
|
|
sub encodes_line ($;$$$) { # %$extended should not have negative keys
|
|
1869
|
0
|
|
0
|
0
|
0
|
0
|
my ($CC, $green_at_left, $green_at_right, $extended) = (shift, shift, shift, shift||{}); # Every elt encodes delta between cells
|
|
1870
|
|
|
|
|
|
|
# A horrible mess of special-cases before we can recognize "runs", and flip axes...
|
|
1871
|
|
|
|
|
|
|
# warn "Got: [@$CC], $green_at_left, $green_at_right";
|
|
1872
|
|
|
|
|
|
|
# Banal case: one rectangle
|
|
1873
|
0
|
0
|
0
|
|
|
0
|
return 0, 1/2 if 1 >= @$CC and not ($green_at_left or $green_at_right); # No greens at all; one rectangle
|
|
|
|
|
0
|
|
|
|
|
|
1874
|
0
|
|
|
|
|
0
|
my(@jumps, $left_red, $right_red, %seen) = @$CC; # jumps between greens
|
|
1875
|
0
|
0
|
|
|
|
0
|
if ($green_at_left) { $left_red = $CC->[0] - 1 }
|
|
|
0
|
|
|
|
|
0
|
|
|
1876
|
0
|
|
|
|
|
0
|
else { shift @jumps }
|
|
1877
|
0
|
0
|
|
|
|
0
|
if ($green_at_right) { $right_red = $CC->[-1] - 1 }
|
|
|
0
|
|
|
|
|
0
|
|
|
1878
|
0
|
|
|
|
|
0
|
else { pop @jumps }
|
|
1879
|
|
|
|
|
|
|
# Need to exclude a trivial case
|
|
1880
|
0
|
0
|
|
|
|
0
|
unless (@jumps) { # Only one green
|
|
1881
|
0
|
0
|
0
|
|
|
0
|
if ($green_at_left or $green_at_right) { # Maximize some metric (∑distances to mid-red points)
|
|
1882
|
0
|
|
|
|
|
0
|
my $sl = 3/(4*$CC->[0] - 1); # ==> intersects right at height ≈¾.
|
|
1883
|
0
|
0
|
|
|
|
0
|
return ($green_at_left ? ($sl, $sl/4) : ($sl, 1/4 - $sl/2)) # Cut the green interval in the same proportion
|
|
1884
|
|
|
|
|
|
|
} else { # Two rectangles; likewise: if ratio of lengths is t≤1, use ¾(1+t²)/(1+t³)
|
|
1885
|
0
|
|
|
|
|
0
|
my $addHalf = 0;
|
|
1886
|
0
|
0
|
0
|
|
|
0
|
if ($extended->{0} and $CC->[0] < $CC->[1] - 1) {
|
|
|
|
0
|
0
|
|
|
|
|
|
1887
|
0
|
|
|
|
|
0
|
$CC = [$CC->[0] + 1, $CC->[1] - 1];
|
|
1888
|
|
|
|
|
|
|
} elsif ($extended->{0} and $CC->[0] == $CC->[1] - 1) {
|
|
1889
|
0
|
|
|
|
|
0
|
$addHalf = 1;
|
|
1890
|
|
|
|
|
|
|
}
|
|
1891
|
0
|
0
|
|
|
|
0
|
if ($CC->[0] + $addHalf == $CC->[1]) { # Go through the center of symmetry, with intersection of edge red lines as above:
|
|
|
|
0
|
|
|
|
|
|
|
1892
|
0
|
|
|
|
|
0
|
my $sl = 3/(4*$CC->[0] + 2*$addHalf - 2);
|
|
1893
|
0
|
|
|
|
|
0
|
return($sl, 1/4 - $sl/2);
|
|
1894
|
|
|
|
|
|
|
} elsif ($CC->[0] < $CC->[1]) { # One strategy is to continue periodically, then make a best fit; this joints
|
|
1895
|
|
|
|
|
|
|
# midpoints of rectangles. If differ by one, this breaks the green line 1:3 (with 1 on the side of longer rectangle).
|
|
1896
|
|
|
|
|
|
|
# On the other hand, to avoid close-to pathological rasterizations, we should divide the green line in the middle!
|
|
1897
|
|
|
|
|
|
|
# The best derasterization of 1+2 cuts the red lines at heights ¾,¼,¾ (the last ¾ makes it also good-in-L²-norm).
|
|
1898
|
|
|
|
|
|
|
# (It is also better since there are two ways to treat it: one can consider the main direction to be horizontal,
|
|
1899
|
|
|
|
|
|
|
# or to be diagonal. This “best” approximation is the same in these two approaches.)
|
|
1900
|
0
|
|
|
|
|
0
|
my $t = $CC->[0] / ($CC->[1] - 1); # Break green as 1:3, but use the slope as above with t-correction
|
|
1901
|
0
|
|
|
|
|
0
|
my $sl = 3*(1+$t*$t)/(1+$t*$t*$t)/(4*$CC->[1] - 2);
|
|
1902
|
0
|
|
|
|
|
0
|
return($sl, 1 - $sl*$CC->[0]);
|
|
1903
|
|
|
|
|
|
|
} else { # Likewise
|
|
1904
|
0
|
|
|
|
|
0
|
my $t = $CC->[1] / ($CC->[0] - 1); # If differ by 1, the distances to red lines are ¼, ¾, ⁵⁄₄,... exactly as ½,3/3 for equal lengths
|
|
1905
|
0
|
|
|
|
|
0
|
my $sl = 3*(1+$t*$t)/(1+$t*$t*$t)/(4*$CC->[0] - 2);
|
|
1906
|
0
|
|
|
|
|
0
|
return($sl, 1 - $sl*$CC->[0]);
|
|
1907
|
|
|
|
|
|
|
}
|
|
1908
|
|
|
|
|
|
|
}
|
|
1909
|
|
|
|
|
|
|
}
|
|
1910
|
|
|
|
|
|
|
# Up to this moment, always successfully return; below, unsuccessful returns indented (the only successful is the last one):
|
|
1911
|
0
|
|
|
|
|
0
|
my %jump_pre_ext = map { ($_ + !!$green_at_left, $extended->{$_}) } keys %$extended; # shift keys of extended to be pre-jump
|
|
|
0
|
|
|
|
|
0
|
|
|
1912
|
0
|
|
|
|
|
0
|
my $tot_jumps = 0;
|
|
1913
|
0
|
|
|
|
|
0
|
$tot_jumps += $_ for @jumps;
|
|
1914
|
0
|
|
|
|
|
0
|
my($slope_min, $slope_max) = (($tot_jumps - !!$jump_pre_ext{0})/@jumps, ($tot_jumps + !!$jump_pre_ext{@jumps})/@jumps);
|
|
1915
|
0
|
0
|
|
|
|
0
|
if (int($slope_min) != int $slope_max) { # differ by ≤1 unless @jumps = 1
|
|
|
|
0
|
|
|
|
|
|
|
1916
|
|
|
|
|
|
|
# There is a chance that after shear transformation with slope = int $slope_max, we have both increasing and decreasing paths.
|
|
1917
|
|
|
|
|
|
|
# But then there is also a horizontal path (yes!); choose it.
|
|
1918
|
0
|
|
|
|
|
0
|
my($H, @ok) = (0, (1) x (1+!!$jump_pre_ext{0})); # Cur min-height; OK-Height of horizontal line (now = before the jump № 0)
|
|
1919
|
0
|
|
|
|
|
0
|
for my $j (0..$#jumps) {
|
|
1920
|
0
|
|
|
|
|
0
|
$H += ( $jumps[$j] - int $slope_max );
|
|
1921
|
0
|
|
|
|
|
0
|
my $add = $jump_pre_ext{$j + 1};
|
|
1922
|
0
|
0
|
|
|
|
0
|
@ok = grep { $H <= $_ and $H + !!$add >= $_ } @ok or
|
|
|
0
|
0
|
|
|
|
0
|
|
|
1923
|
|
|
|
|
|
|
return;
|
|
1924
|
|
|
|
|
|
|
}
|
|
1925
|
0
|
|
|
|
|
0
|
die "Bug: need to fix the constant term for the shear transform"; # XXX ??? And axes flip! Check start/end segments too!
|
|
1926
|
0
|
|
0
|
|
|
0
|
my $tot = (grep $_, @ok) || 0; # □□□□□□□□□□□□□ Example. (after shear transform)
|
|
1927
|
0
|
|
|
|
|
0
|
return int $slope_max, $tot/@ok; # □□□□ □ □□□ @ok = 1, $tot = 1
|
|
1928
|
|
|
|
|
|
|
} elsif (%$extended) { # In general: may always look for non-decreasing path (after flip+shear).
|
|
1929
|
|
|
|
|
|
|
# Find rightmost consecutive run on each row (of those joining to the preceeding row)
|
|
1930
|
0
|
|
|
|
|
0
|
my($H, @cur) = (0, (1) x (1+!!$jump_pre_ext{0})); # Cur min-height; OK-Height of horizontal line (now = before the jump № 0)
|
|
1931
|
0
|
|
|
|
|
0
|
my(@starts) = ((0) x (1+!!$jump_pre_ext{0}));
|
|
1932
|
0
|
|
|
|
|
0
|
for my $j (0..$#jumps) {
|
|
1933
|
0
|
|
|
|
|
0
|
$H += ( $jumps[$j] - int $slope_max );
|
|
1934
|
0
|
|
|
|
|
0
|
my $add = !!$jump_pre_ext{$j + 1};
|
|
1935
|
|
|
|
|
|
|
# if one of cur is above $H + $add, remove the run at this height
|
|
1936
|
0
|
0
|
|
|
|
0
|
return if $cur[0] > $H + $add;
|
|
1937
|
0
|
0
|
0
|
|
|
0
|
pop @starts, pop @cur if $#cur and $cur[1] > $H + $add;
|
|
1938
|
0
|
0
|
|
|
|
0
|
@cur = grep { $H <= $_ and $H + $add >= $_ } @cur or
|
|
|
0
|
0
|
|
|
|
0
|
|
|
1939
|
|
|
|
|
|
|
return;
|
|
1940
|
|
|
|
|
|
|
}
|
|
1941
|
|
|
|
|
|
|
}
|
|
1942
|
0
|
|
|
|
|
0
|
my($U,$D,@jU,@jD,%seenU,%seenD) = (0,0); # jumps, seen: modifiable up/down
|
|
1943
|
0
|
|
|
|
|
0
|
$#jU = $#jD = @jumps + 5; # Make small negative indices access unreachable elts
|
|
1944
|
0
|
|
0
|
|
|
0
|
$extended->[$_] and $jD[$_ + !!$green_at_left]++ and $jU[$_ + !!$green_at_left - 1]++ for keys %$extended; # may access -1
|
|
|
|
|
0
|
|
|
|
|
|
1945
|
0
|
|
|
|
|
0
|
for my $n (0..$#jumps) {
|
|
1946
|
0
|
|
|
|
|
0
|
$seen {$jumps[$n]}++;
|
|
1947
|
0
|
0
|
|
|
|
0
|
$seenU{$jumps[$n]}++ if $jU[$n];
|
|
1948
|
0
|
0
|
|
|
|
0
|
$seenD{$jumps[$n]}++ if $jD[$n];
|
|
1949
|
|
|
|
|
|
|
}
|
|
1950
|
0
|
|
|
|
|
0
|
$seen{$_}++ for @jumps;
|
|
1951
|
|
|
|
|
|
|
# warn("many keys\n"),
|
|
1952
|
0
|
0
|
|
|
|
0
|
return if 2 + !!$U + !!$D < keys %seen; # There should be at most 2 different jumps (after correction UP/DOWN)
|
|
1953
|
0
|
|
|
|
|
0
|
my @JUMPS = sort {$a <=> $b} keys %seen;
|
|
|
0
|
|
|
|
|
0
|
|
|
1954
|
|
|
|
|
|
|
# print("jumps=@JUMPS\n"),
|
|
1955
|
0
|
|
|
|
|
0
|
my($min, $max) = @JUMPS[0,-1];
|
|
1956
|
|
|
|
|
|
|
# warn("jumps=@JUMPS\n"),
|
|
1957
|
0
|
0
|
0
|
|
|
0
|
return if @JUMPS > 1 and $max - $min > 1 + !!$U + !!$D; # If two different jumps, must differ by 1
|
|
1958
|
0
|
|
0
|
|
|
0
|
my($min_can_U, $max_can_D) = ($seen{$min} == ($seenU{$min} || 0), $seen{$max} == ($seenD{$max} || 0));
|
|
|
|
|
0
|
|
|
|
|
|
1959
|
0
|
0
|
|
|
|
0
|
if ($min_can_U) { # Cannot correct if two mins are adjacent
|
|
1960
|
0
|
0
|
|
|
|
0
|
$min_can_U = 0 if grep { $jumps[$_] == $min and $jumps[$_+1] == $min } 0..($#jumps-1);
|
|
|
0
|
0
|
|
|
|
0
|
|
|
1961
|
|
|
|
|
|
|
} # min and max cannot conflict!
|
|
1962
|
0
|
0
|
|
|
|
0
|
if ($max_can_D) { # Cannot correct if two mins are adjacent
|
|
1963
|
0
|
0
|
|
|
|
0
|
$max_can_D = 0 if grep { $jumps[$_] == $max and $jumps[$_+1] == $max } 0..($#jumps-1);
|
|
|
0
|
0
|
|
|
|
0
|
|
|
1964
|
|
|
|
|
|
|
}
|
|
1965
|
|
|
|
|
|
|
# warn("edge too long, jumps=@JUMPS\n"),
|
|
1966
|
0
|
0
|
0
|
|
|
0
|
return if not $green_at_left and (my $l_O = $CC->[0] - $min - 1) > 0 # left end too long (with $green_at_left already done)
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1967
|
|
|
|
|
|
|
or not $green_at_right and (my $r_O = $CC->[-1] - $min - 1) > 0; # right end too long
|
|
1968
|
|
|
|
|
|
|
# Now may do the induction step (the trivial case @JUMPS == 1 and $min = 1 is already excluded)
|
|
1969
|
0
|
|
|
|
|
0
|
my @rect = 1;
|
|
1970
|
0
|
|
|
|
|
0
|
for my $j (@jumps) {
|
|
1971
|
0
|
0
|
|
|
|
0
|
$rect[-1]++, next if $j == $min;
|
|
1972
|
0
|
|
|
|
|
0
|
push @rect, 1;
|
|
1973
|
|
|
|
|
|
|
} # Found new rectangles
|
|
1974
|
0
|
0
|
0
|
|
|
0
|
return unless my($sl, $sh) = encodes_line \@rect, (!$green_at_left and !$l_O), (!$green_at_right and !$r_O);
|
|
|
|
|
0
|
|
|
|
|
|
1975
|
|
|
|
|
|
|
# print "sub-slant=$sl <-- (@rect), ", (!$green_at_left and !$l_O), ", ", (!$green_at_right and !$r_O), "\n";
|
|
1976
|
0
|
|
|
|
|
0
|
$sh += $sl*0.5; # Recalc so that the origin is at bottom of the leftmost new-red (=old-green) line
|
|
1977
|
0
|
|
|
|
|
0
|
$sl += $min; # Undo the shear transformation; now we are in x-y-exchanged coordinate system; origin = bot. of 1st green
|
|
1978
|
|
|
|
|
|
|
# If we had green_at_left, the first of old green intervals is (in x-y-exchanged coordinate system) vertical, centered at (0,0)
|
|
1979
|
|
|
|
|
|
|
# Otherwise it was centered at (1,$CC->[0])
|
|
1980
|
0
|
0
|
|
|
|
0
|
my($X,$Y) = ($green_at_left ? (0,0) : (1, $CC->[0]));
|
|
1981
|
0
|
|
|
|
|
0
|
$Y -= 0.5;
|
|
1982
|
0
|
|
|
|
|
0
|
$sh += $Y - $X*$sl; # Now $sh is w.r.t. the unshifed x-y-exchanged coordinate system
|
|
1983
|
0
|
|
|
|
|
0
|
return (1/$sl, -$sh/$sl); # Finally, exchange the axes back
|
|
1984
|
|
|
|
|
|
|
}
|
|
1985
|
|
|
|
|
|
|
|
|
1986
|
|
|
|
|
|
|
sub stroke_2_line ($) {
|
|
1987
|
0
|
|
|
0
|
0
|
0
|
my($s, %seen, %dup) = shift; # $s->[$i][0] is dir∈(0..7);
|
|
1988
|
0
|
|
|
|
|
0
|
my @d = map $_->[0], @$s;
|
|
1989
|
0
|
|
|
|
|
0
|
$seen{$_}++ for @d;
|
|
1990
|
0
|
0
|
|
|
|
0
|
2 >= (my @D = keys %seen) or return;
|
|
1991
|
0
|
0
|
|
|
|
0
|
if (@D == 1) {
|
|
1992
|
0
|
|
|
|
|
0
|
my($dx,$dy) = ($dx[$d[0]], $dy[$d[0]]);
|
|
1993
|
0
|
|
|
|
|
0
|
return [0,0, @d * $dx, @d * $dy, 0,0, 2*$D[0]]; # move-beg, vector, move-end, 2*dir
|
|
1994
|
|
|
|
|
|
|
} # now @D == 2;
|
|
1995
|
0
|
|
0
|
|
|
0
|
$d[$_-1] == $d[$_] and $dup{$d[$_]}++ for 1..$#d;
|
|
1996
|
|
|
|
|
|
|
# warn "@d --> dup ", join(' ', %dup), "\n";
|
|
1997
|
0
|
0
|
|
|
|
0
|
1 >= (my @DD = keys %dup) or return;
|
|
1998
|
0
|
0
|
|
|
|
0
|
my $dup = @DD ? $DD[0] : $d[$d[0] % 2]; # if @DD is empty, two dirs alternate; assume that the odd one is a separator; ==>
|
|
1999
|
0
|
|
|
|
|
0
|
my $dir = $D[0] + $D[1];
|
|
2000
|
0
|
|
|
|
|
0
|
my $sep = $dir - $dup; # directional-independence — there is no guarantie that y=x-y preserves the best fit
|
|
2001
|
0
|
0
|
0
|
|
|
0
|
$dir += 8 if $dir == 7 and !($D[0] * $D[1]);
|
|
2002
|
|
|
|
|
|
|
# Do not “optimize” horizontal/vertical lines of len>2 with one diag stroke at the end (excluding tips):
|
|
2003
|
0
|
0
|
0
|
|
|
0
|
if ($sep % 2 and $seen{$sep} and $seen{$sep} <= 2 and @d > 2 and $seen{$sep} == (my @eSEP = grep $d[$_] == $sep, 0, -1)) {
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2004
|
0
|
|
|
|
|
0
|
@eSEP = grep !$s->[$_][5], @eSEP;
|
|
2005
|
0
|
0
|
|
|
|
0
|
return if @eSEP <= 1;
|
|
2006
|
|
|
|
|
|
|
}
|
|
2007
|
|
|
|
|
|
|
# return if $sep % 2 and $seen{$sep} == 1 and @d > 2 and grep $_ == $sep, @d[0,-1];
|
|
2008
|
0
|
|
|
|
|
0
|
my($i,$col,@col) = (0,1);
|
|
2009
|
0
|
|
|
|
|
0
|
while ($i < @d) {
|
|
2010
|
0
|
0
|
|
|
|
0
|
if ($d[$i] == $sep) {
|
|
2011
|
0
|
|
|
|
|
0
|
push @col, $col;
|
|
2012
|
0
|
|
|
|
|
0
|
$col = 1;
|
|
2013
|
|
|
|
|
|
|
} else {
|
|
2014
|
0
|
|
|
|
|
0
|
$col++;
|
|
2015
|
|
|
|
|
|
|
}
|
|
2016
|
0
|
|
|
|
|
0
|
$i++;
|
|
2017
|
|
|
|
|
|
|
}
|
|
2018
|
0
|
|
|
|
|
0
|
push @col, $col;
|
|
2019
|
|
|
|
|
|
|
# warn "Scan of col: @col\n";
|
|
2020
|
0
|
0
|
|
|
|
0
|
my($slope, $offset) = encodes_line \@col or return;
|
|
2021
|
0
|
|
|
|
|
0
|
$offset -= 0.5 - $slope*0.5; # Recalc offset to be w.r.t. the center of the first square
|
|
2022
|
|
|
|
|
|
|
#### warn "slope=$slope; offset=$offset of @col; dup=$dup, sep=$sep [in = @d]\n";
|
|
2023
|
|
|
|
|
|
|
# “Reflection” below moves squares on diagonal to a horizontal sequence of squares; it preserves the square centered at (½,½)
|
|
2024
|
|
|
|
|
|
|
# Offsets w.r.t. this center are inverted
|
|
2025
|
0
|
0
|
|
|
|
0
|
($dup, $sep, $slope, $offset) = ($sep, $dup, 1-$slope, -$offset) if $dup % 2; # goes more diagonally than horizontally/vertically
|
|
2026
|
|
|
|
|
|
|
# Now $dup is horizontal/vertical, and $sep is diagonal
|
|
2027
|
0
|
|
|
|
|
0
|
my($dx,$dy) = ($dx[$dup], $dy[$dup]);
|
|
2028
|
0
|
|
|
|
|
0
|
my($dx1,$dy1) = ($dx[$sep]-$dx, $dy[$sep]-$dy); # “orthogonal” direction
|
|
2029
|
0
|
|
|
|
|
0
|
my $C = grep $_ == $sep, @d; # Naive move in “orthogonal” direction
|
|
2030
|
0
|
|
|
|
|
0
|
my $lineC = @d * $slope + $offset;
|
|
2031
|
0
|
|
|
|
|
0
|
my $end_off = $lineC - $C;
|
|
2032
|
0
|
|
|
|
|
0
|
return [$dx1 * $offset, $dy1 * $offset, @d * $dx + $lineC * $dx1, @d * $dy + $lineC * $dy1, # vectors of start_offset, end_coord,
|
|
2033
|
|
|
|
|
|
|
$dx1 * $end_off, $dy1 * $end_off, $dir]; # end_off
|
|
2034
|
|
|
|
|
|
|
}
|
|
2035
|
|
|
|
|
|
|
|
|
2036
|
|
|
|
|
|
|
# Break a “smooth” stroke into convex parts, straight lines, and snakes-not-convertable-to-straight-lines
|
|
2037
|
|
|
|
|
|
|
sub stroke_subdivide ($) { # We suppose it is known that this is not suitable for calculated lines, but rotates at most by ±1
|
|
2038
|
0
|
|
|
0
|
0
|
0
|
my ($edges, $last_snake, $last_r, @runs, @turns, @t_pos, @parts) = (shift, -1, 0, 1); # @runs starts with the first REAL elt, 1
|
|
2039
|
0
|
|
|
|
|
0
|
$turns[0] = $t_pos[0] = 0; # Turns are at VERTICES, so they are shifted w.r.t. edges by -½: this 0 means -½ w.r.t. edge nuns
|
|
2040
|
0
|
|
|
|
|
0
|
for my $i (1..$#$edges) {
|
|
2041
|
0
|
0
|
|
|
|
0
|
next unless my $r = ($edges->[$i][0] - $edges->[$i-1][0])%8; # One of 0, 1, 7
|
|
2042
|
0
|
|
0
|
|
|
0
|
$last_r ||= $r;
|
|
2043
|
0
|
|
|
|
|
0
|
push @turns, $r; # 135° corners: direction
|
|
2044
|
0
|
|
|
|
|
0
|
push @t_pos, $i; # same: ordinal of edge which is after the turn
|
|
2045
|
0
|
0
|
|
|
|
0
|
next if $r == $last_r;
|
|
2046
|
0
|
|
|
|
|
0
|
push @runs, $#turns; # Which corner starts a new convex sequence of corners (including sequences of length=1 in snakes!)
|
|
2047
|
0
|
|
|
|
|
0
|
$last_r = $r;
|
|
2048
|
|
|
|
|
|
|
} # a run is in turns, from $runs[$j] inclusive to $runs[$j+1] exclusive; has the same direction of turns
|
|
2049
|
0
|
|
|
|
|
0
|
push(@turns, 0); push(@t_pos, scalar @$edges); # These are not REAL, and not included in @runs due to end-exclusion
|
|
|
0
|
|
|
|
|
0
|
|
|
2050
|
0
|
|
|
|
|
0
|
push @runs, $#turns; # runs are turns between consecutive elts of @runs, begin-inclusive, end-exclusive (REAL elts only!)
|
|
2051
|
0
|
|
|
|
|
0
|
for my $j (1..$#runs) {
|
|
2052
|
0
|
0
|
|
|
|
0
|
if ($runs[$j] - $runs[$j-1] > 1) { # a convex run: ≥2 corners, so cannot be a part of a snake
|
|
|
|
0
|
|
|
|
|
|
|
2053
|
0
|
0
|
0
|
|
|
0
|
$parts[-1][1][1] = $runs[$j-2] if $parts[-1] and $parts[-1][1]; # previous part is a snake (unterminated yet); terminate
|
|
2054
|
0
|
|
|
|
|
0
|
push @parts, [[$runs[$j-1], $runs[$j]-1]]; # parts are both-ends-inclusive
|
|
2055
|
|
|
|
|
|
|
} elsif (++$last_snake != $j) { # start of a new snake (ends are dealed with on hext non-snake)
|
|
2056
|
0
|
|
|
|
|
0
|
push @parts, [undef, [$runs[$j-1]]]; # Which turn starts a new snake; the termination slot is bogus so far
|
|
2057
|
0
|
|
|
|
|
0
|
$last_snake = $j;
|
|
2058
|
|
|
|
|
|
|
}
|
|
2059
|
|
|
|
|
|
|
}
|
|
2060
|
|
|
|
|
|
|
# $snakes[-1][1] = @turns if $finish_snake;
|
|
2061
|
0
|
0
|
|
|
|
0
|
$parts[-1][1][1] = $#turns - 1 if $parts[-1][1]; # parts are both-ends-inclusive; include the last REAL element
|
|
2062
|
|
|
|
|
|
|
#my @T = map { $t_pos[$_] . ($turns[$_] > 6 ? '-' : ($turns[$_] ? '+' : '')) } 0..$#turns;
|
|
2063
|
|
|
|
|
|
|
#warn "turns=(@T), runs=(@runs), snake-parts ", (map 0+!!$_->[1], @parts),
|
|
2064
|
|
|
|
|
|
|
# ", lines (turn#) ", (map {"$_->[1][0]..$_->[1][1], "} grep $_->[1], @parts),
|
|
2065
|
|
|
|
|
|
|
# ", lines (edge) ", (map {($t_pos[$_->[1][0]]-1)."..$t_pos[$_->[1][1]], "} grep $_->[1], @parts), "\n";
|
|
2066
|
0
|
|
|
|
|
0
|
my @parts_edges;
|
|
2067
|
|
|
|
|
|
|
# @parts_edges = map [$t_pos[$_->[-1][0]] - 1, $t_pos[$_->[-1][0]]], @parts; # start: edge before corner
|
|
2068
|
0
|
|
|
|
|
0
|
for my $part (@parts) {
|
|
2069
|
0
|
0
|
|
|
|
0
|
if ($part->[0]) {
|
|
2070
|
0
|
|
|
|
|
0
|
my $mid = $part->[0][1] != $#t_pos; # Avoid accessing out-of-bound value (will be overwritten later anyway)
|
|
2071
|
0
|
|
|
|
|
0
|
push @parts_edges, [ $t_pos[$part->[0][0] - 1], $t_pos[$part->[0][1] + $mid] - $mid ]; # both sides inclusive
|
|
2072
|
|
|
|
|
|
|
} else {
|
|
2073
|
0
|
|
|
|
|
0
|
push @parts_edges, [ $t_pos[$part->[1][0]] - 1, $t_pos[$part->[1][1]] ]; # overlaps by 1 the neighbors
|
|
2074
|
|
|
|
|
|
|
}
|
|
2075
|
|
|
|
|
|
|
}
|
|
2076
|
|
|
|
|
|
|
# for my $i (0..$#parts) {
|
|
2077
|
|
|
|
|
|
|
# my $part = $parts[$i];
|
|
2078
|
|
|
|
|
|
|
# if ($part->[0]) {
|
|
2079
|
|
|
|
|
|
|
# my $b = $t_pos[$part->[0][0] - !!$i];
|
|
2080
|
|
|
|
|
|
|
# my $mid = $i != $#parts;
|
|
2081
|
|
|
|
|
|
|
# my $e = $t_pos[$part->[0][1] + $mid] - $mid;
|
|
2082
|
|
|
|
|
|
|
# push @parts_edges, [ $b, $e ]; # both sides inclusive
|
|
2083
|
|
|
|
|
|
|
# } else {
|
|
2084
|
|
|
|
|
|
|
# push @parts_edges, [ $t_pos[$part->[1][0]] - 1, $t_pos[$part->[1][1]] ]; # overlaps by 1 the neighbors
|
|
2085
|
|
|
|
|
|
|
# }
|
|
2086
|
|
|
|
|
|
|
# }
|
|
2087
|
|
|
|
|
|
|
#warn "predivide 0..$#$edges: ", join(' ', map $_->[0], @$edges), " => ", (map "$_->[0]...$_->[1]" . (!!$_->[2] && ':L') . " ", @parts_edges), "\n";
|
|
2088
|
|
|
|
|
|
|
# The logic above breaks for first/last segments:
|
|
2089
|
0
|
|
|
|
|
0
|
$parts_edges[0][0] = 0 ;#if $parts[0][1]; # incorporate the full preceding segment into the snake,
|
|
2090
|
0
|
|
|
|
|
0
|
$parts_edges[-1][1] = $#$edges ;#if $parts[-1][1]; # and not just one edge of it
|
|
2091
|
|
|
|
|
|
|
#warn "predivide 0..$#$edges: ", join(' ', map $_->[0], @$edges), " => ", (map "$parts_edges[$_]->[0]...$parts_edges[$_]->[1]" . (!!$parts[$_]->[1] && ':S') . " ", 0..$#parts_edges), "\n";
|
|
2092
|
|
|
|
|
|
|
#warn "parts_edges=$#parts_edges, edges=$#$edges; @ -1: b: $parts_edges[-1][0]; e: $parts_edges[-1][1]\n";
|
|
2093
|
|
|
|
|
|
|
#warn "parts_edges=$#parts_edges, edges=$#$edges; @ 2: b: $parts_edges[2][0]; e: $parts_edges[2][1]\n" if $#parts_edges==2;
|
|
2094
|
0
|
|
|
|
|
0
|
my($J, $donext, @out) = (0, 1); # up to $J-1 are written to @out
|
|
2095
|
|
|
|
|
|
|
#
|
|
2096
|
|
|
|
|
|
|
# We split the sequence of directions into snakes and convex parts. (May be overlapping where they join.)
|
|
2097
|
|
|
|
|
|
|
# Currently, we use this info in a very rudimentary way: we try to convert a snake to a line (with the overlap edge, or not);
|
|
2098
|
|
|
|
|
|
|
# if cannot, we join the "unrecognized" parts together.
|
|
2099
|
|
|
|
|
|
|
#
|
|
2100
|
0
|
|
|
|
|
0
|
for my $j (0..$#parts) { # Linearize (sub)snakes; remove overlap between snakes and ???
|
|
2101
|
0
|
0
|
|
|
|
0
|
next unless $donext++; # May skip the convex part after an unrecognized snake
|
|
2102
|
0
|
|
|
|
|
0
|
my $part = $parts[$j];
|
|
2103
|
|
|
|
|
|
|
#warn " fixing... j=$j"; # part=<@$part>";
|
|
2104
|
|
|
|
|
|
|
#warn(" $J <-- $j"),
|
|
2105
|
0
|
0
|
|
|
|
0
|
$out[$J++] = $parts_edges[$j], next unless my $snake = $part->[1]; # convex: at start, or after convex/recognized-line
|
|
2106
|
0
|
|
|
|
|
0
|
my($b,$e) = @{ $parts_edges[$j] };
|
|
|
0
|
|
|
|
|
0
|
|
|
2107
|
0
|
|
|
|
|
0
|
my @S = @$edges[$b..$e]; # Try first extension by 1 on both sides
|
|
2108
|
0
|
|
|
|
|
0
|
my($line) = stroke_2_line \@S;
|
|
2109
|
|
|
|
|
|
|
#warn "to line: #$j (end: parts=$#parts, parts_edges=$#parts_edges, edges=$#$edges; b: $b; e: $e; OK: ", 0+!!$line,"\n";
|
|
2110
|
|
|
|
|
|
|
##warn "parts_edges=$#parts_edges, edges=$#$edges; @ 2: b: $parts_edges[2][0]; e: $parts_edges[2][1]\n" if $#parts_edges==2 and $j==2;
|
|
2111
|
0
|
0
|
|
|
|
0
|
if ($line) {
|
|
2112
|
|
|
|
|
|
|
#warn " longline: j=$j J=$J\n";
|
|
2113
|
0
|
|
|
|
|
0
|
$parts_edges[$j][2] = $line;
|
|
2114
|
0
|
0
|
|
|
|
0
|
$J and $parts_edges[$J-1][1]--; # A convex run contains at least 3 edges, so we will not annihilate it completely
|
|
2115
|
0
|
0
|
|
|
|
0
|
$j == $#parts or $parts_edges[$j+1][0]++;
|
|
2116
|
0
|
|
|
|
|
0
|
$out[$J++] = $parts_edges[$j];
|
|
2117
|
|
|
|
|
|
|
next
|
|
2118
|
0
|
|
|
|
|
0
|
} # Now last resort: try to shorten to min possible; since stroke_2_line() failed, we know that @S is long enough…
|
|
2119
|
0
|
|
|
|
|
0
|
my($b1, $e1) = ($b + 1, $e - 1);
|
|
2120
|
0
|
0
|
|
|
|
0
|
$b1 = $b unless $b;
|
|
2121
|
0
|
0
|
|
|
|
0
|
$e1 = $e if $e == $#$edges; # Does not make sense to shorten the snake at start/end of the stroke
|
|
2122
|
0
|
0
|
0
|
|
|
0
|
@out = @parts_edges, next if $b == $b1 and $e == $e1; # stroke is a wholesale snake
|
|
2123
|
0
|
|
|
|
|
0
|
@S = @$edges[$b1..$e1]; # Now try shortened by 1 on both sides
|
|
2124
|
0
|
0
|
|
|
|
0
|
unless (($line) = stroke_2_line \@S) {
|
|
2125
|
|
|
|
|
|
|
#warn " no-short-line: j=$j J=$J end->", ($j || 1 ? '' : '(omitted)' ), $parts_edges[$j + ($j!=$#parts)][1], "\n";
|
|
2126
|
0
|
0
|
|
|
|
0
|
$out[$J++] = $parts_edges[$j] unless $J; # create a previous part, if none
|
|
2127
|
0
|
|
|
|
|
0
|
$donext = 0;
|
|
2128
|
0
|
|
|
|
|
0
|
$out[$J-1][1] = $parts_edges[$j + ($j!=$#parts)][1]; # Extend the preceding part
|
|
2129
|
0
|
|
|
|
|
0
|
next;
|
|
2130
|
|
|
|
|
|
|
}
|
|
2131
|
|
|
|
|
|
|
#warn "shortened: #$j; b: $b --> $b1; e: $e -> $e1\n";
|
|
2132
|
|
|
|
|
|
|
#warn " short-line: j=$j J=$J\n";
|
|
2133
|
0
|
0
|
|
|
|
0
|
if ($b == $b1) {
|
|
2134
|
0
|
0
|
|
|
|
0
|
$J and $parts_edges[$J-1][1]--;
|
|
2135
|
|
|
|
|
|
|
} else {
|
|
2136
|
0
|
|
|
|
|
0
|
$parts_edges[$j][0]++
|
|
2137
|
|
|
|
|
|
|
}
|
|
2138
|
0
|
0
|
|
|
|
0
|
if ($e == $e1) {
|
|
2139
|
0
|
0
|
|
|
|
0
|
$j == $#parts or $parts_edges[$j+1][0]++;
|
|
2140
|
|
|
|
|
|
|
} else {
|
|
2141
|
0
|
|
|
|
|
0
|
$parts_edges[$j][1]--
|
|
2142
|
|
|
|
|
|
|
}
|
|
2143
|
0
|
|
|
|
|
0
|
$parts_edges[$j][2] = $line;
|
|
2144
|
0
|
|
|
|
|
0
|
$out[$J++] = $parts_edges[$j];
|
|
2145
|
|
|
|
|
|
|
}
|
|
2146
|
0
|
|
|
|
|
0
|
@out;
|
|
2147
|
|
|
|
|
|
|
}
|
|
2148
|
|
|
|
|
|
|
|
|
2149
|
|
|
|
|
|
|
sub crosses_line ($$;$$) { # segL = [stX,stY,eX,eY,del_eX, del_eY]; segL = [stX,stY,eX,eY] eq = (X-stX)*(eY-stY) - (Y-stY)*(eX-stX)
|
|
2150
|
0
|
|
0
|
0
|
0
|
0
|
my($seg,$segL,$opp, $expand, $endEq, $stEq) = (shift,shift,shift,shift||0); # del_eX, del_eY is in the same coordinate system as segL; the rest is shifted
|
|
2151
|
0
|
|
|
|
|
0
|
my($stX,$stY,$eX,$eY,$DeX,$DeY) = @$segL;
|
|
2152
|
|
|
|
|
|
|
# warn "opp=",!!$opp,"\tseg=[@$seg], line=($stX,$stY,$eX,$eY,$DeX,$DeY)";
|
|
2153
|
0
|
|
|
|
|
0
|
my $dF = ($seg->[0] - $seg->[2])*($eY-$stY) - ($seg->[1] - $seg->[3])*($eX-$stX);
|
|
2154
|
0
|
0
|
|
|
|
0
|
if ($opp) {
|
|
2155
|
0
|
|
|
|
|
0
|
$stEq = ($seg->[0] - $DeX)*($eY-$stY) - ($seg->[1] - $DeY)*($eX-$stX); # [0,1] is w.r.t. logical end
|
|
2156
|
0
|
|
|
|
|
0
|
$endEq = $stEq - $dF;
|
|
2157
|
|
|
|
|
|
|
} else {
|
|
2158
|
0
|
|
|
|
|
0
|
$endEq = ($seg->[4] - $stX)*($eY-$stY) - ($seg->[5] - $stY)*($eX-$stX); # [4,5] is w.r.t. start
|
|
2159
|
0
|
|
|
|
|
0
|
$stEq = $endEq + $dF;
|
|
2160
|
|
|
|
|
|
|
}
|
|
2161
|
|
|
|
|
|
|
# warn("st=$stEq end=$endEq"),
|
|
2162
|
0
|
0
|
|
|
|
0
|
return unless $endEq*$stEq < 0;
|
|
2163
|
0
|
|
|
|
|
0
|
my $frac = $endEq/($endEq - $stEq);
|
|
2164
|
0
|
0
|
|
|
|
0
|
$frac = 1 - $frac if $opp;
|
|
2165
|
0
|
|
|
|
|
0
|
my $new = $frac*(1+$expand);
|
|
2166
|
0
|
0
|
|
|
|
0
|
$new = ($frac + 1)/2 if $new > ($frac + 1)/2; # Do not expand pathologically
|
|
2167
|
0
|
|
|
|
|
0
|
$frac = $new;
|
|
2168
|
0
|
0
|
|
|
|
0
|
$frac = 1 - $frac if $opp;
|
|
2169
|
0
|
|
|
|
|
0
|
my $out = [($seg->[0]*$frac + $seg->[2]*(1-$frac)), ($seg->[1]*$frac + $seg->[3]*(1-$frac))]; # in coordinates of seg
|
|
2170
|
|
|
|
|
|
|
# warn "Out=[@$out] st=$stEq end=$endEq";
|
|
2171
|
0
|
|
|
|
|
0
|
$out
|
|
2172
|
|
|
|
|
|
|
}
|
|
2173
|
|
|
|
|
|
|
|
|
2174
|
|
|
|
|
|
|
sub stroke_2_strokes ($$$) {
|
|
2175
|
|
|
|
|
|
|
# The 1st version should not be applied to smooth closed loops: we assume that 0 is a corner
|
|
2176
|
2
|
|
|
2
|
0
|
6
|
my($s, $calc_hash, $closed) = (shift, shift, shift); # $s->[$i][0] is dir (0..7);
|
|
2177
|
2
|
|
|
|
|
11
|
my @d = map $_->[0], @$s;
|
|
2178
|
|
|
|
|
|
|
# warn "stroke: @d\n"; # start is before the segment with dir = $dir[start]
|
|
2179
|
2
|
|
|
|
|
8
|
my($prev_corner, @corners, @calc) = (0, [0]); # corners, at index: 0=start; optional: 1=calc_line, 2=start_moved, 3=end_moved
|
|
2180
|
2
|
|
|
|
|
7
|
for my $i (0..$#d) { # Between $corners[-1] (inclusive) and $prev_corner (exclusive) there is a region without calculated segments
|
|
2181
|
4
|
100
|
66
|
|
|
23
|
if ($i == $#d or abs(($d[$i+1] - $d[$i])%8 - 4) <= 2) { # found a corner at $i+1 (max+1 is AT_END)
|
|
2182
|
2
|
|
|
|
|
9
|
my(@SS, @parts) = @$s[$prev_corner..$i];
|
|
2183
|
2
|
50
|
|
|
|
10
|
if (@SS <= 1) {
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2184
|
0
|
|
|
|
|
0
|
@parts = [$prev_corner, $i];
|
|
2185
|
|
|
|
|
|
|
} elsif (2 == @SS) { # Do not convert to a line — most of the time is not beneficial
|
|
2186
|
2
|
|
|
|
|
9
|
@parts = ([$prev_corner, $i - 1], [$i - 1, $i]);
|
|
2187
|
|
|
|
|
|
|
} elsif (my($Line) = stroke_2_line \@SS) {
|
|
2188
|
0
|
|
|
|
|
0
|
@parts = [$prev_corner, $i, $Line];
|
|
2189
|
|
|
|
|
|
|
} else {
|
|
2190
|
0
|
|
|
|
|
0
|
@parts = stroke_subdivide(\@SS);
|
|
2191
|
|
|
|
|
|
|
#warn "subdivide 0..$#SS: ", join(' ', map $_->[0], @SS), " => ", (map "$_->[0]...$_->[1]" . (!!$_->[2] && ':L') . " ", @parts), " [$s->[0][1], $s->[0][2]] --> [$s->[-1][3], $s->[-1][4]]\n";
|
|
2192
|
0
|
|
|
|
|
0
|
$_->[0] += $prev_corner, $_->[1] += $prev_corner for @parts;
|
|
2193
|
|
|
|
|
|
|
# @parts = [$prev_corner, $i];
|
|
2194
|
|
|
|
|
|
|
## $prev_corner = $i+1;
|
|
2195
|
|
|
|
|
|
|
## next; # Not found
|
|
2196
|
|
|
|
|
|
|
} # Invariant: between corners, has either 1-edge segments, or a calculated line
|
|
2197
|
2
|
|
|
|
|
5
|
for my $part (@parts) { # Invariant: $prev_corner >= $corners[-1][0] (this is a candidate for the next corner)
|
|
2198
|
|
|
|
|
|
|
#warn " prev=$prev_corner line=<@{$part->[2] || []}> part[1]=$part->[1] last=$corners[-1][0]";
|
|
2199
|
4
|
50
|
|
|
|
40
|
$prev_corner = $part->[1] + 1, next unless my $line = $part->[2];
|
|
2200
|
|
|
|
|
|
|
# Now we found a calculated segment (at least 2 edges)
|
|
2201
|
0
|
0
|
|
|
|
0
|
push @corners, [$prev_corner] if $prev_corner != $corners[-1][0]; # create a new unrecognized chunk
|
|
2202
|
0
|
|
0
|
|
|
0
|
@{$corners[-1]}[1,2,3] = ($line, !($line->[0] == 0 and $line->[1] == 0), !($line->[4] == 0 and $line->[5] == 0));
|
|
|
0
|
|
0
|
|
|
0
|
|
|
2203
|
0
|
|
|
|
|
0
|
push @corners, [$prev_corner = $part->[1]+1]; # start new segment
|
|
2204
|
0
|
0
|
|
|
|
0
|
next if $part->[0] == $part->[1];
|
|
2205
|
0
|
|
|
|
|
0
|
for my $S (@$s[$part->[0]..$part->[1]]) {
|
|
2206
|
0
|
|
|
|
|
0
|
$calc_hash->{$S->[1],$S->[2]}{$S->[0]}++; # x,y,d
|
|
2207
|
0
|
|
|
|
|
0
|
my $d1 = ($S->[0] + 4)%8;
|
|
2208
|
0
|
|
|
|
|
0
|
$calc_hash->{$S->[3],$S->[4]}{$d1}++; # x1,y1,d1
|
|
2209
|
|
|
|
|
|
|
}
|
|
2210
|
|
|
|
|
|
|
}
|
|
2211
|
|
|
|
|
|
|
}
|
|
2212
|
|
|
|
|
|
|
}
|
|
2213
|
2
|
50
|
|
|
|
10
|
push @corners, [$#d+1] unless $corners[-1][0] == $#d+1; # end last segment
|
|
2214
|
|
|
|
|
|
|
# Fixing involves inserting new segments (making new corners); to avoid changing indices, do it back to front:
|
|
2215
|
2
|
|
|
|
|
7
|
for my $i (reverse(0..$#corners-1)) { # Try to fix misplaced joints (currently, only on unrecognized/calculated joins
|
|
2216
|
|
|
|
|
|
|
# warn "doing segment=$i; [", (join ', ', map +(ref() ? "[@$_]" : "$_"), @{$corners[$i]}), "]\n";
|
|
2217
|
2
|
|
|
|
|
5
|
unless (0 and $corners[$i][1]) {
|
|
2218
|
2
|
|
0
|
|
|
18
|
my $move_start = (($i or $closed) and $corners[$i-1][3]) && [@{$corners[$i-1][1]}[4,5]];
|
|
2219
|
2
|
|
50
|
|
|
9
|
my $move_end = $corners[$i+1][2] && [@{$corners[$i+1][1]}[0,1]];
|
|
2220
|
|
|
|
|
|
|
#warn "Fixing segment=$i: start=$move_start end=$move_end\n";
|
|
2221
|
2
|
50
|
|
|
|
7
|
if ($corners[$i][1]) { # Move start on straight-line segments, and move start and end on runs of 1-edge segments
|
|
2222
|
|
|
|
|
|
|
# No need to move start on the first segment if either (A) non-closed curve, or (B) last run is made of 1-edge segments.
|
|
2223
|
0
|
0
|
0
|
|
|
0
|
next unless ($i or $closed) and $corners[$i-1][1]; # If previous is a run of 1-edge segments, it would be fixed there
|
|
|
|
|
0
|
|
|
|
|
|
2224
|
0
|
0
|
|
|
|
0
|
my $my_move_start = $corners[$i][2] ? [@{$corners[$i][1]}[0,1]] : [0,0];
|
|
|
0
|
|
|
|
|
0
|
|
|
2225
|
0
|
|
0
|
|
|
0
|
$move_start ||= [0,0];
|
|
2226
|
0
|
0
|
|
|
|
0
|
next unless grep $move_start->[$_] != $my_move_start->[$_], 0, 1; # Just an optimization; the code below is more robust
|
|
2227
|
|
|
|
|
|
|
# Segments intersect iff ends of each one are on opposite sides of the line of other one.
|
|
2228
|
0
|
0
|
|
|
|
0
|
my $cross_prev = crosses_line($corners[$i-1][1], $corners[$i][1], !'opp', $extend_tip) or next;
|
|
2229
|
0
|
0
|
|
|
|
0
|
my $cross_our = crosses_line($corners[$i][1], $corners[$i-1][1], 'opp', $extend_tip) or next;
|
|
2230
|
|
|
|
|
|
|
# warn "Fixing... prev=[@$cross_prev] (@{$corners[$i-1][1]}[2,3]) our=[@$cross_our] (@{$corners[$i][1]}[0,1])";
|
|
2231
|
|
|
|
|
|
|
# Try one: just cut off at the intersection
|
|
2232
|
0
|
|
|
|
|
0
|
my @prev;
|
|
2233
|
0
|
|
|
|
|
0
|
$prev[$_] = ($corners[$i-1][1][4+$_] += $cross_prev->[$_] - $corners[$i-1][1][2+$_]) for 0, 1;
|
|
2234
|
0
|
|
|
|
|
0
|
$corners[$i-1][1][2+$_] = $cross_prev->[$_] for 0, 1;
|
|
2235
|
0
|
|
|
|
|
0
|
$corners[$i][1][$_] = $cross_our->[$_] for 0, 1;
|
|
2236
|
0
|
|
|
|
|
0
|
$corners[$i-1][3] = 0; # No longer have a mismatch
|
|
2237
|
0
|
|
|
|
|
0
|
$corners[$i][2] = 0; # No longer have a mismatch
|
|
2238
|
0
|
|
|
|
|
0
|
my @targ = map +($move_start->[$_] + $my_move_start->[$_])/2, 0, 1;
|
|
2239
|
0
|
|
|
|
|
0
|
my($r,$r1) = map $corners[$i-$_][1][6], 0, 1;
|
|
2240
|
0
|
|
|
|
|
0
|
$r1 = ($r1 + 8)%16;
|
|
2241
|
0
|
|
|
|
|
0
|
my $rot = ($r + $r1 + 16*(abs($r - $r1) >= 8))%32;
|
|
2242
|
0
|
|
|
|
|
0
|
splice @corners, $i, 0, [$corners[$i][0], [@prev, @targ, @targ, ($rot + 16)%32/2, 1]],
|
|
2243
|
|
|
|
|
|
|
[$corners[$i][0], [@targ, @$cross_our, @$cross_our, $rot/2, 1]];
|
|
2244
|
|
|
|
|
|
|
# $marked++;
|
|
2245
|
0
|
|
|
|
|
0
|
next;
|
|
2246
|
|
|
|
|
|
|
}
|
|
2247
|
2
|
50
|
33
|
|
|
13
|
next unless $move_start or $move_end;
|
|
2248
|
|
|
|
|
|
|
#warn "Fixing segment=$i: start=$move_start end=$move_end\n";
|
|
2249
|
0
|
|
|
|
|
0
|
my $len = $corners[$i+1][0] - $corners[$i][0];
|
|
2250
|
0
|
0
|
|
|
|
0
|
my @do = (($move_start ? 0 : ()), ($move_end ? $len-1 : ()));
|
|
|
|
0
|
|
|
|
|
|
|
2251
|
0
|
0
|
0
|
|
|
0
|
$#do = 0 if @do == 2 and not $do[1]; # len = 1
|
|
2252
|
0
|
|
|
|
|
0
|
my $kill = (@do == $len);
|
|
2253
|
0
|
|
|
|
|
0
|
for my $seg (reverse @do) { # reverse: as above
|
|
2254
|
0
|
0
|
0
|
|
|
0
|
my $start = ($move_start and not $seg) ? $move_start : [0,0];
|
|
2255
|
0
|
0
|
0
|
|
|
0
|
my $end = ($move_end and $seg == $len-1) ? $move_end : [0,0];
|
|
2256
|
0
|
|
|
|
|
0
|
my $dir = $d[$corners[$i][0] + $seg]; # direction before correction (1 after 2*$dir means approx)
|
|
2257
|
0
|
0
|
|
|
|
0
|
warn "i=$i, seg=$seg d=<@d> #d=$#d corners[$i]=<@{$corners[$i]}> #corners=$#corners\n\tcorners=<",
|
|
|
0
|
|
|
|
|
0
|
|
|
2258
|
|
|
|
|
|
|
join('> <', map "@$_", @corners), '>' unless defined $dir;
|
|
2259
|
0
|
|
|
|
|
0
|
my $line = [$start->[0], $start->[1], $end->[0] + $dx[$dir], $end->[1] + $dy[$dir], $end->[0], $end->[1], 2*$dir, 1];
|
|
2260
|
0
|
0
|
|
|
|
0
|
if ($seg) {
|
|
2261
|
0
|
|
|
|
|
0
|
$corners[$i+1][2] = 0; # No longer have a mismatch
|
|
2262
|
0
|
|
|
|
|
0
|
splice @corners, $i+1, 0, [$corners[$i+1][0] - 1, $line]; # with no mismatches
|
|
2263
|
|
|
|
|
|
|
} else {
|
|
2264
|
0
|
|
|
|
|
0
|
my $pos = $corners[$i][0];
|
|
2265
|
0
|
0
|
|
|
|
0
|
$corners[$i][0]++, splice @corners, $i, 0, [] unless $kill;
|
|
2266
|
0
|
|
|
|
|
0
|
@{$corners[$i]} = ($pos, $line);
|
|
|
0
|
|
|
|
|
0
|
|
|
2267
|
0
|
0
|
|
|
|
0
|
$corners[$i-1][3] = 0 if $i; # No longer have a mismatch
|
|
2268
|
0
|
0
|
|
|
|
0
|
$corners[$i+1][2] = 0 if $corners[$i+1]; # No longer have a mismatch
|
|
2269
|
|
|
|
|
|
|
}
|
|
2270
|
|
|
|
|
|
|
# warn "Fixed segment=$i: start=[@$move_start] end=[@$move_end]\n" if $move_start and $move_end;
|
|
2271
|
|
|
|
|
|
|
}
|
|
2272
|
|
|
|
|
|
|
}
|
|
2273
|
|
|
|
|
|
|
}
|
|
2274
|
2
|
|
|
|
|
7
|
for my $i (reverse(0..$#corners-1)) {
|
|
2275
|
|
|
|
|
|
|
# warn "doing segment=$i; [", (join ', ', map +(ref() ? "[@$_]" : "$_"), @{$corners[$i]}), "]\n";
|
|
2276
|
|
|
|
|
|
|
}
|
|
2277
|
2
|
|
|
|
|
5
|
my @breaks = 0; # Meaning: $corner[$break] starts a new sub-stroke
|
|
2278
|
2
|
|
|
|
|
6
|
for my $i (0..$#corners-2) { # check for mismatch at end
|
|
2279
|
0
|
0
|
0
|
|
|
0
|
push @breaks, $i+1 if $corners[$i][3] or $corners[$i+1][2];
|
|
2280
|
|
|
|
|
|
|
}
|
|
2281
|
2
|
|
|
|
|
10
|
\@corners, \@breaks, \@calc;
|
|
2282
|
|
|
|
|
|
|
}
|
|
2283
|
|
|
|
|
|
|
|
|
2284
|
|
|
|
|
|
|
sub traverse_boundary($$$$$) { # The blob is on our right
|
|
2285
|
0
|
|
|
0
|
0
|
0
|
my ($x, $y, $dir, $blob, $nextEdge, $c) = (shift, shift, shift, shift, shift, 1);
|
|
2286
|
|
|
|
|
|
|
# warn "Enter traverse_boundary()\n";
|
|
2287
|
0
|
|
|
|
|
0
|
while (1) { # Greedy algorithm: we always go left if we can
|
|
2288
|
|
|
|
|
|
|
# warn "... x=$x, y=$y, d=$dir\n";
|
|
2289
|
0
|
|
|
|
|
0
|
my $dir1 = ($dir - 2) %8;
|
|
2290
|
0
|
|
|
|
|
0
|
my $dx = $dx[$dir];
|
|
2291
|
0
|
|
|
|
|
0
|
my $dy = $dy[$dir];
|
|
2292
|
0
|
|
|
|
|
0
|
my $dx1 = $dx[$dir1];
|
|
2293
|
0
|
|
|
|
|
0
|
my $dy1 = $dy[$dir1];
|
|
2294
|
0
|
|
|
|
|
0
|
my($x1, $y1) = ($x+$dx+$dx1, $y+$dy+$dy1);
|
|
2295
|
0
|
0
|
|
|
|
0
|
if ($blob->[$y1][$x1]) { # Turn Left (already precalculated)
|
|
|
|
0
|
|
|
|
|
|
|
2296
|
|
|
|
|
|
|
} elsif ($blob->[$y + $dy][$x + $dx]) { # Continue
|
|
2297
|
0
|
|
|
|
|
0
|
$x1 = $x + $dx; $y1 = $y + $dy; $dir1 = $dir;
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
2298
|
|
|
|
|
|
|
} else { # Turn Right
|
|
2299
|
0
|
|
|
|
|
0
|
($x1, $y1, $dir1) = ($x, $y, ($dir + 2) % 8);
|
|
2300
|
|
|
|
|
|
|
}
|
|
2301
|
0
|
|
|
|
|
0
|
$nextEdge->[$dir][$y][$x] = [$x1, $y1, $dir1];
|
|
2302
|
0
|
|
|
|
|
0
|
($x, $y, $dir) = ($x1, $y1, $dir1);
|
|
2303
|
0
|
0
|
|
|
|
0
|
return $c if $nextEdge->[$dir][$y][$x];
|
|
2304
|
0
|
|
|
|
|
0
|
$c++;
|
|
2305
|
|
|
|
|
|
|
}
|
|
2306
|
|
|
|
|
|
|
}
|
|
2307
|
|
|
|
|
|
|
|
|
2308
|
|
|
|
|
|
|
sub _traverse_boundary($$$$$) { # The blob is on our right
|
|
2309
|
0
|
|
|
0
|
|
0
|
my ($x, $y, $dir, $blob, $nextEdge, $p, $dirOffset) = (shift, shift, shift, shift, shift, [], []);
|
|
2310
|
|
|
|
|
|
|
# warn "Enter _traverse_boundary()\n";
|
|
2311
|
0
|
0
|
|
|
|
0
|
return if $nextEdge->[$dir][$y][$x];
|
|
2312
|
0
|
|
|
|
|
0
|
while (1) { # Greedy algorithm: we always go left if we can, and only in even directions
|
|
2313
|
|
|
|
|
|
|
# warn "... x=$x, y=$y, d=$dir\n";
|
|
2314
|
0
|
|
|
|
|
0
|
my $dir1 = ($dir - 2) %8; # $dir - 2 points where there is NO blob
|
|
2315
|
0
|
|
|
|
|
0
|
my $dx = $dx[$dir];
|
|
2316
|
0
|
|
|
|
|
0
|
my $dy = $dy[$dir];
|
|
2317
|
0
|
|
|
|
|
0
|
my $dx1 = $dx[$dir1];
|
|
2318
|
0
|
|
|
|
|
0
|
my $dy1 = $dy[$dir1];
|
|
2319
|
0
|
|
|
|
|
0
|
my($x1, $y1) = ($x+$dx+$dx1, $y+$dy+$dy1); # diagonal directin
|
|
2320
|
0
|
|
|
|
|
0
|
push @$dirOffset, $dir;
|
|
2321
|
0
|
0
|
|
|
|
0
|
if ($blob->[$y1][$x1]) { # Turn Left (already precalculated)
|
|
|
|
0
|
|
|
|
|
|
|
2322
|
0
|
|
|
|
|
0
|
push @$p, [($dir-1)%8,$x,$y,$x1,$y1,$dirOffset];
|
|
2323
|
0
|
|
|
|
|
0
|
$dirOffset = [];
|
|
2324
|
|
|
|
|
|
|
} elsif ($blob->[$y + $dy][$x + $dx]) { # Continue
|
|
2325
|
0
|
|
|
|
|
0
|
$x1 = $x + $dx; $y1 = $y + $dy; $dir1 = $dir;
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
2326
|
0
|
|
|
|
|
0
|
push @$p, [$dir,$x,$y,$x1,$y1,$dirOffset];
|
|
2327
|
0
|
|
|
|
|
0
|
$dirOffset = [];
|
|
2328
|
|
|
|
|
|
|
} else { # Turn Right (In place!)
|
|
2329
|
0
|
|
|
|
|
0
|
($x1, $y1, $dir1) = ($x, $y, ($dir + 2) % 8);
|
|
2330
|
|
|
|
|
|
|
}
|
|
2331
|
0
|
|
|
|
|
0
|
$nextEdge->[$dir][$y][$x] = [$x1, $y1, $dir1];
|
|
2332
|
0
|
|
|
|
|
0
|
($x, $y, $dir) = ($x1, $y1, $dir1);
|
|
2333
|
|
|
|
|
|
|
|
|
2334
|
|
|
|
|
|
|
# push(@$p, [$dir,$x,$y,$x1,$y1]),
|
|
2335
|
|
|
|
|
|
|
# warn( '[', join('], [', map "@$_", @$p), ']'),
|
|
2336
|
0
|
0
|
|
|
|
0
|
if ($nextEdge->[$dir][$y][$x]) {
|
|
2337
|
0
|
0
|
|
|
|
0
|
unshift @{ $p->[-1][5] }, @$p if @$p;
|
|
|
0
|
|
|
|
|
0
|
|
|
2338
|
0
|
0
|
|
|
|
0
|
push @$p, [undef, $x,$y] unless @$p; # Singleton
|
|
2339
|
0
|
|
|
|
|
0
|
return $p;
|
|
2340
|
|
|
|
|
|
|
}
|
|
2341
|
|
|
|
|
|
|
}
|
|
2342
|
|
|
|
|
|
|
}
|
|
2343
|
|
|
|
|
|
|
|
|
2344
|
|
|
|
|
|
|
# start, end, and the encountered MForks are marked as already visited (into $traversedEdges)
|
|
2345
|
|
|
|
|
|
|
sub traverse_stroke ($$$$$$$;$) { # XXX Is there a duplication between seen/traversed???
|
|
2346
|
2
|
|
|
2
|
0
|
8
|
my($x,$y,$dir,$seenEndEdge,$nextEdge,$traversedEdges,$tips,$endstip) = (shift, shift, shift, shift, shift, shift, shift, shift);
|
|
2347
|
2
|
|
|
|
|
8
|
my($X,$Y) = ($x,$y);
|
|
2348
|
2
|
|
|
|
|
3
|
my @stroke;
|
|
2349
|
2
|
|
|
|
|
4
|
while (1) {
|
|
2350
|
4
|
|
|
|
|
10
|
my $x1 = $x + $dx[$dir];
|
|
2351
|
4
|
|
|
|
|
7
|
my $y1 = $y + $dy[$dir];
|
|
2352
|
4
|
50
|
0
|
|
|
24
|
last if $traversedEdges->{$x,$y,$dir}++ and not ($x1==$X and $y1==$Y); # applicable to loops: looped back (but not to tip)
|
|
|
|
|
33
|
|
|
|
|
|
2353
|
4
|
|
|
|
|
15
|
$traversedEdges->{$x1,$y1,($dir+4)%8}++;
|
|
2354
|
4
|
|
|
|
|
15
|
push @stroke, [$dir,$x,$y,$x1,$y1];
|
|
2355
|
4
|
100
|
|
|
|
17
|
$seenEndEdge->{$x1,$y1,($dir+4)%8}++, last unless defined(my $n = $nextEdge->[$y][$x][$dir]);
|
|
2356
|
|
|
|
|
|
|
#warn "found next edge: $x,$y $dir --> $x1,$y1 +$n\n";
|
|
2357
|
2
|
50
|
|
|
|
9
|
if (my $tip = $tips->{$x1,$y1}) {
|
|
2358
|
0
|
|
|
|
|
0
|
$tip = $tip->[2];
|
|
2359
|
|
|
|
|
|
|
# last if $tip == ($dir+4)%8; # When splitting a loop, happens on the 1st step (but we removed this tip!)
|
|
2360
|
0
|
|
|
|
|
0
|
my $x2 = $x1 + $dx[$tip];
|
|
2361
|
0
|
|
|
|
|
0
|
my $y2 = $y1 + $dy[$tip];
|
|
2362
|
0
|
|
|
|
|
0
|
push @stroke, [$tip,$x1,$y1,$x2,$y2,'tip'];
|
|
2363
|
0
|
|
|
|
|
0
|
push @stroke, [($tip+4)%8,$x2,$y2,$x1,$y1,'tip'];
|
|
2364
|
0
|
|
|
|
|
0
|
warn "doing tip from ($x1,$y1): dir=$tip, from_dir=$dir, next_dir=", ($dir + $n) % 8, "\n"
|
|
2365
|
|
|
|
|
|
|
if debug > 4;
|
|
2366
|
0
|
|
|
|
|
0
|
$traversedEdges->{$x1,$y1,$tip}++; # Protect from code which finds closed loops
|
|
2367
|
0
|
|
|
|
|
0
|
$traversedEdges->{$x2,$y2,($tip+4)%8}++; # Protect from code which finds closed loops
|
|
2368
|
|
|
|
|
|
|
}
|
|
2369
|
2
|
|
|
|
|
8
|
($x,$y,$dir) = ($x1, $y1, ($dir + $n) % 8);
|
|
2370
|
|
|
|
|
|
|
# last if $seenEndEdge->{$x,$y,$dir}; # applicable to loops: looped back
|
|
2371
|
|
|
|
|
|
|
}
|
|
2372
|
2
|
|
|
|
|
4
|
warn "found edges in a stroke: ", scalar @stroke, ': (', join(') (', "@{$stroke[0]}[1,2]", map "@$_[3,4]", @stroke), ")\n"
|
|
2373
|
|
|
|
|
|
|
if debug > 4;
|
|
2374
|
2
|
50
|
|
|
|
8
|
$stroke[$_][5] = 'tip' for $endstip ? (0, -1) : ();
|
|
2375
|
2
|
|
|
|
|
22
|
\@stroke;
|
|
2376
|
|
|
|
|
|
|
}
|
|
2377
|
|
|
|
|
|
|
|
|
2378
|
|
|
|
|
|
|
sub nnn6_do_Simple_and_edges ($$$$$$$$$$$$$) {
|
|
2379
|
2
|
|
|
2
|
0
|
67
|
my($width, $height, $edge, $cntedge,,$lastedge, $rays, $offs, $longedges, $blob, $pixels, $skipExtraBlob, $tailEdge, $coarse_blobs)
|
|
2380
|
|
|
|
|
|
|
= (shift, shift, shift, shift, shift, shift, shift, shift, shift, shift, shift, shift, shift);
|
|
2381
|
2
|
|
|
|
|
6
|
my(@nextEdge, @endEdge, %edges, %seenEndEdge, @strokes, %traversedEdges);
|
|
2382
|
2
|
|
|
|
|
8
|
for my $y (0..$#$edge) { # Effectively, “move” the position of the joint along the spur in MFork/Tail pairs from MFork to Tail
|
|
2383
|
11
|
100
|
|
|
|
29
|
next unless $edge->[$y]; # But only when there are exactly 3 edges (at Tail vertex, which is the branching point)
|
|
2384
|
6
|
|
|
|
|
11
|
for my $x ( 0..$#{ $edge->[$y] } ) {
|
|
|
6
|
|
|
|
|
16
|
|
|
2385
|
39
|
50
|
33
|
|
|
197
|
next unless $edge->[$y][$x] and my $t = $tailEdge->{$x,$y};
|
|
2386
|
0
|
|
|
|
|
0
|
my($dir, $rot) = @$t[2,3];
|
|
2387
|
0
|
0
|
|
|
|
0
|
next unless 3 == $cntedge->[$y][$x]; # was: ¤
|
|
2388
|
0
|
|
|
|
|
0
|
my @d = map +($dir+$_)%8, ($rot == 1) + 3, 5 - ($rot == -1);
|
|
2389
|
0
|
|
|
|
|
0
|
for my $branch (0, 1) {
|
|
2390
|
0
|
|
|
|
|
0
|
my $D = $d[$branch];
|
|
2391
|
0
|
|
|
|
|
0
|
my $x1 = $x + $dx[$D];
|
|
2392
|
0
|
|
|
|
|
0
|
my $y1 = $y + $dy[$D]; # special-case transversal of 2 edges leading into the branch point
|
|
2393
|
0
|
|
|
|
|
0
|
$nextEdge[$y1][$x1][($D+4)%8] = ($d[1-$branch] - $D - 4)%8; # would special-case transversal of the spur later
|
|
2394
|
|
|
|
|
|
|
}
|
|
2395
|
|
|
|
|
|
|
}
|
|
2396
|
|
|
|
|
|
|
}
|
|
2397
|
2
|
|
|
|
|
9
|
for my $y (0..$#$edge) { # For every directed-edge, find the next directed-edge. If none, mark the opposite as end-edge.
|
|
2398
|
11
|
100
|
|
|
|
27
|
next unless $edge->[$y]; # Except for spurs of the MFork (special-cased later).
|
|
2399
|
6
|
|
|
|
|
24
|
for my $x ( 0..$#{ $edge->[$y] } ) {
|
|
|
6
|
|
|
|
|
15
|
|
|
2400
|
39
|
50
|
|
|
|
84
|
next unless $edge->[$y][$x];
|
|
2401
|
39
|
|
|
|
|
71
|
for my $dir ( 0..$#{ $edge->[$y][$x] } ) {
|
|
|
39
|
|
|
|
|
86
|
|
|
2402
|
40
|
100
|
|
|
|
93
|
next unless $edge->[$y][$x][$dir];
|
|
2403
|
8
|
|
|
|
|
36
|
$edges{$x,$y,$dir} = [$x,$y,$dir];
|
|
2404
|
8
|
|
|
|
|
17
|
my $x1 = $x + $dx[$dir];
|
|
2405
|
8
|
|
|
|
|
14
|
my $y1 = $y + $dy[$dir];
|
|
2406
|
8
|
100
|
|
|
|
55
|
if ($cntedge->[$y1][$x1] == 2) {
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
2407
|
4
|
|
|
|
|
7
|
my @o;
|
|
2408
|
4
|
|
|
|
|
28
|
push @o, $_ for grep $edge->[$y1][$x1][$_], 0..7;
|
|
2409
|
4
|
|
|
|
|
12
|
my @oo = grep $_ != -4, map {($_- $dir + 4) % 8 - 4} @o; # find the other edge (is not it easier to find the sum???)
|
|
|
8
|
|
|
|
|
23
|
|
|
2410
|
|
|
|
|
|
|
#warn "found dirs [@o] at ($x,$y) --> $x1 $y1 $dir --> rot=$oo[0]\n";
|
|
2411
|
4
|
|
|
|
|
21
|
$nextEdge[$y][$x][$dir] = $oo[0];
|
|
2412
|
|
|
|
|
|
|
} elsif ($tailEdge->{$x,$y}) { # MFork, Tail; don't include in the end/nextEdge, special-case later
|
|
2413
|
|
|
|
|
|
|
# } elsif ($rays[$y][$x][$dir][0] =~ /^([MT])/) { # MFork, Tail; don't include in the end/nextEdge, special-case later
|
|
2414
|
|
|
|
|
|
|
} elsif ($tailEdge->{$x1,$y1}) { # Do not start at tail attachment; $nextEdge already set
|
|
2415
|
|
|
|
|
|
|
} else {
|
|
2416
|
4
|
|
|
|
|
32
|
push(@endEdge, [$x1,$y1,($dir+4)%8]); # Do not try to drive through junctions
|
|
2417
|
|
|
|
|
|
|
}
|
|
2418
|
|
|
|
|
|
|
}
|
|
2419
|
|
|
|
|
|
|
}
|
|
2420
|
|
|
|
|
|
|
}
|
|
2421
|
|
|
|
|
|
|
#warn "found endEdges: ", scalar @endEdge, "\n";
|
|
2422
|
2
|
|
|
|
|
5
|
my(@calc, %inCalcEdge);
|
|
2423
|
2
|
|
|
|
|
4
|
for my $edge (@endEdge) { # Find non-closed strokes (those having end-edge)
|
|
2424
|
4
|
|
|
|
|
12
|
my($x,$y,$dir) = @$edge;
|
|
2425
|
4
|
100
|
|
|
|
27
|
next if $seenEndEdge{$x,$y,$dir}++;
|
|
2426
|
|
|
|
|
|
|
# warn "endEdge: $x,$y, $dir, $cntedge->[$y][$x].\n";
|
|
2427
|
2
|
|
|
|
|
11
|
my $stroke = traverse_stroke($x,$y,$dir,\%seenEndEdge,\@nextEdge,\%traversedEdges, $tailEdge); # made of [$dir,$x,$y,$x1,$y1]
|
|
2428
|
2
|
|
33
|
|
|
10
|
my $closed = $stroke->[0][1] == $stroke->[-1][3] && $stroke->[0][2] == $stroke->[-1][4];
|
|
2429
|
2
|
50
|
|
|
|
6
|
if ($closed) {
|
|
2430
|
0
|
|
|
|
|
0
|
$closed = -2; # -2 means smooth, 2 means has a corner. Presume smooth (but with a junction)
|
|
2431
|
0
|
|
|
|
|
0
|
for my $i (0..$#$stroke) {
|
|
2432
|
0
|
0
|
|
|
|
0
|
$closed = 2, last unless abs(($stroke->[$i][0] - $stroke->[$i-1][0] + 4)%8 - 4) < 2; # At i=0, wraps back to the end
|
|
2433
|
|
|
|
|
|
|
}
|
|
2434
|
|
|
|
|
|
|
}
|
|
2435
|
|
|
|
|
|
|
# $closed &&= -2 if abs(($stroke->[0][1] - $stroke->[-1][1] + 4)%8 - 4) < 2;
|
|
2436
|
2
|
|
|
|
|
7
|
my($breaks, $runs) = [0];
|
|
2437
|
2
|
50
|
|
|
|
16
|
if ($closed < 0) { # loop known to be smooth; stroke_2_strokes() won't find anything except ends
|
|
2438
|
0
|
|
|
|
|
0
|
$runs = [[0],[$#$stroke+1]]; # fake corners at ends; [0] means: start at 0, no calculated lines until the next
|
|
2439
|
|
|
|
|
|
|
} else {
|
|
2440
|
2
|
|
|
|
|
14
|
($runs, $breaks) = stroke_2_strokes($stroke, \%inCalcEdge, $closed); # Meaning: $runs->[$break] starts a new sub-stroke
|
|
2441
|
|
|
|
|
|
|
}
|
|
2442
|
2
|
|
|
|
|
9
|
push @strokes, [$closed, !'blob', $stroke, $runs, $breaks]; # (strokes with endpoints: “open”)
|
|
2443
|
|
|
|
|
|
|
}
|
|
2444
|
|
|
|
|
|
|
# warn "found open strokes: ", scalar @strokes, "\n";
|
|
2445
|
2
|
|
|
|
|
7
|
my(@closedStrokes, %edgesDone);
|
|
2446
|
2
|
|
|
|
|
0
|
my @E;
|
|
2447
|
2
|
|
|
|
|
9
|
for my $E (sort keys %$tailEdge) { # Best place to cut a closed stroke — if present.
|
|
2448
|
0
|
|
|
|
|
0
|
my $edge = $tailEdge->{$E}; # Need to normalize order, since bugs in fontforge are sensitive to the order
|
|
2449
|
0
|
|
|
|
|
0
|
my($x,$y,$dir,$rot) = @$edge;
|
|
2450
|
0
|
|
|
|
|
0
|
my $D = ($dir+4)%8;
|
|
2451
|
0
|
|
|
|
|
0
|
my $x1 = $x + $dx[$dir];
|
|
2452
|
0
|
|
|
|
|
0
|
my $y1 = $y + $dy[$dir]; # the encountered MForks are marked as already visited (by traverse_stroke())
|
|
2453
|
0
|
|
|
|
|
0
|
push @E, [$x1,$y1,$D,!!'tip',$x,$y,$E,$rot]; # start with MFork end of the tail
|
|
2454
|
|
|
|
|
|
|
} # (those already encoutnered are ignored by traverse_stroke() anyway)
|
|
2455
|
2
|
|
|
|
|
45
|
push @E, map [@$_,0], @edges{sort keys %edges};
|
|
2456
|
2
|
|
|
|
|
24
|
for my $e (@E) { # Handle closed strokes (without end-edge, need to loop through all edges)
|
|
2457
|
8
|
|
|
|
|
21
|
my($x,$y,$dir,$T,$x1,$y1,$E,$rot) = @$e; # (Need to normalize order, since bugs in fontforge are sensitive to the order)
|
|
2458
|
8
|
50
|
|
|
|
27
|
next if $traversedEdges{$x,$y,$dir};
|
|
2459
|
0
|
0
|
|
|
|
0
|
if ($T) { # starting at MFork; need to redo the structure of “next” edges; we
|
|
2460
|
0
|
0
|
|
|
|
0
|
$nextEdge[$y][$x][$dir] = ($rot == 1 ? 0 : 7); # go clockwise (same direction as blobs), assuming the tip is outside
|
|
2461
|
0
|
|
|
|
|
0
|
my $x2 = $x1 + $dx[($dir+($rot != -1))%8]; # (x,y,d) is tip→joint=(x1,y1); we continue same-dir, or 45° counter-clockw
|
|
2462
|
0
|
|
|
|
|
0
|
my $y2 = $y1 + $dy[($dir+($rot != -1))%8];
|
|
2463
|
0
|
0
|
|
|
|
0
|
$nextEdge[$y2][$x2][($dir+($rot != -1)+4)%8] = (($rot == -1 ? 0 : 7)); # at end of the loop, return to the tip (DUP???)
|
|
2464
|
0
|
|
|
|
|
0
|
delete $tailEdge->{$E};
|
|
2465
|
|
|
|
|
|
|
}
|
|
2466
|
0
|
|
|
|
|
0
|
push @closedStrokes, traverse_stroke($x,$y,$dir,\%seenEndEdge,\@nextEdge,\%traversedEdges, $tailEdge, $T); # of [$dir,$x,$y,$x1,$y1]
|
|
2467
|
0
|
|
|
|
|
0
|
push @{ $closedStrokes[-1] }, !'blob';
|
|
|
0
|
|
|
|
|
0
|
|
|
2468
|
|
|
|
|
|
|
}
|
|
2469
|
2
|
|
|
|
|
4
|
my(@nextEdgeBlob, @entryPointBlob); # With lastedge, includes ends of lines:
|
|
2470
|
2
|
|
|
|
|
11
|
find_blobs($blob, $width, $height, $pixels, $cntedge, $offs, $lastedge, $skipExtraBlob);
|
|
2471
|
2
|
|
|
|
|
5
|
for my $y (1..$height) {
|
|
2472
|
10
|
|
|
|
|
18
|
my $inner = 0;
|
|
2473
|
10
|
|
|
|
|
20
|
for my $x ( 1..$width ) {
|
|
2474
|
82
|
50
|
|
|
|
200
|
next unless !$blob->[$y][$x] == $inner;
|
|
2475
|
0
|
|
|
|
|
0
|
my $blobX = $x - $inner;
|
|
2476
|
0
|
|
|
|
|
0
|
$inner = 1 - $inner;
|
|
2477
|
0
|
0
|
|
|
|
0
|
my $dir = $inner ? 0 : 4; # $dir - 2 is a direction to exit the blob
|
|
2478
|
0
|
0
|
|
|
|
0
|
next if $nextEdgeBlob[$dir][$y][$blobX]; # already passed through
|
|
2479
|
0
|
0
|
|
|
|
0
|
if ($coarse_blobs) {
|
|
2480
|
0
|
|
|
|
|
0
|
push @entryPointBlob, [$blobX, $y, $dir];
|
|
2481
|
0
|
|
|
|
|
0
|
$entryPointBlob[-1][3] = traverse_boundary($blobX, $y, $dir, $blob, \@nextEdgeBlob);
|
|
2482
|
|
|
|
|
|
|
} else {
|
|
2483
|
0
|
|
|
|
|
0
|
push @closedStrokes, _traverse_boundary($blobX, $y, $dir, $blob, \@nextEdgeBlob);
|
|
2484
|
0
|
|
|
|
|
0
|
push @{ $closedStrokes[-1] }, !!'blob';
|
|
|
0
|
|
|
|
|
0
|
|
|
2485
|
|
|
|
|
|
|
}
|
|
2486
|
|
|
|
|
|
|
}
|
|
2487
|
|
|
|
|
|
|
}
|
|
2488
|
2
|
|
|
|
|
6
|
for my $stroke (@closedStrokes) {
|
|
2489
|
0
|
|
|
|
|
0
|
my $is_blob = pop @$stroke;
|
|
2490
|
0
|
0
|
0
|
|
|
0
|
push(@strokes, [undef, !!'blob', $stroke, undef, [0]]), next
|
|
2491
|
|
|
|
|
|
|
if @$stroke == 1 and not defined $stroke->[0][0];
|
|
2492
|
|
|
|
|
|
|
# Try to restart it on а corner (if present)
|
|
2493
|
0
|
|
|
|
|
0
|
my($i,$corner) = (-1, 2);
|
|
2494
|
0
|
|
|
|
|
0
|
while (++$i <= $#$stroke) {
|
|
2495
|
0
|
|
|
|
|
0
|
my($d,$prevd) = ($stroke->[$i][0], $stroke->[$i-1][0]); # At i=0, wraps back to the end
|
|
2496
|
0
|
0
|
|
|
|
0
|
last if abs((($d-$prevd) % 8) - 4) <= 2; # 135° angle is not a corner
|
|
2497
|
|
|
|
|
|
|
}
|
|
2498
|
0
|
0
|
|
|
|
0
|
$i = $corner = 0 if $i > $#$stroke;
|
|
2499
|
0
|
0
|
|
|
|
0
|
$stroke = [@$stroke[$i..$#$stroke, 0..($i-1)]] if $i;
|
|
2500
|
0
|
|
|
|
|
0
|
my($breaks, $runs) = [0];
|
|
2501
|
0
|
0
|
|
|
|
0
|
if ($corner == 0) { # loop known to be smooth; stroke_2_strokes() won't find anything except ends
|
|
2502
|
0
|
|
|
|
|
0
|
$runs = [[0],[$#$stroke+1]]; # fake corners at ends; [0] means: start at 0, no calculated lines until the next
|
|
2503
|
|
|
|
|
|
|
} else {
|
|
2504
|
0
|
|
|
|
|
0
|
($runs, $breaks) = stroke_2_strokes($stroke, \%inCalcEdge, 'closed'); # Meaning: $runs->[$break] starts a new sub-stroke
|
|
2505
|
|
|
|
|
|
|
}
|
|
2506
|
|
|
|
|
|
|
# if $is_blob, we do not want to break loops; so the first element is reset to 0
|
|
2507
|
0
|
|
|
|
|
0
|
push @strokes, [$corner - 1, $is_blob, $stroke, $runs, $breaks]; # loop: 1 if have corners, -1 if smooth
|
|
2508
|
|
|
|
|
|
|
}
|
|
2509
|
|
|
|
|
|
|
# return if $opt{marked} and not ($marked and $marked2);
|
|
2510
|
2
|
|
|
|
|
7
|
for my $e (@$longedges) { # [$x, $y, $x+$dx+$dx1,$y+$dy+$dy1, $offset, $dir, $rot]
|
|
2511
|
0
|
0
|
0
|
|
|
0
|
next if not ref $e and $e eq 'erased';
|
|
2512
|
0
|
|
|
|
|
0
|
push @strokes, [0, !'blob', [[-20, @$e[0..3]]]]; # dir==-20
|
|
2513
|
|
|
|
|
|
|
}
|
|
2514
|
|
|
|
|
|
|
#warn "found strokes: ", scalar @strokes, "\n";
|
|
2515
|
|
|
|
|
|
|
# warn($edge->[8][7][5] ? "### <$edge->[8][7][5]>" : "###### not yet");
|
|
2516
|
2
|
|
|
|
|
36
|
[\@strokes, \@nextEdgeBlob, \@entryPointBlob, \%inCalcEdge];
|
|
2517
|
|
|
|
|
|
|
}
|
|
2518
|
|
|
|
|
|
|
|
|
2519
|
|
|
|
|
|
|
1;
|
|
2520
|
|
|
|
|
|
|
|
|
2521
|
|
|
|
|
|
|
__END__
|