Skip to content

Commit

Permalink
ADDED: integrate cross mode through zbuffer
Browse files Browse the repository at this point in the history
  • Loading branch information
qindapao committed Nov 25, 2023
1 parent 097f80c commit d426f93
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 163 deletions.
26 changes: 18 additions & 8 deletions documentation/mdbook_asciio/src/for_developers/cross_algorithm.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ then the steps are as follows:
5. update drawing.

# Detailed steps
## Find the cross point
## 1 Find the cross point

The cross point must be the coverage of characters,
and the characters that cover each other are the characters we care about.
Expand Down Expand Up @@ -66,9 +66,9 @@ because they absolutely determine what kind of cross point is generated.
diagonal crossing, you also need to record characters at 45 degrees,
135 degrees, 225 degrees, and 315 degrees.

## Judgment scene
## 2 Judgment scene

### General situation
### 2.1 General situation

First of all, all cross characters are directional, we need to group
them by direction and type.
Expand Down Expand Up @@ -135,7 +135,7 @@ For the `╤` sign, the condition for its appearance is that:
Pay attention to the bold part above, because if this condition is true,
then the filling character should be ``, not ``.

### Improve efficiency
### 2.2 Improve efficiency

There is no need to perform calculations every time to determine the characters
filled in the middle. Whenever a new scene calculation occurs, we can save the
Expand All @@ -146,7 +146,7 @@ A hash table is most suitable for this, and the hash key can use a combination
of upper, lower, left, and right characters(The oblique lines are the four diagonal
directions.).

### An additional case needs to be considered separately
### 2.3 An additional case needs to be considered separately

```
││
Expand Down Expand Up @@ -179,10 +179,11 @@ in the background is ok, so we think it ok!
**In summary:** If the character next to it is also an cross point, then
either the foreground character or the background character of the
intersection can meet the requirements.So I said earlier that the foreground
and background characters of the cross point all need to be recorded.
and background characters of the cross point all need to be recorded.In fact,
there may be more than one character that meets the condition in the background,
so all of these characters need to be considered, not just limited to 2 characters.


### The case of character coverage
### 2.4 The case of character coverage

There is another situation that needs to be explained separately,Similar to
above but slightly different
Expand Down Expand Up @@ -219,6 +220,15 @@ characters are considered.
>The definition of the cross point has already been mentioned in the
previous chapter

### 2.5 A method to simplify logical judgment

When we determine what characters should be filled in a point, we first consider
the characters in the four directions. Then after these characters are considered,
characters in three directions are considered, and finally characters in two directions
are considered. The advantage of this is that if the characters in the four directions
do not meet the conditions, then when judging the characters in the three directions,
there is no need to repeatedly consider that a certain character may be a character
in the four directions, because it has been ruled out before. Similar logic is used
for two-way characters. This can greatly reduce the complexity of logical judgment.


33 changes: 9 additions & 24 deletions documentation/mdbook_asciio/src/modes/cross.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Cross Mode

## Introduction
## 1 Introduction

In normal mode, elements boundaries are independent of each.

Expand All @@ -11,7 +11,7 @@ In cross mode intersections are merged:
![cross_elements](cross_elements.png)


## Complex graphics
## 2 Complex graphics

The cross-mode lets you create graphics like this table

Expand All @@ -31,32 +31,32 @@ The cross-mode lets you create graphics like this table
```

## Enabling cross-mode
## 3 Enabling cross-mode

### Globally
### 3.1 Globally

Add this line in your user configuration.

```perl
USE_CROSS_MODE => 1,
```

### Dynamically
### 3.2 Dynamically

Binding: «x» 'Switch cross mode'
Binding: «z-x» 'Switch cross mode'

## Line and Box
## 4 Line and Box

![cross_lines](cross_lines.gif)

![cross_boxs](cross_boxs.gif)

## Lines and boxes
## 5 Lines and boxes

![cross_box_line](cross_box_line.gif)


## Exported to text
## 6 Exported to text

```
.--------. ╭────────╮ ┏━━━━━━━━┓ ╔════════╗
Expand Down Expand Up @@ -97,19 +97,4 @@ Binding: «x» 'Switch cross mode'
```

## Excluded elements

Some elements do not need to cross, such as ellipse, so they can
be excluded in the configuration file.

```perl
CROSS_MODE_IGNORE =>
[
'App::Asciio::stripes::ellipse',
'App::Asciio::stripes::if_box'
],
```

Just write the full path of the element to be excluded.


5 changes: 4 additions & 1 deletion lib/App/Asciio/Ascii.pm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use strict; use warnings;
use App::Asciio::Cross ;
use App::Asciio::String ;
use App::Asciio::Markup ;
use App::Asciio::ZBuffer ;

