]> git.vpit.fr Git - perl/modules/LaTeX-TikZ.git/commitdiff
Abstract the mod antiduplication logic in a new context object
authorVincent Pit <vince@profvince.com>
Sun, 6 Feb 2011 21:15:58 +0000 (22:15 +0100)
committerVincent Pit <vince@profvince.com>
Sat, 12 Feb 2011 14:17:08 +0000 (15:17 +0100)
Scope::Guard is no longer needed.

MANIFEST
Makefile.PL
lib/LaTeX/TikZ.pm
lib/LaTeX/TikZ/Context.pm [new file with mode: 0644]
lib/LaTeX/TikZ/Set.pm
lib/LaTeX/TikZ/Set/Chain.pm
lib/LaTeX/TikZ/Set/Path.pm
t/00-load.t
t/92-pod-coverage.t

index bfb95b96da3d9f51138365d2efc1ada4d04b332a..14baa3853f894f93407862d9894259de4b24aae6 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -4,6 +4,7 @@ META.yml
 Makefile.PL
 README
 lib/LaTeX/TikZ.pm
+lib/LaTeX/TikZ/Context.pm
 lib/LaTeX/TikZ/Formatter.pm
 lib/LaTeX/TikZ/Functor.pm
 lib/LaTeX/TikZ/Functor/Rule.pm
