File Coverage

blib/lib/LaTeX/TikZ/Formatter.pm
Criterion Covered Total %
statement 66 78 84.6
branch 12 16 75.0
condition 1 3 33.3
subroutine 16 17 94.1
pod 6 7 85.7
total 101 121 83.4


line stmt bran cond sub pod time code
1             package LaTeX::TikZ::Formatter;
2              
3 10     10   33 use strict;
  10         12  
  10         248  
4 10     10   33 use warnings;
  10         11  
  10         295  
5              
6             =head1 NAME
7              
8             LaTeX::TikZ::Formatter - LaTeX::TikZ formatter object.
9              
10             =head1 VERSION
11              
12             Version 0.03
13              
14             =cut
15              
16             our $VERSION = '0.03';
17              
18             =head1 DESCRIPTION
19              
20             A formatter object turns a L tree into the actual TikZ code, depending on some parameters such as the scale, the unit or the origin.
21              
22             =cut
23              
24 10     10   30 use Sub::Name ();
  10         7  
  10         112  
25              
26 10     10   2649 use LaTeX::TikZ::Point;
  10         22  
  10         261  
27              
28 10     10   53 use LaTeX::TikZ::Interface;
  10         11  
  10         144  
29              
30 10     10   32 use LaTeX::TikZ::Tools;
  10         10  
  10         107  
31              
32 10     10   27 use Mouse;
  10         12  
  10         33  
33 10     10   2102 use Mouse::Util::TypeConstraints;
  10         13  
  10         27  
