File Coverage

blib/lib/LaTeX/TikZ/Scope.pm
Criterion Covered Total %
statement 82 82 100.0
branch 29 30 96.6
condition 14 15 93.3
subroutine 11 11 100.0
pod 4 4 100.0
total 140 142 98.5


line stmt bran cond sub pod time code
1             package LaTeX::TikZ::Scope;
2              
3 10     10   36 use strict;
  10         12  
  10         274  
4 10     10   36 use warnings;
  10         13  
  10         314  
5              
6             =head1 NAME
7              
8             LaTeX::TikZ::Scope - An object modeling a TikZ scope or layer.
9              
10             =head1 VERSION
11              
12             Version 0.03
13              
14             =cut
15              
16             our $VERSION = '0.03';
17              
18 10     10   65 use Sub::Name ();
  10         11  
  10         105  
19              
20 10     10   31 use LaTeX::TikZ::Tools;
  10         11  
  10         140  
21              
22 10     10   33 use Mouse;
  10         10  
  10         40  
23              
24             =head1 ATTRIBUTES
25              
26             =head2 C
27              
28             =cut
29              
30             has '_mods' => (
31             is => 'rw',
32             isa => 'Maybe[ArrayRef[LaTeX::TikZ::Mod::Formatted]]',
33             init_arg => 'mods',
34             default => sub { [ ] },
35             );
36              
37 350     350 1 293 sub mods { @{$_[0]->_mods} }
  350         945  
