From: Vincent Pit Date: Sun, 18 Jul 2010 22:32:25 +0000 (+0200) Subject: Fix layer folding X-Git-Tag: v0.01~44 X-Git-Url: http://git.vpit.fr/?p=perl%2Fmodules%2FLaTeX-TikZ.git;a=commitdiff_plain;h=32fbc7c8acf36e81b3c3436152e664e79c74dc87 Fix layer folding We can't rely on the common mod folding optimization when both scopes have specific layers applied, because all mods need to be reapplied after the layer is enforced. Also test layers in t/21-layer.t. --- diff --git a/MANIFEST b/MANIFEST index 5f168c0..d765e1d 100644 --- a/MANIFEST +++ b/MANIFEST @@ -33,4 +33,5 @@ t/00-load.t t/01-api.t t/10-set.t t/20-mod.t +t/21-layer.t t/91-pod.t diff --git a/lib/LaTeX/TikZ/Mod/Layer.pm b/lib/LaTeX/TikZ/Mod/Layer.pm index ca0b2bd..f1f0daf 100644 --- a/lib/LaTeX/TikZ/Mod/Layer.pm +++ b/lib/LaTeX/TikZ/Mod/Layer.pm @@ -78,9 +78,11 @@ around 'new' => sub { if (defined $name) { $self->meta->find_attribute_by_name('name') ->type_constraint->assert_valid($name); - confess("Can't redefine layer '$name'") if keys(%args) > 1; my $layer = $layers{$name}; - return $layer if defined $layer; + if (defined $layer) { + confess("Can't redefine layer '$name'") if keys(%args) > 1; + return $layer; + } } return $self->$orig(%args); @@ -191,7 +193,8 @@ sub apply { use LaTeX::TikZ::API layer => sub { shift; - __PACKAGE__->new(name => $_[0]); + my $name = shift; + __PACKAGE__->new(name => $name, @_); }; __PACKAGE__->meta->make_immutable( diff --git a/lib/LaTeX/TikZ/Scope.pm b/lib/LaTeX/TikZ/Scope.pm index c072259..c048a1c 100644 --- a/lib/LaTeX/TikZ/Scope.pm +++ b/lib/LaTeX/TikZ/Scope.pm @@ -181,7 +181,23 @@ sub fold { $right->_mods_cache, ); - if (@$common) { + my $has_different_layers; + for (@$only_left) { + if ($_->type eq 'layer') { + $has_different_layers = 1; + last; + } + } + unless ($has_different_layers) { + for (@$only_right) { + if ($_->type eq 'layer') { + $has_different_layers = 1; + last; + } + } + } + + if (!$has_different_layers and @$common) { my $x = $left->new ->mod(@$only_left) ->body($left->_body); diff --git a/lib/LaTeX/TikZ/Set.pm b/lib/LaTeX/TikZ/Set.pm index e719643..655eb2f 100644 --- a/lib/LaTeX/TikZ/Set.pm +++ b/lib/LaTeX/TikZ/Set.pm @@ -58,23 +58,29 @@ sub mod { sub mods_unique { my ($set) = @_; - my (@mods, $has_layer); + my (@mods, $last_layer); MOD: for my $mod ($set->mods) { - $has_layer = 1 if $ltml_tc->check($mod); + my $is_layer = $ltml_tc->check($mod); + $last_layer = $mod if $is_layer; my $tag = $mod->tag; my $old = $mods{$tag} || []; for (@$old) { next MOD if $_->[0]->cover($mod); } - push @{$mods{$tag}}, [ $mod, $last_mod++ ]; + push @{$mods{$tag}}, [ $mod, $last_mod++, $is_layer ]; push @mods, $mod; } - if ($has_layer) { + if ($last_layer) { # Clips and mods don't propagate through layers. Hence if a layer is set, # force their reuse. - @mods = map $_->[0], sort { $a->[1] <=> $b->[1] } map @$_, values %mods; + @mods = $last_layer; + push @mods, map $_->[0], + sort { $a->[1] <=> $b->[1] } + grep !$_->[2], + map @$_, + values %mods; } return @mods; diff --git a/t/21-layer.t b/t/21-layer.t new file mode 100644 index 0000000..6aaafd6 --- /dev/null +++ b/t/21-layer.t @@ -0,0 +1,176 @@ +#!perl -T + +use strict; +use warnings; + +use Test::More tests => 9 + 3 * 6; + +use LaTeX::TikZ; + +my $tikz = Tikz->formatter( + format => '%d', +); + +sub check { + my ($set, $desc, $exp, $layers) = @_; + + local $Test::Builder::Level = $Test::Builder::Level + 1; + + my ($head, $decl, $body) = eval { + $tikz->render(ref $set eq 'ARRAY' ? @$set : $set); + }; + is $@, '', "$desc: no error"; + + unless (ref $exp eq 'ARRAY') { + $exp = [ split /\n/, $exp ]; + } + unshift @$exp, '\begin{tikzpicture}'; + push @$exp, '\end{tikzpicture}'; + + my $exp_decl = [ + map("\\pgfdeclarelayer{$_}", @$layers), + "\\pgfsetlayers{main,@{[join ',', @$layers]}}", + ]; + + is_deeply $decl, $exp_decl, "$desc: declarations"; + is_deeply $body, $exp, "$desc: body"; +} + +my $middle = eval { + Tikz->layer('middle'); +}; +is $@, '', 'creating a layer doesn\'t croak'; + +my $top = eval { + Tikz->layer( + 'top', + above => [ 'middle' ], + ); +}; +is $@, '', 'creating a layer above another doesn\'t croak'; + +my $bottom = eval { + Tikz->layer( + 'bottom', + above => [ 'main' ], + below => [ 'middle' ], + ); +}; +is $@, '', 'creating a layer above and below anothers doesn\'t croak'; + +my $foo = eval { + Tikz->raw('foo') + ->mod($middle) +}; +is $@, '', 'creating a layered raw set doesn\'t croak'; + +check $foo, 'one layered raw set', <<'RES', [ 'middle' ]; +\begin{pgfonlayer}{middle} +\draw foo ; +\end{pgfonlayer} +RES + +my $bar = eval { + Tikz->raw('bar') + ->mod($top) +}; +is $@, '', 'creating another layered raw set doesn\'t croak'; + +my $seq = Tikz->seq($foo, $bar); + +check $seq, 'a sequence of two layered raw sets', <<'RES', [ qw/middle top/ ]; +\begin{pgfonlayer}{middle} +\draw foo ; +\end{pgfonlayer} +\begin{pgfonlayer}{top} +\draw bar ; +\end{pgfonlayer} +RES + +sub failed_valid { + my ($tc) = @_; + qr/Validation failed for '\Q$tc\E'/; +} + +eval { + $seq->layer(sub { }); +}; +like $@, failed_valid('Str'), 'directly adding a wrong layer croaks'; + +eval { + $seq->layer($bottom); +}; +is $@, '', 'directly adding a layer to a sequence doesn\'t croak'; + +my $res = eval { + $seq->layer; +}; +is $@, '', 'calling an empty ->layer onto a sequence doesn\'t croak'; +is "$res", "$seq", 'empty ->layer returns the object itself'; + +check $seq, 'a layered sequence', <<'RES', [ qw/bottom middle top/ ]; +\begin{pgfonlayer}{bottom} +\begin{pgfonlayer}{middle} +\draw foo ; +\end{pgfonlayer} +\begin{pgfonlayer}{top} +\draw bar ; +\end{pgfonlayer} +\end{pgfonlayer} +RES + +my $baz = Tikz->raw('baz'); +$seq->add($baz); + +my $red = Tikz->color('red'); +$seq->mod($red); + +check $seq, 'mods folding with layers 1', <<'RES', [ qw/bottom middle top/ ]; +\begin{pgfonlayer}{bottom} +\begin{scope} [color=red] +\begin{pgfonlayer}{middle} +\draw [color=red] foo ; +\end{pgfonlayer} +\begin{pgfonlayer}{top} +\draw [color=red] bar ; +\end{pgfonlayer} +\draw baz ; +\end{scope} +\end{pgfonlayer} +RES + +$baz->mod($top); + +check $seq, 'mods folding with layers 2', <<'RES', [ qw/bottom middle top/ ]; +\begin{pgfonlayer}{bottom} +\begin{scope} [color=red] +\begin{pgfonlayer}{middle} +\draw [color=red] foo ; +\end{pgfonlayer} +\begin{pgfonlayer}{top} +\draw [color=red] bar ; +\end{pgfonlayer} +\begin{pgfonlayer}{top} +\draw [color=red] baz ; +\end{pgfonlayer} +\end{scope} +\end{pgfonlayer} +RES + +my $seq2 = Tikz->seq($bar, $baz, $foo) + ->mod($red); + +check $seq2, 'mods folding with layers 3', <<'RES', [ qw/middle top/ ]; +\begin{scope} [color=red] +\begin{pgfonlayer}{top} +\begin{scope} [color=red] +\draw bar ; +\draw baz ; +\end{scope} +\end{pgfonlayer} +\begin{pgfonlayer}{middle} +\draw [color=red] foo ; +\end{pgfonlayer} +\end{scope} +RES +