]> git.vpit.fr Git - perl/modules/LaTeX-TikZ.git/commitdiff
Fix layer folding
authorVincent Pit <vince@profvince.com>
Sun, 18 Jul 2010 22:32:25 +0000 (00:32 +0200)
committerVincent Pit <vince@profvince.com>
Sun, 18 Jul 2010 22:32:25 +0000 (00:32 +0200)
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.

MANIFEST
lib/LaTeX/TikZ/Mod/Layer.pm
lib/LaTeX/TikZ/Scope.pm
lib/LaTeX/TikZ/Set.pm
t/21-layer.t [new file with mode: 0644]

index 5f168c043eb24acf4983ec36ff38711f134aeca0..d765e1dd695c02aa7a58ec59603d47768c2fcffa 100644 (file)
--- 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
index ca0b2bd6cc026ae40014d422dbe45317009b1579..f1f0daf472defc03daf8874415b2e735ae579def 100644 (file)
@@ -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(
index c07225962824d95eb027c538d991c3e09b18c79d..c048a1c700fdbd9bcba99b84d990d30f84d54b1a 100644 (file)
@@ -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);
index e719643a3a7fea5d64c81efd4cd873da5ee2fd4e..655eb2fc2470f56605a271cfb8e49caa7e700614 100644 (file)
@@ -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 (file)
index 0000000..6aaafd6
--- /dev/null
@@ -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
+