index 705e233f2714de73787b60c4288ebc9d0a71fa8b..ff2e181389f9ab144831f21f0944074f12c4af5e 100644 (file)
@@ -19,7 +19,6 @@ my %PREREQ_PM = (
  'Math::Trig'    => 0,
  'Mouse'         => '0.80', # register_type_constraint + type constraint bug
  'Scalar::Util'  => 0,
- 'Scope::Guard'  => 0,
  'Sub::Name'     => 0,
  'Task::Weaken'  => 0,
  'constant'      => 0,
index 69a55c81ef7517b432c84232c7eb222984444121..7ba3a740e4a6106bece14645955f6110371c7aeb 100644 (file)
@@ -386,8 +386,6 @@ L<Any::Moose> with L<Mouse> 0.80 or greater.
 
 L<Sub::Name>.
 
-L<Scope::Guard>.
-
 L<Math::Complex>, L<Math::Trig>.
 
 L<Scalar::Util>, L<List::Util>, L<Task::Weaken>.
diff --git a/lib/LaTeX/TikZ/Context.pm b/lib/LaTeX/TikZ/Context.pm
new file mode 100644 (file)
index 0000000..5909cb8
--- /dev/null
@@ -0,0 +1,164 @@
+package LaTeX::TikZ::Context;
+
+use strict;
+use warnings;
+
+=head1 NAME
+
+LaTeX::TikZ::Context - An object modeling in which context a set is evaluated.
+
+=head1 VERSION
+
+Version 0.02
+
+=cut
+
+our $VERSION = '0.02';
+
+use LaTeX::TikZ::Mod (); # Required to work around a bug in Mouse
+
+use LaTeX::TikZ::Tools;
+
+use Any::Moose;
+
+=head1 ATTRIBUTES
+
+=head2 C<parent>
+
+The parent context of the current one, or C<undef> for the topmost context.
+
+=cut
+
+has 'parent' => (
+ is       => 'ro',
+ isa      => 'Maybe[LaTeX::TikZ::Context]',
+ required => 0,
+ default  => undef,
+);
+
+=head2 C<mods>
+
+The list of mods that are asked to be applied in this context.
+
+=cut
+
+has '_mods' => (
+ is       => 'ro',
+ isa      => 'ArrayRef[LaTeX::TikZ::Mod]',
+ required => 0,
+ default  => sub { [ ] },
+ init_arg => 'mods',
+);
+
+sub mods { @{$_[0]->_mods} }
+
+has '_applied_mods' => (
+ is       => 'ro',
+ isa      => 'HashRef[LaTeX::TikZ::Mod]',
+ required => 0,
+ default  => sub { { } },
+ init_arg => undef,
+);
+
+=head2 C<effective_mods>
+
+The list of mods that actually need to be applied in this context.
+
+=cut
+
+has '_effective_mods' => (
+ is       => 'ro',
+ isa      => 'ArrayRef[LaTeX::TikZ::Mod]',
+ required => 0,
+ default  => sub { [ ] },
+ init_arg => undef,
+);
+
+sub effective_mods { @{$_[0]->_effective_mods} }
+
+has '_last_mod' => (
+ is       => 'rw',
+ isa      => 'Int',
+ required => 0,
+ default  => 0,
+ init_arg => undef,
+);
+
+my $ltml_tc = LaTeX::TikZ::Tools::type_constraint('LaTeX::TikZ::Mod::Layer');
+
+sub BUILD {
+ my $cxt  = shift;
+ my $pcxt = $cxt->parent;
+
+ my $applied_mods = $cxt->_applied_mods;
+ for (my $c = $pcxt; defined $c; $c = $c->parent) {
+  my $mods = $c->_applied_mods;
+  while (my ($tag, $mods_info) = each %$mods) {
+   unshift @{$applied_mods->{$tag}}, @$mods_info;
+  }
+ }
+
+ my $last_mod       = defined $pcxt ? $pcxt->_last_mod : 0;
+ my $effective_mods = $cxt->_effective_mods;
+
+ my $last_layer;
+
+MOD:
+ for my $mod ($cxt->mods) {
+  my $is_layer = $ltml_tc->check($mod);
+  $last_layer  = $mod if $is_layer;
+
+  my $tag = $mod->tag;
+  my $old = $applied_mods->{$tag} || [];
+  for (@$old) {
+   next MOD if $_->[0]->covers($mod);
+  }
+
+  push @{$applied_mods->{$tag}}, [ $mod, $last_mod++, $is_layer ];
+  push @$effective_mods, $mod;
+ }
+
+ if ($last_layer) {
+  # Clips and mods don't propagate through layers. Hence, if a layer is set,
+  # we should force their reuse.
+  @$effective_mods = $last_layer;
+  push @$effective_mods, map $_->[0],
+                          sort { $a->[1] <=> $b->[1] }
+                           grep !$_->[2],
+                            map @$_,
+                             values %$applied_mods;
+ }
+
+ $cxt->_last_mod($last_mod);
+}
+
+=head1 SEE ALSO
+
+L<LaTeX::TikZ>.
+
+=head1 AUTHOR
+
+Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
+
+You can contact me by mail or on C<irc.perl.org> (vincent).
+
+=head1 BUGS
+
+Please report any bugs or feature requests to C<bug-latex-tikz at rt.cpan.org>, or through the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=LaTeX-TikZ>.
+I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command.
+
+    perldoc LaTeX::TikZ
+
+=head1 COPYRIGHT & LICENSE
+
+Copyright 2011 Vincent Pit, all rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=cut
+
+1; # End of LaTeX::TikZ::Context
index 4e6eafcab46dc282645010805e07fb4f03734b57..e673097ec2f20e7a9349e8cabff8cdef3cfb84c3 100644 (file)
@@ -15,8 +15,7 @@ Version 0.02
 
 our $VERSION = '0.02';
 
-use Scope::Guard ();
-
+use LaTeX::TikZ::Context;
 use LaTeX::TikZ::Scope;
 
 use LaTeX::TikZ::Tools;
@@ -49,9 +48,10 @@ This method is required by the interface :
 
 =item *
 
-C<draw $formatter>
+C<draw $formatter, $context>
 
 Returns an array reference of TikZ code lines required to effectively draw the current set object, formatted by the L<LaTeX::TikZ::Formatter> object C<$formatter>.
+The current evaluation context is passed as the L<LaTeX::TikZ::Context> object C<$context>.
 
 =back
 
@@ -82,63 +82,25 @@ sub mod {
  $set;
 }
 
-{
- our %mods;
- our $last_mod = 0;
-
- around 'draw' => sub {
-  my ($orig, $set, $tikz) = @_;
-
-  local $last_mod = $last_mod;
-
-  # Save a deep copy
-  my %saved_idx = map { $_ => $#{$mods{$_}} } keys %mods;
-  my $guard     = Scope::Guard->new(sub {
-   for (keys %mods) {
-    if (exists $saved_idx{$_}) {
-     $#{$mods{$_}} = $saved_idx{$_};
-    } else {
-     delete $mods{$_};
-    }
-   }
-  });
-
-  my (@mods, $last_layer);
-MOD:
-  for my $mod ($set->mods) {
-   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]->covers($mod);
-   }
-   push @{$mods{$tag}}, [ $mod, $last_mod++, $is_layer ];
-   push @mods,          $mod;
-  }
-
-  if ($last_layer) {
-   # Clips and mods don't propagate through layers. Hence if a layer is set,
-   # force their reuse.
-   @mods = $last_layer;
-   push @mods, map $_->[0],
-                sort { $a->[1] <=> $b->[1] }
-                 grep !$_->[2],
-                  map @$_,
-                   values %mods;
-  }
-
-  my $body = $set->$orig($tikz);
-
-  if (@mods) {
-   $body = LaTeX::TikZ::Scope->new
-                             ->mod(map $_->apply($tikz), @mods)
-                             ->body($body);
-  }
-
-  $body;
- };
-}
+around 'draw' => sub {
+ my ($orig, $set, $tikz, $pcxt) = @_;
+
+ my $cxt = LaTeX::TikZ::Context->new(
+  parent => $pcxt,
+  mods   => [ $set->mods ],
+ );
+
+ my $body = $set->$orig($tikz, $cxt);
+
+ my @mods = $cxt->effective_mods;
+ if (@mods) {
+  $body = LaTeX::TikZ::Scope->new
+                            ->mod(map $_->apply($tikz), @mods)
+                            ->body($body);
+ }
+
+ $body;
+};
 
 =head2 C<layer $layer>
 