38              
39             has '_mods_cache' => (
40             is => 'ro',
41             isa => 'Maybe[HashRef[LaTeX::TikZ::Mod::Formatted]]',
42             init_arg => undef,
43             default => sub { +{ } },
44             );
45              
46             =head2 C
47              
48             =cut
49              
50             has 'body' => (
51             is => 'ro',
52             isa => 'ArrayRef[Str]',
53             required => 1,
54             init_arg => 'body',
55             );
56              
57             my $my_tc = LaTeX::TikZ::Tools::type_constraint(__PACKAGE__);
58             my $ltmf_tc = LaTeX::TikZ::Tools::type_constraint('LaTeX::TikZ::Mod::Formatted');
59             my $body_tc = __PACKAGE__->meta->find_attribute_by_name('body')
60             ->type_constraint;
61              
62             around 'BUILDARGS' => sub {
63             my ($orig, $class, %args) = @_;
64              
65             my $mods = $args{mods};
66             if (defined $mods and ref $mods eq 'ARRAY') {
67             for my $mod (@$mods) {
68             $mod = $ltmf_tc->coerce($mod);
69             }
70             }
71              
72             my $body = $args{body};
73             if ($my_tc->check($body)) {
74             push @$mods, $body->mods;
75             $args{body} = $body->body;
76             }
77              
78             $args{mods} = $mods;
79              
80             $class->$orig(%args);
81             };
82              
83             sub BUILD {
84 195     195 1 256 my $scope = shift;
85              
86 195         312 my $cache = $scope->_mods_cache;
87              
88 195         171 my @unique_mods;
89 195         282 for my $mod ($scope->mods) {
90 232         510 my $tag = $mod->tag;
91 232 100       504 next if exists $cache->{$tag};
92 228         385 $cache->{$tag} = $mod;
93 228         462 push @unique_mods, $mod;
94             }
95 195         986 $scope->_mods(\@unique_mods);
96             }
97              
98             =head1 METHODS
99              
100             =cut
101              
102             my $inter = Sub::Name::subname('inter' => sub {
103             my ($lh, $rh) = @_;
104              
105             my (@left, @common, @right);
106             my %where;
107              
108             --$where{$_} for keys %$lh;
109             ++$where{$_} for keys %$rh;
110              
111             while (my ($key, $where) = each %where) {
112             if ($where < 0) {
113             push @left, $lh->{$key};
114             } elsif ($where > 0) {
115             push @right, $rh->{$key};
116             } else {
117             push @common, $rh->{$key};
118             }
119             }
120              
121             return \@left, \@common, \@right;
122             });
123              
124             =head2 C
125              
126             =cut
127              
128             sub instantiate {
129 145     145 1 153 my ($scope) = @_;
130              
131 145         130 my ($layer, @clips, @raw_mods);
132 145         254 for ($scope->mods) {
133 164         267 my $type = $_->type;
134 164 100       357 if ($type eq 'clip') {
    100          
135 38         87 unshift @clips, $_->content;
136             } elsif ($type eq 'layer') {
137 23 50       33 confess("Can't apply two layers in a row") if defined $layer;
138 23         49 $layer = $_->content;
139             } else { # raw
140 103         298 push @raw_mods, $_->content;
141             }
142             }
143              
144 145         179 my @body = @{$scope->body};
  145         349  
145              
146 145 100       360 my $mods_string = @raw_mods ? ' [' . join(',', @raw_mods) . ']' : undef;
147              
148 145 100 100     815 if (@raw_mods and @body == 1 and $body[0] =~ /^\s*\\draw\b\s*([^\[].*)\s*$/) {
      66        
149 42         151 $body[0] = "\\draw$mods_string $1"; # Has trailing semicolon
150 42         56 $mods_string = undef; # Done with mods
151             }
152              
153 145         308 for (0 .. $#clips) {
154 38         35 my $clip = $clips[$_];
155 38         56 my $clip_string = "\\clip $clip ;";
156 38 100 100     100 my $mods_string = ($_ == $#clips and defined $mods_string)
157             ? $mods_string : '';
158 38         60 unshift @body, "\\begin{scope}$mods_string",
159             $clip_string;
160 38         58 push @body, "\\end{scope}",
161             }
162              
163 145 100 100     519 if (not @clips and defined $mods_string) {
164 37         87 unshift @body, "\\begin{scope}$mods_string";
165 37         50 push @body, "\\end{scope}";
166             }
167              
168 145 100       271 if (defined $layer) {
169 23         31 unshift @body, "\\begin{pgfonlayer}{$layer}";
170 23         23 push @body, "\\end{pgfonlayer}";
171             }
172              
173 145         923 return @body;
174             }
175              
176             =head2 C
177              
178             =cut
179              
180             sub fold {
181 139     139 1 158 my ($left, $right, $rev) = @_;
182              
183 139         110 my (@left, @right);
184              
185 139 100       419 if ($my_tc->check($left)) {
186              
187 75 100       187 if ($my_tc->check($right)) {
188              
189 57         161 my ($only_left, $common, $only_right) = $inter->(
190             $left->_mods_cache,
191             $right->_mods_cache,
192             );
193              
194 57         58 my $has_different_layers;
195 57         105 for (@$only_left, @$only_right) {
196 52 100       181 if ($_->type eq 'layer') {
197 6         6 $has_different_layers = 1;
198 6         7 last;
199             }
200             }
201              
202 57 100 100     248 if (!$has_different_layers and @$common) {
203 20         119 my $x = $left->new(
204             mods => $only_left,
205             body => $left->body,
206             );
207 20         128 my $y = $left->new(
208             mods => $only_right,
209             body => $right->body,
210             );
211 20         74 return $left->new(
212             mods => $common,
213             body => fold($x, $y, $rev),
214             );
215             } else {
216 37         85 @right = $right->instantiate;
217             }
218             } else {
219 18         48 $body_tc->assert_valid($right);
220 18         108 @right = @$right;
221             }
222              
223 55         115 @left = $left->instantiate;
224             } else {
225 64 100       152 if ($my_tc->check($right)) {
226 12         34 return fold($right, $left, 1);
227             } else {
228 52         159 $body_tc->assert_valid($_) for $left, $right;
229 52         527 @left = @$left;
230 52         85 @right = @$right;
231             }
232             }
233              
234 107 100       615 $rev ? [ @right, @left ] : [ @left, @right ];
235             }
236              
237             use overload (
238 53     53   123 '@{}' => sub { [ $_[0]->instantiate ] },
239 10     10   17441 );
  10         5560  
  10         70  
240              
241             __PACKAGE__->meta->make_immutable;
242              
243             =head1 SEE ALSO
244              
245             L.
246              
247             =head1 AUTHOR
248              
249             Vincent Pit, C<< >>, L.
250              
251             You can contact me by mail or on C (vincent).
252              
253             =head1 BUGS
254              
255             Please report any bugs or feature requests to C, or through the web interface at L.
256             I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
257              
258             =head1 SUPPORT
259              
260             You can find documentation for this module with the perldoc command.
261              
262             perldoc LaTeX::TikZ
263              
264             =head1 COPYRIGHT & LICENSE
265              
266             Copyright 2010,2011,2012,2013,2014,2015 Vincent Pit, all rights reserved.
267              
268             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
269              
270             =cut
271              
272             1; # End of LaTeX::TikZ::Scope