use Readonly ;
Readonly my $EXPORT_PLAIN_TEXT => 0 ;
Expand Down Expand Up @@ -100,7 +101,9 @@ for my $element (@elements)
# If there is cross overlay, the characters of the cross need to be exported
if($self->{USE_CROSS_MODE})
{
for(App::Asciio::Cross::get_cross_mode_overlays($self))
my $zbuffer = App::Asciio::ZBuffer->new(1, @{$self->{ELEMENTS}}) ;

for(App::Asciio::Cross::get_cross_mode_overlays($zbuffer))
{
$lines[$_->[1]][$_->[0]] = [$_->[2]] if defined $lines[$_->[1]][$_->[0]] ;
}
Expand Down
167 changes: 60 additions & 107 deletions lib/App/Asciio/Cross.pm
Original file line number Diff line number Diff line change
Expand Up @@ -12,79 +12,6 @@ use Clone;
use List::Util qw(first) ;
use List::MoreUtils qw(any) ;

use App::Asciio::String ;
use App::Asciio::Markup ;


sub get_ascii_array_and_crossings
{
my ($asciio, $cross_filler_chars, $start_x, $end_x, $start_y, $end_y) = @_ ;

my (@lines, @cross_point_index) ;

for my $element (@{$asciio->{ELEMENTS}})
{
next if any { $_ eq ref($element) } @{$asciio->{CROSS_MODE_IGNORE}} ;

for my $strip (@{$element->get_stripes()})
{
my $line_index = -1 ;

for my $sub_strip (split("\n", $strip->{TEXT}))
{
$line_index++ ;

my $y = $element->{Y} + $strip->{Y_OFFSET} + $line_index ;

next if defined $start_y && ($y < $start_y || $y >= $end_y) ;

$sub_strip = $USE_MARKUP_CLASS->delete_markup_characters($sub_strip) ;

my $character_index = 0 ;

for my $character (split '', $sub_strip)
{
my $x = $element->{X} + $strip->{X_OFFSET} + $character_index ;

if((defined $start_x) && ($x < $start_x || $x >= $end_x))
{
# skip
}
elsif($x >= 0 && $y >= 0)
{
# keep the characters that may be crossing in the array
# other characters are discarded
if(exists $cross_filler_chars->{$character})
{
if(defined $lines[$y][$x])
{
push @{$lines[$y][$x]}, $character ;

push @cross_point_index, [$y, $x] ;
}
else
{
$lines[$y][$x] = [$character] ;
}

}
else
{
delete $lines[$y][$x] ;
}
}

$character_index += unicode_length($character);
}
}
}
}

@cross_point_index = grep { defined $lines[$_->[0]][$_->[1]][1] } @cross_point_index ;

return(\@lines, \@cross_point_index) ;
}

#-----------------------------------------------------------------------------
# ascii: + X . '
# unicode: ┼ ┤ ├ ┬ ┴ ╭ ╮ ╯ ╰ ╳ ... ...
Expand Down Expand Up @@ -113,24 +40,25 @@ my %all_cross_chars = map {$_, 1}
'', '', '', '', '', '',
'', '', '', '', '', '', '',
'', '', '', '', '', '', '',
'', '', '', '', '', '', '', ''
) ;

my %diagonal_cross_chars = map {$_, 1} ('\\', '/', '', '', '') ;

my %unicode_left_thin_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_right_thin_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_up_thin_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_down_thin_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_left_thin_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_right_thin_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_up_thin_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_down_thin_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;

my %unicode_left_double_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_right_double_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_up_double_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_down_double_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '') ;

my %unicode_left_bold_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_right_bold_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_up_bold_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_down_bold_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_left_bold_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_right_bold_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_up_bold_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;
my %unicode_down_bold_chars = map {$_, 1} ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '') ;

