]> git.vpit.fr Git - perl/modules/LaTeX-TikZ.git/blob - lib/LaTeX/TikZ/Mod/Layer.pm
Globally store layer objects as weak references
[perl/modules/LaTeX-TikZ.git] / lib / LaTeX / TikZ / Mod / Layer.pm
1 package LaTeX::TikZ::Mod::Layer;
2
3 use strict;
4 use warnings;
5
6 =head1 NAME
7
8 LaTeX::TikZ::Mod::Layer - A modifier that specifies a drawing layer.
9
10 =head1 VERSION
11
12 Version 0.01
13
14 =cut
15
16 our $VERSION = '0.01';
17
18 use Scalar::Util ();
19 use List::Util   ();
20
21 use LaTeX::TikZ::Mod::Formatted;
22
23 use Any::Moose;
24 use Any::Moose 'Util::TypeConstraints';
25
26 with 'LaTeX::TikZ::Mod';
27
28 has 'name' => (
29  is       => 'ro',
30  isa      => 'Str',
31  required => 1,
32 );
33
34 subtype 'LaTeX::TikZ::Mod::LevelList'
35      => as 'ArrayRef[LaTeX::TikZ::Mod::Layer]';
36
37 coerce 'LaTeX::TikZ::Mod::LevelList'
38     => from 'Str'
39     => via { [ __PACKAGE__->new(name => $_) ] };
40
41 coerce 'LaTeX::TikZ::Mod::LevelList'
42     => from 'ArrayRef[Str]'
43     => via { [ map __PACKAGE__->new(name => $_), @$_ ] };
44
45 has '_above' => (
46  is       => 'ro',
47  isa      => 'LaTeX::TikZ::Mod::LevelList',
48  init_arg => 'above',
49  default  => sub { [ ] },
50  coerce   => 1,
51 );
52
53 sub above { @{$_[0]->_above} }
54
55 has '_below' => (
56  is       => 'ro',
57  isa      => 'LaTeX::TikZ::Mod::LevelList',
58  init_arg => 'below',
59  default  => sub { [ ] },
60  coerce   => 1,
61 );
62
63 sub below { @{$_[0]->_below} }
64
65 has '_score' => (
66  is       => 'ro',
67  isa      => 'Int',
68  init_arg => undef,
69  lazy     => 1,
70  builder  => '_build_score',
71 );
72
73 my %layers;
74
75 around 'new' => sub {
76  my ($orig, $self, %args) = @_;
77
78  my $name = $args{name};
79  if (defined $name) {
80   $self->meta->find_attribute_by_name('name')
81              ->type_constraint->assert_valid($name);
82   my $layer = $layers{$name};
83   if (defined $layer) {
84    confess("Can't redefine layer '$name'") if keys(%args) > 1;
85    return $layer;
86   }
87  }
88
89  return $self->$orig(%args);
90 };
91
92 sub BUILD {
93  my ($self) = @_;
94
95  my $name = $self->name;
96  $layers{$name} = $self;
97  Scalar::Util::weaken($layers{$name});
98 }
99
100 sub DEMOLISH {
101  my ($self) = @_;
102
103  delete $layers{$self->name};
104 }
105
106 sub tag {
107  my ($self) = @_;
108
109  ref($self) . '/' . $self->name;
110 }
111
112 sub cover { $_[0]->name eq $_[1]->name }
113
114 {
115  our %score;
116
117  sub score {
118   my $layer = $_[0];
119
120   my $name = $layer->name;
121
122   return $score{$name} if exists $score{$name};
123
124   my (@lower, $min);
125   for ($layer->above) {
126    my $cur = $_->score;
127    if (defined $cur) {
128     $min = $cur if not defined $min or $min < $cur;
129    } else {
130     push @lower, $_;
131    }
132   }
133
134   my (@higher, $max);
135   for ($layer->below) {
136    my $cur = $_->score;
137    if (defined $cur) {
138     $max = $cur if not defined $max or $max < $cur;
139    } else {
140     push @higher, $_;
141    }
142   }
143
144   if (defined $min) {
145    if (defined $max) {
146     confess("Order mismatch for $name") unless $min < $max;
147     $score{$name} = ($min + $max) / 2;
148    } else {
149     my $i = List::Util::max(values %score);
150     $score{$_} = ++$i for $name, @higher;
151    }
152   } elsif (defined $max) {
153    my $i = List::Util::min(values %score);
154    $score{$_} = --$i for @lower, $name;
155   } else {
156    my $i = 0;
157    $score{$_} = ++$i for @lower, $name, @higher;
158   }
159
160   $score{$name}
161  }
162
163  sub declare {
164   shift;
165
166   return unless @_;
167
168   local %score = (main => 0);
169
170   $_->score for @_;
171
172   my @layers = sort { $score{$a} <=> $score{$b} }
173                 map { ref() ? $_->name : $_ }
174                  keys %score;
175
176   my @intro = map "\\pgfdeclarelayer{$_}",
177                grep $_ ne 'main',
178                 @layers;
179
180   return (
181    @intro,
182    "\\pgfsetlayers{" . join(',', @layers) . "}",
183   );
184  }
185 }
186
187 sub apply {
188  my ($self) = @_;
189
190  LaTeX::TikZ::Mod::Formatted->new(
191   type    => 'layer',
192   content => $self->name,
193  )
194 }
195
196 use LaTeX::TikZ::Interface layer => sub {
197  shift;
198
199  my $name = shift;
200  __PACKAGE__->new(name => $name, @_);
201 };
202
203 __PACKAGE__->meta->make_immutable(
204  inline_constructor => 0,
205 );
206
207 =head1 AUTHOR
208
209 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
210
211 You can contact me by mail or on C<irc.perl.org> (vincent).
212
213 =head1 BUGS
214
215 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>.
216 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
217
218 =head1 SUPPORT
219
220 You can find documentation for this module with the perldoc command.
221
222     perldoc LaTeX::TikZ
223
224 =head1 COPYRIGHT & LICENSE
225
226 Copyright 2010 Vincent Pit, all rights reserved.
227
228 This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
229
230 =cut
231
232 1; # End of LaTeX::TikZ::Mod::Layer