34              
35             =head1 ATTRIBUTES
36              
37             =head2 C
38              
39             The unit in which lengths are printed.
40             Valid units are C for centimeters and C for points.
41              
42             Defaults to C.
43              
44             =cut
45              
46             has 'unit' => (
47             is => 'ro',
48             isa => enum([ qw ]),
49             default => 'cm',
50             );
51              
52             =head2 C
53              
54             The format used to print the numbers.
55              
56             Defaults to C<%s>.
57              
58             =cut
59              
60             has 'format' => (
61             is => 'ro',
62             isa => 'Str',
63             default => '%s',
64             );
65              
66             =head2 C
67              
68             The scale of the drawing.
69              
70             Defaults to C<1>.
71              
72             =cut
73              
74             has 'scale' => (
75             is => 'rw',
76             isa => 'Num',
77             default => 1,
78             );
79              
80             =head2 C
81              
82             The width of the drawing area.
83              
84             Defaults to C for none.
85              
86             =cut
87              
88             has 'width' => (
89             is => 'rw',
90             isa => 'Maybe[Num]',
91             );
92              
93             =head2 C
94              
95             The height of the drawing area.
96              
97             Defaults to C for none.
98              
99             =cut
100              
101             has 'height' => (
102             is => 'rw',
103             isa => 'Maybe[Num]',
104             );
105              
106             =head2 C
107              
108             A point coerced into a L object that represents the logical origin of the printed area.
109             If L and L are set, the canvas will be equivalent to a rectangle whose lower left corner at C<-$origin> and of given width and length.
110              
111             Defaults to C<(0, 0)>, meaning that the drawing area goes from C<(0, 0)> to C<($width, $height)>.
112              
113             =cut
114              
115             has 'origin' => (
116             is => 'rw',
117             isa => 'LaTeX::TikZ::Point::Autocoerce',
118             coerce => 1,
119             );
120              
121             =head1 METHODS
122              
123             =head2 C
124              
125             An unique identifier of the formatter object.
126              
127             =cut
128              
129             sub id {
130 17     17 1 15 my $tikz = shift;
131              
132 17         24 my $origin = $tikz->origin;
133 17 50       27 if (defined $origin) {
134 0         0 my ($x, $y) = map $origin->$_, qw;
135 0         0 $origin = "($x;$y)";
136             } else {
137 17         19 $origin = "(0;0)";
138             }
139              
140 102 100       163 join $;, map {
141 17         95 defined() ? "$_" : '(undef)';
142             } map($tikz->$_, qw), $origin;
143             }
144              
145             =head2 C
146              
147             my ($header_lines, $mod_lines, $content_lines) = $formatter->render(@sets);
148              
149             Processes all the L objects given in C<@sets> to produce the actual TikZ code to insert in the LaTeX file.
150             First, all the mods applied to the sets and their subsets are collected, and a declaration is emitted if needed for each of them by calling L.
151             Then, the image code is generated for each set.
152              
153             This method returns a list of array references :
154              
155             =over 4
156              
157             =item *
158              
159             The first one contains the header lines to include between the C<\documentclass> and the C<\begin{document}>.
160              
161             =item *
162              
163             The second one contains the mod declaration lines to put inside the document, between C<\begin{document}> and C<\end{document}>.
164              
165             =item *
166              
167             Finally, there's one array reference for each given TikZ set, which contain the lines for the actual TikZ pictures.
168              
169             =back
170              
171             The lines returned by L don't end with a line feed.
172              
173             my ($header, $declarations, $set1_body, $set2_body) = $formatter->render($set1, $set2);
174              
175             open my $tex, '>', 'test.tex' or die "open('>test.tex'): $!";
176              
177             print $tex "$_\n" for (
178             "\\documentclass[12pt]{article}",
179             @$header,
180             "\\begin{document}",
181             "\\pagestyle{empty}",
182             @$declarations,
183             "First set :"
184             "\\begin{center}",
185             @$set1_body,
186             "\\end{center}",
187             "Second set :"
188             "\\begin{center}",
189             @$set2_body,
190             "\\end{center}",
191             "\\end{document}",
192             );
193              
194             =cut
195              
196             my $find_mods = do {
197             our %seen;
198              
199             my $find_mods_rec;
200             $find_mods_rec = do {
201 10     10   2173 no warnings 'recursion';
  10         17  
  10         6198  
202              
203             Sub::Name::subname('find_mods_rec' => sub {
204             my ($set, $layers, $others) = @_;
205              
206             for ($set->mods) {
207             my $tag = $_->tag;
208             next if $seen{$tag}++;
209              
210             if ($_->isa('LaTeX::TikZ::Mod::Layer')) {
211             push @$layers, $_;
212             } else {
213             push @$others, $_;
214             }
215             }
216              
217             my @subsets = $set->does('LaTeX::TikZ::Set::Container')
218             ? $set->kids
219             : ();
220              
221             $find_mods_rec->($_, $layers, $others) for @subsets;
222             });
223             };
224              
225             Sub::Name::subname('find_mods' => sub {
226             local %seen = ();
227              
228             $find_mods_rec->(@_);
229             });
230             };
231              
232             my $translate;
233              
234             sub render {
235 112     112 1 42317 my ($tikz, @sets) = @_;
236              
237 112 100       309 unless ($translate) {
238 8         53 require LaTeX::TikZ::Functor;
239             $translate = LaTeX::TikZ::Functor->new(
240             rules => [
241             'LaTeX::TikZ::Set::Point' => sub {
242 11     11   10 my ($functor, $set, $v) = @_;
243              
244 11         22 $set->new(
245             point => [
246             $set->x + $v->x,
247             $set->y + $v->y,
248             ],
249             label => $set->label,
250             pos => $set->pos,
251             );
252             },
253 8         100 ],
254             );
255             }
256              
257 112         298 my $origin = $tikz->origin;
258 112 100       206 if (defined $origin) {
259 1         6 @sets = map $_->$translate($origin), @sets;
260             }
261              
262 112         113 my (@layers, @other_mods);
263 112         911 my $seq = LaTeX::TikZ::Set::Sequence->new(kids => \@sets);
264 112         5682 $find_mods->($seq, \@layers, \@other_mods);
265              
266 112         279 my $w = $tikz->width;
267 112         204 my $h = $tikz->height;
268 112         130 my $canvas = '';
269 112 50 33     271 if (defined $w and defined $h) {
270 0         0 require LaTeX::TikZ::Set::Rectangle;
271 0         0 for (@sets) {
272 0         0 $_->clip(LaTeX::TikZ::Set::Rectangle->new(
273             from => 0,
274             width => $w,
275             height => $h,
276             ));
277             }
278 0         0 $_ = $tikz->len($_) for $w, $h;
279 0         0 $canvas = ",papersize={$w,$h},body={$w,$h}";
280             }
281              
282 112         313 my @header = (
283             "\\usepackage[pdftex,hcentering,vcentering$canvas]{geometry}",
284             "\\usepackage{tikz}",
285             "\\usetikzlibrary{patterns}",
286             );
287              
288 112         115 my @decls;
289 112 100       250 push @decls, LaTeX::TikZ::Mod::Layer->declare(@layers) if @layers;
290 112         317 push @decls, $_->declare($tikz) for @other_mods;
291              
292 112         276 my @bodies = map [
293             "\\begin{tikzpicture}",
294 112         184 @{ $_->draw($tikz) },
295             "\\end{tikzpicture}",
296             ], @sets;
297              
298 111         829 return \@header, \@decls, @bodies;
299             }
300              
301             =head2 C
302              
303             my $physical_len = $formatter->len($logical_len);
304              
305             Format the given length according to the formatter options.
306              
307             =cut
308              
309             sub len {
310 265     265 1 232 my ($tikz, $len) = @_;
311              
312 265 100       378 $len = 0 if LaTeX::TikZ::Tools::numeq($len, 0);
313              
314 265         1251 sprintf $tikz->format . $tikz->unit, $len * $tikz->scale;
315             }
316              
317             =head2 C
318              
319             my $physical_angle = $formatter->angle($logical_angle);
320              
321             Format the given angle (in radians) according to the formatter options.
322              
323             =cut
324              
325             sub angle {
326 0     0 1 0 my ($tikz, $a) = @_;
327              
328 0         0 $a = ($a * 180) / CORE::atan2(0, -1);
329 0 0       0 $a += 360 if LaTeX::TikZ::Tools::numcmp($a, 0) < 0;
330              
331 0         0 require POSIX;
332 0         0 sprintf $tikz->format, POSIX::ceil($a);
333             }
334              
335             =head2 C
336              
337             my $label = $formatter->label($name, $pos);
338              
339             Returns the TikZ code for a point labeled C<$name> at position C<$pos> according to the formatter options.
340              
341             =cut
342              
343             sub label {
344 2     2 1 2 my ($tikz, $name, $pos) = @_;
345              
346 2         7 my $scale = sprintf '%0.2f', $tikz->scale / 5;
347              
348 2         6 "node[scale=$scale,$pos] {$name}";
349             }
350              
351             =head2 C
352              
353             Format the given line thickness according to the formatter options.
354              
355             =cut
356              
357             sub thickness {
358 61     61 1 75 my ($tikz, $width) = @_;
359              
360             # width=1 is 0.4 points for a scale of 2.5
361 61         824 0.8 * $width * ($tikz->scale / 5);
362             }
363              
364             LaTeX::TikZ::Interface->register(
365             formatter => sub {
366 10     10 0 1760 shift;
367              
368 10         208 __PACKAGE__->new(@_);
369             },
370             );
371              
372             __PACKAGE__->meta->make_immutable;
373              
374             =head1 SEE ALSO
375              
376             L.
377              
378             =head1 AUTHOR
379              
380             Vincent Pit, C<< >>, L.
381              
382             You can contact me by mail or on C (vincent).
383              
384             =head1 BUGS
385              
386             Please report any bugs or feature requests to C, or through the web interface at L.
387             I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
388              
389             =head1 SUPPORT
390              
391             You can find documentation for this module with the perldoc command.
392              
393             perldoc LaTeX::TikZ
394              
395             =head1 COPYRIGHT & LICENSE
396              
397             Copyright 2010,2011,2012,2013,2014,2015 Vincent Pit, all rights reserved.
398              
399             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
400              
401             =cut
402              
403             1; # End of LaTeX::TikZ::Formatter