my @unicode_cross_chars = (
{%unicode_left_thin_chars} , {%unicode_left_double_chars} , {%unicode_left_bold_chars} ,
Expand Down Expand Up @@ -272,27 +200,58 @@ my @diagonal_char_func = (
['', \&scene_unicode_x],
) ;


sub get_cross_mode_overlays
{
my ($asciio, $start_x, $end_x, $start_y, $end_y) = @_;

my ($ascii_array, $crossings) = get_ascii_array_and_crossings($asciio, \%all_cross_chars, $start_x, $end_x, $start_y, $end_y);
my @ascii_array = @{$ascii_array} ;
my ($zbuffer, $start_x, $end_x, $start_y, $end_y) = @_;

my @overlays ;

for(@{$crossings})
return @overlays unless(defined $zbuffer->{intersecting_elements}) ;

my $cross_zbuffer = Clone::clone($zbuffer) ;

while (my ($coordinate, $char_stacks) = each %{ $cross_zbuffer->{intersecting_elements} })
{
my ($row, $col) = @{$_} ;

my ($up, $down, $left, $right) =
($ascii_array[$row-1][$col], $ascii_array[$row+1][$col], $ascii_array[$row][$col-1], $ascii_array[$row][$col+1]);
my $cross_array ;
my $char_count = 0 ;
for my $char (@$char_stacks)
{
last unless exists $all_cross_chars{$char};
push @{$cross_array}, $char;
$char_count++;
}
if($char_count >= 2)
{
$cross_zbuffer->{intersecting_elements}{$coordinate} = $cross_array ;
}
else
{
delete $cross_zbuffer->{intersecting_elements}{$coordinate} ;
}
}

while( my($coordinate, $elements) = each $cross_zbuffer->{intersecting_elements}->%*)
{
my ($Y, $X) = split ';', $coordinate ;

if(defined $start_x)
{
next if(($Y>$end_y) || ($Y<$start_y) || ($X>$end_x) || ($X<$start_x)) ;
}

my $neighbors_stack = $cross_zbuffer->get_neighbors_stack($coordinate) ;

my ($char_315, $up, $char_45, $right, $char_135, $down, $char_225, $left) =
($neighbors_stack->{($Y-1) . ';' . ($X-1)} // [$undef_char],
$neighbors_stack->{($Y-1) . ';' . $X} // [$undef_char],
$neighbors_stack->{($Y-1) . ';' . ($X+1)} // [$undef_char],
$neighbors_stack->{$Y . ';' . ($X+1)} // [$undef_char],
$neighbors_stack->{($Y+1) . ';' . ($X+1)} // [$undef_char],
$neighbors_stack->{($Y+1) . ';' . $X} // [$undef_char],
$neighbors_stack->{($Y+1) . ';' . ($X-1)} // [$undef_char],
$neighbors_stack->{$Y . ';' . ($X-1)} // [$undef_char]);

my $normal_key = ((defined $up) ? join('o', @{$up}) : $undef_char) . '_'
. ((defined $down) ? join('o', @{$down}) : $undef_char) . '_'
. ((defined $left) ? join('o', @{$left}) : $undef_char) . '_'
. ((defined $right) ? join('o', @{$right}) : $undef_char) ;
my $normal_key = join(join('o', @{$up}), '_', join('o', @{$down}), '_', join('o', @{$left}), '_', join('o', @{$right})) ;

unless(exists $normal_char_cache{$normal_key})
{
Expand All @@ -302,33 +261,27 @@ for(@{$crossings})

if($normal_char_cache{$normal_key})
{
if($normal_char_cache{$normal_key} ne $ascii_array[$row][$col][-1])
if($normal_char_cache{$normal_key} ne $elements->[0])
{
push @overlays, [$col, $row, $normal_char_cache{$normal_key}];
push @overlays, [$X, $Y, $normal_char_cache{$normal_key}];
}

next;
}

next unless exists $diagonal_cross_chars{$ascii_array[$row][$col][-1]} ;

my ($char_45, $char_135, $char_225, $char_315) =
($ascii_array[$row-1][$col+1], $ascii_array[$row+1][$col+1], $ascii_array[$row+1][$col-1], $ascii_array[$row-1][$col-1]);
next unless exists $diagonal_cross_chars{$elements->[0]} ;

my $diagonal_key = ((defined $char_45) ? join('o', @{$char_45}) : $undef_char) . '_'
. ((defined $char_135) ? join('o', @{$char_135}) : $undef_char) . '_'
. ((defined $char_225) ? join('o', @{$char_225}) : $undef_char) . '_'
. ((defined $char_315) ? join('o', @{$char_315}) : $undef_char) ;
my $diagonal_key = join(join('o', @{$char_45}), '_', join('o', @{$char_135}), '_', join('o', @{$char_225}), '_', join('o', @{$char_315})) ;

unless(exists $diagonal_char_cache{$diagonal_key})
{
my $scene_func = first { $_->[$FUNCTION]($char_45, $char_135, $char_225, $char_315) } @diagonal_char_func;
$diagonal_char_cache{$diagonal_key} = ($scene_func) ? $scene_func->[$CHARACTER] : '';
}

if($diagonal_char_cache{$diagonal_key} && ($diagonal_char_cache{$diagonal_key} ne $ascii_array[$row][$col][-1]))
if($diagonal_char_cache{$diagonal_key} && ($diagonal_char_cache{$diagonal_key} ne $elements->[0]))
{
push @overlays, [$col, $row, $diagonal_char_cache{$diagonal_key}];
push @overlays, [$X, $Y, $diagonal_char_cache{$diagonal_key}];
}

}
Expand Down
Loading

0 comments on commit d426f93

Please sign in to comment.