index d1022f842d4527513b3b9745a6a83b8d233f7bdf..eb5395f5ef27bf3b6216d38499d10414cb9dcdaf 100644 (file)
@@ -152,7 +152,7 @@ sub add {
 =cut
 
 sub path {
- my ($set, $tikz) = @_;
+ my $set = shift;
 
  my @kids  = $set->kids;
  return '' unless @kids;
@@ -160,7 +160,7 @@ sub path {
  my $conn  = $set->connector;
 
  my $prev  = $kids[0];
- my $path  = $prev->path($tikz);
+ my $path  = $prev->path(@_);
 
  if ($set->cycle) {
   push @kids, LaTeX::TikZ::Set::Raw->new(
@@ -168,13 +168,14 @@ sub path {
   );
  }
 
+ my $tikz = $_[0];
  for my $i (1 .. $#kids) {
   my $next = $kids[$i];
   my $link = $set->$conn($i - 1, $prev, $next, $tikz);
   confess('Invalid connector') unless defined $link and not blessed $link;
   $link    = " $link ";
   $link    =~ s/\s+/ /g;
-  $path   .= $link . $next->path($tikz);
+  $path   .= $link . $next->path(@_);
   $prev    = $next;
  }
 
index 081f35bce312e2ffe30e353d737ef3954e76d56e..e681828eaa571e21cc241c46d3fe4206a0542145 100644 (file)
@@ -39,9 +39,10 @@ These methods are required by the interface :
 
 =item *
 
-C<path $formatter>
+C<path $formatter, $context>
 
 Returns the TikZ code that builds a path out of the current set object as a string formatted by the L<LaTeX::TikZ::Formatter> object C<$formatter>.
+The current evaluation context is passed as the L<LaTeX::TikZ::Context> object C<$context>.
 
 =item *
 
index e8ec3aa10205039ea5a0dedcb932cba5ecbe8207..1fa8c0f4789b26b05491133676d73a81373e7586 100644 (file)
@@ -3,10 +3,11 @@
 use strict;
 use warnings;
 
-use Test::More tests => 36;
+use Test::More tests => 37;
 
 BEGIN {
  use_ok( 'LaTeX::TikZ' );
+ use_ok( 'LaTeX::TikZ::Context' );
  use_ok( 'LaTeX::TikZ::Formatter' );
  use_ok( 'LaTeX::TikZ::Functor' );
  use_ok( 'LaTeX::TikZ::Functor::Rule' );
index bb340b63eea2b56c3818718386f559146612a084..49689882e7982464be52a9240e6ceda5bccdb9ac 100644 (file)
@@ -15,7 +15,7 @@ my $min_pc = 0.18;
 eval "use Pod::Coverage $min_pc";
 plan skip_all => "Pod::Coverage $min_pc required for testing POD coverage" if $@;
 
-plan tests => 36;
+plan tests => 37;
 
 my $moose_private = { also_private => [ qr/^BUILD$/, qr/^DEMOLISH$/ ] };
 
@@ -23,6 +23,7 @@ my $moose_private = { also_private => [ qr/^BUILD$/, qr/^DEMOLISH$/ ] };
 pod_coverage_ok( 'LaTeX::TikZ::Interface' );
 
 pod_coverage_ok( 'LaTeX::TikZ' );
+pod_coverage_ok( 'LaTeX::TikZ::Context', $moose_private);
 pod_coverage_ok( 'LaTeX::TikZ::Formatter' );
 pod_coverage_ok( 'LaTeX::TikZ::Functor' );
 pod_coverage_ok( 'LaTeX::TikZ::Functor::Rule' );