File Coverage

blib/lib/Games/Go/SGF2misc/SVG.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             # Games::Go::SGF2misc::SVG
2             #
3             # Author: Orien Vandenbergh
4             # $Id: SVG.pm,v 1.1.1.1 2004/05/10 20:52:19 orien Exp $
5             # vi: fdm=marker fdl=0
6              
7             package Games::Go::SGF2misc::SVG;
8              
9 1     1   8990 use 5.006;
  1         7  
  1         53  
10 1     1   7 use strict;
  1         3  
  1         49  
11 1     1   6 use warnings;
  1         7  
  1         37  
12              
13 1     1   1988 use Image::LibRSVG;
  0            
  0            
14             use XML::LibXML;
15              
16             require Exporter;
17              
18             our @ISA = qw(Exporter);
19             our %EXPORT_TAGS = ( 'all' => [qw( )] );
20             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
21             our @EXPORT = qw( );
22             our $VERSION = '1.00';
23              
24             # sub new() {{{
25             sub new() {
26             my $class = shift;
27             my $self = bless( { @_ }, $class );
28             $self->init();
29             return $self;
30             }
31             # }}}
32             # sub init() {{{
33             sub init() {
34             my ($self) = @_;
35             $self->{'imagesize'} = '3in' unless $self->{'imagesize'};
36             $self->{'boardsize'} = 19 unless $self->{'boardsize'};
37             $self->{'blocksize'} = 50;
38             $self->{'virtualsize'} = $self->{'blocksize'} * $self->{'boardsize'};
39              
40             $self->{'border'} = $self->{'blocksize'} / 2;
41             $self->{'stonesize'} = $self->{'border'} - 2;
42              
43             $self->{'gobanColor'} = '#eeb14b' unless $self->{'gobancolor'};
44             $self->{'whiteColor'} = 'white' unless $self->{'whitecolor'};
45             $self->{'blackColor'} = 'black' unless $self->{'blackcolor'};
46              
47             if (ref($self->{'gobanColor'}) eq 'ARRAY') {
48             $self->{'gobanColor'} = sprintf('rgb(%d,%d,%d)', @{$self->{'gobanColor'}});
49             }
50             if (ref($self->{'whiteColor'}) eq 'ARRAY') {
51             $self->{'whiteColor'} = sprintf('rgb(%d,%d,%d)', @{$self->{'gobanColor'}});
52             }
53             if (ref($self->{'blackColor'}) eq 'ARRAY') {
54             $self->{'blackColor'} = sprintf('rgb(%d,%d,%d)', @{$self->{'gobanColor'}});
55             }
56              
57             $self->{'document'} = XML::LibXML->createDocument(1.0, "UTF-8");
58             $self->{'svg'} = $self->{'document'}->createElement("svg");
59             $self->{'svg'}->setAttribute('xmlns','http://www.w3.org/2000/svg');
60             $self->{'svg'}->setAttribute('xmlns:xlink','http://www.w3.org/1999/xlink');
61             $self->{'svg'}->setAttribute('width',$self->{'imagesize'});
62             $self->{'svg'}->setAttribute('height',$self->{'imagesize'});
63             $self->{'svg'}->setAttribute('viewBox',sprintf('0 0 %d %d', $self->{'virtualsize'}, $self->{'virtualsize'}));
64             $self->{'svg'}->setAttribute('preserveAspectRatio','xMidYMid meet');
65             $self->{'document'}->setDocumentElement($self->{'svg'});
66              
67             my $node;
68             my $grad;
69             my $defs = $self->{'svg'}->addNewChild(undef,'defs');
70             $defs->setAttribute('id','defs00001');
71              
72             # Gradients {{{
73             $grad = $defs->addNewChild(undef,'linearGradient');
74             $grad->setAttribute('id','BlackLinearGradient');
75              
76             $node = $grad->addNewChild(undef,'stop');
77             $node->setAttribute('offset', '0');
78             $node->setAttribute('style', 'stop-color:#888888;stop-opacity:1;');
79              
80             $node = $grad->addNewChild(undef,'stop');
81             $node->setAttribute('offset', '1');
82             $node->setAttribute('style', 'stop-color:#000000;stop-opacity:1;');
83              
84             $grad = $defs->addNewChild(undef,'linearGradient');
85             $grad->setAttribute('id','WhiteLinearGradient');
86              
87             $node = $grad->addNewChild(undef,'stop');
88             $node->setAttribute('offset', '0');
89             $node->setAttribute('style', 'stop-color:#ffffff;stop-opacity:1;');
90              
91             $node = $grad->addNewChild(undef,'stop');
92             $node->setAttribute('offset', '1');
93             $node->setAttribute('style', 'stop-color:#dddddd;stop-opacity:1;');
94              
95             $node = $defs->addNewChild(undef,'radialGradient');
96             $node->setAttribute('id', 'BlackRadialGradient');
97             $node->setAttribute('cx', '0.36437908');
98             $node->setAttribute('cy', '0.335985');
99             $node->setAttribute('fx', '0.36437908');
100             $node->setAttribute('fy', '0.335985');
101             $node->setAttribute('r', '0.55236467');
102             $node->setAttribute('xlink:href','#BlackLinearGradient');
103              
104             $node = $defs->addNewChild(undef,'radialGradient');
105             $node->setAttribute('id', 'WhiteRadialGradient');
106             $node->setAttribute('cx', '0.36437908');
107             $node->setAttribute('cy', '0.335985');
108             $node->setAttribute('fx', '0.36437908');
109             $node->setAttribute('fy', '0.335985');
110             $node->setAttribute('r', '0.59236467');
111             $node->setAttribute('xlink:href','#WhiteLinearGradient');
112             # }}}
113              
114             $node = $defs->addNewChild(undef,'circle');
115             $node->setAttribute('id','Stone');
116             $node->setAttribute('r',$self->{'stonesize'});
117              
118             $node = $defs->addNewChild(undef,'circle');
119             $node->setAttribute('id', 'Hoshi');
120             $node->setAttribute('r',$self->{'blocksize'}/10);
121              
122             $node = $defs->addNewChild(undef,'circle');
123             $node->setAttribute('id', 'MarkerCircle');
124             $node->setAttribute('r',$self->{'stonesize'}/2);
125              
126             $node = $defs->addNewChild(undef,'rect');
127             $node->setAttribute('id', 'MarkerRectangle');
128             $node->setAttribute('x',0);
129             $node->setAttribute('y',0);
130             $node->setAttribute('width', $self->{'blocksize'}/2.5);
131             $node->setAttribute('height',$self->{'blocksize'}/2.5);
132             $node->setAttribute('transform',sprintf('translate(-%d,-%d)',$self->{'blocksize'}/5,$self->{'blocksize'}/5));
133              
134             my $pi = 3.1415927;
135             my $b = ($self->{'blocksize'}/2) * sin($pi/3);
136             my $c = ($self->{'blocksize'}/2) * cos($pi/3);
137              
138             $node = $defs->addNewChild(undef,'polygon');
139             $node->setAttribute('id', 'MarkerTriangle');
140             $node->setAttribute('points',sprintf('0,-%f, -%f,%f %f,%f', 2*($b/3), $c,($b/3), $c,($b/3)));
141             }
142             # }}}
143             # sub drawGoban() {{{
144             sub drawGoban() {
145             my ($self) = shift;
146              
147             my $node;
148             my $goban = $self->{'svg'}->addNewChild(undef,'g');
149             $goban->setAttribute('id','Goban');
150              
151             $node = $goban->addNewChild(undef,'rect');
152             $node->setAttribute('id','board');
153             $node->setAttribute('x','0');
154             $node->setAttribute('y','0');
155             $node->setAttribute('width',$self->{'virtualsize'});
156             $node->setAttribute('height',$self->{'virtualsize'});
157             $node->setAttribute('style',sprintf('fill:%s;',$self->{'gobanColor'}));
158            
159             $node = $goban->addNewChild(undef,'rect');
160             $node->setAttribute('id', 'border');
161             $node->setAttribute('x', $self->{'border'});
162             $node->setAttribute('y', $self->{'border'});
163             $node->setAttribute('width', $self->{'virtualsize'} - $self->{'blocksize'});
164             $node->setAttribute('height', $self->{'virtualsize'} - $self->{'blocksize'});
165             $node->setAttribute('style', 'stroke:black;stroke-width:3;fill-opacity:0;');
166              
167             foreach my $x (($self->{'border'} + $self->{'blocksize'})..$self->{'virtualsize'}-$self->{'blocksize'}) {
168             next if (($x-$self->{'border'}) % $self->{'blocksize'});
169             $node = $goban->addNewChild(undef,'line');
170             #$node->setAttribute('id', 'border');
171             $node->setAttribute('x1', $x);
172             $node->setAttribute('y1', $self->{'border'});
173             $node->setAttribute('x2', $x);
174             $node->setAttribute('y2', $self->{'virtualsize'} - $self->{'border'});
175             $node->setAttribute('style', 'stroke:black;stroke-width:1;');
176              
177             $node = $goban->addNewChild(undef,'line');
178             #$node->setAttribute('id', 'border');
179             $node->setAttribute('x1', $self->{'border'});
180             $node->setAttribute('y1', $x);
181             $node->setAttribute('x2', $self->{'virtualsize'} - $self->{'border'});
182             $node->setAttribute('y2', $x);
183             $node->setAttribute('style', 'stroke:black;stroke-width:1;');
184             }
185             my %hoshi = ( 5 => [[2,2] ],
186             7 => [[3,3] ],
187             9 => [[2,2], [6,2],
188             [4,4],
189             [2,6], [6,6] ],
190             11 => [[3,3], [7,3],
191             [5,5],
192             [3,7], [7,7] ],
193             13 => [[2,2], [6,2], [10,2],
194             [2,6], [6,6], [10,6],
195             [2,10], [6,10], [10,10] ],
196             19 => [[3,3], [9,3], [15,3],
197             [3,9], [9,9], [15,9],
198             [3,15], [9,15], [15,15] ],
199             );
200             foreach my $point (@{ $hoshi{$self->{'boardsize'}} }) {
201             my $x = $self->{'border'}+($self->{'blocksize'}*$point->[0]);
202             my $y = $self->{'border'}+($self->{'blocksize'}*$point->[1]);
203             $node = $goban->addNewChild(undef,'use');
204             #$node->setAttribute('id', 'border');
205             $node->setAttribute('x', $x);
206             $node->setAttribute('y', $y);
207             $node->setAttribute('xlink:href', '#Hoshi');
208             $node->setAttribute('style', 'fill:black;');
209             }
210             }
211             # }}}
212             # sub calcXY {{{
213             sub calcXY {
214             my $self = shift;
215             my ($pos) = @_;
216              
217             my ($x,$y);
218             if (ref($pos) eq 'ARRAY') {
219             ($x,$y) = @{ $pos };
220             } else {
221             $pos = lc($pos);
222             $pos =~ /([a-z])([a-z])/;
223             ($x,$y) = ((ord($1)-ord('a')),(ord($2)-ord('a')));
224             }
225              
226             return ($self->{'border'}+($self->{'blocksize'}*$x),$self->{'border'}+($self->{'blocksize'}*$y));
227             }
228             # }}}
229             # sub calcAA {{{
230             sub calcAA {
231             my $self = shift;
232             my ($pos) = @_;
233              
234             my ($x,$y);
235             if (ref($pos) eq 'ARRAY') {
236             ($x,$y) = @{ $pos };
237             $x = chr(ord('a')+$x);
238             $y = chr(ord('a')+$y);
239             return ($x.$y);
240             } else {
241             return lc($pos);
242             }
243             }
244             # }}}
245             # sub fetchGroup {{{
246             sub fetchGroup() {
247             my $self = shift;
248             my ($move) = @_;
249            
250             my $id = sprintf('pos_%s', $move);
251             my $node = $self->{'document'}->getElementsById($id);
252              
253             if (not defined $node) {
254             $node = $self->{'positions'}{$move};
255             }
256             if (not defined $node) {
257             $node = $self->{'svg'}->addNewChild(undef,'g');
258             $node->setAttribute('id',sprintf('pos_%s',$move));
259             $node->setAttribute('transform',sprintf('translate(%d,%d)',$self->calcXY($move)));
260             $self->{'positions'}{$move} = $node;
261             }
262              
263             return $node;
264             }
265             #}}}
266             # sub setLastMove {{{
267             sub setLastMove() {
268             my $self = shift;
269             my ($group) = @_;
270            
271             my $node = $self->{'document'}->getElementsById("LastMoveMarker");
272             if ($node) {
273             my $parent = $node->parentNode();
274             $parent->removeChild($node);
275             } else {
276             $node = $self->{'document'}->createElement('use');
277             $node->setAttribute('class','marker')
278             }
279             $group->addChild($node);
280              
281             return $node;
282             }
283             #}}}
284              
285             # sub placeStone {{{
286             sub placeStone {
287             my $self = shift;
288             my ($player, $move) = @_;
289            
290             my ($x,$y) = $self->calcXY($move);
291             my $loc = $self->calcAA($move);
292             my $group = $self->fetchGroup($loc);
293              
294             $group->setAttribute('transform',sprintf('translate(%d,%d)',$self->calcXY($move)));
295             my $node = $group->addNewChild(undef,'use');
296             $node->setAttribute('xlink:href','#Stone');
297             $node->setAttribute('class','stone');
298              
299             if ( $player =~ /b/i ) {
300             $group->setAttribute('class','black');
301             if ($self->{'useGradients'}) {
302             $node->setAttribute('style','fill:url(#BlackRadialGradient);stroke:black;stroke-width:1;');
303             } else {
304             $node->setAttribute('style','fill:black;stroke:black;stroke-width:1;');
305             }
306             } else {
307             $group->setAttribute('class','white');
308             if ($self->{'useGradients'}) {
309             $node->setAttribute('style','fill:url(#WhiteRadialGradient);stroke:black;stroke-width:1;');
310             } else {
311             $node->setAttribute('style','fill:white;stroke:black;stroke-width:1;');
312             }
313             }
314             }
315             # }}}
316             # sub addCircle {{{
317             sub addCircle {
318             my $self = shift;
319             my ($move) = @_;
320            
321             my $loc = $self->calcAA($move);
322             my $group = $self->fetchGroup($loc);
323              
324             my $node = $group->addNewChild(undef,'use');
325             $node->setAttribute('xlink:href','#MarkerCircle');
326             $node->setAttribute('class', 'marker');
327              
328             my @attr = $group->attributes();
329             my $color = 'white';
330             foreach my $attr(@attr) {
331             if (($attr->name() eq 'class') and ($attr->getValue() =~ /black/i)) {
332             $color = 'black';
333             }
334             }
335             if ($color eq 'black') {
336             $node->setAttribute('style','stroke:white;stroke-width:2;fill-opacity:0;');
337             } else {
338             $node->setAttribute('style','stroke:black;stroke-width:1;fill-opacity:0;');
339             }
340             }
341             # }}}
342             # sub addLetter {{{
343             sub addLetter {
344             my $self = shift;
345             my ($move,$text) = @_;
346            
347             my $loc = $self->calcAA($move);
348             my $group = $self->fetchGroup($loc);
349              
350             my $node = $group->addNewChild(undef,'text');
351             #$node->setAttribute('x','0');
352             $node->setAttribute('class', 'marker');
353             $node->setAttribute('text-anchor','middle');
354             $node->setAttribute('alignment-baseline','middle');
355              
356             $text =~ /\s*(\w{1,3})/;
357             my $letter = $1;
358              
359             $node->setAttribute('font-size', $self->{'blocksize'} * 0.5);
360             $node->setAttribute('y',$self->{'stonesize'}*0.46);
361            
362             $node->appendText($letter);
363              
364             my @attr = $group->attributes();
365             my $color = 'white';
366             foreach my $attr(@attr) {
367             if (($attr->name() eq 'class') and ($attr->getValue() =~ /black/i)) {
368             $color = 'black';
369             }
370             }
371             if ($color eq 'black') {
372             $node->setAttribute('fill', 'white');
373             #$node->setAttribute('style','stroke:white;stroke-width:2;fill-opacity:0;');
374             } else {
375             $node->setAttribute('fill', 'black');
376             #$node->setAttribute('style','stroke:black;stroke-width:1;fill-opacity:0;');
377             }
378             }
379             # }}}
380             # sub addSquare {{{
381             sub addSquare {
382             my $self = shift;
383             my ($move) = @_;
384            
385             my $loc = $self->calcAA($move);
386             my $group = $self->fetchGroup($loc);
387              
388             my $node = $group->addNewChild(undef,'use');
389             $node->setAttribute('xlink:href','#MarkerRectangle');
390             $node->setAttribute('class', 'marker');
391              
392             my @attr = $group->attributes();
393             my $color = 'white';
394             foreach my $attr(@attr) {
395             if (($attr->name() eq 'class') and ($attr->getValue() =~ /black/i)) {
396             $color = 'black';
397             }
398             }
399             if ($color eq 'black') {
400             $node->setAttribute('style','stroke:white;stroke-width:2;fill-opacity:0;');
401             } else {
402             $node->setAttribute('style','stroke:black;stroke-width:1;fill-opacity:0;');
403             }
404             }
405             # }}}
406             # sub addTriangle {{{
407             sub addTriangle {
408             my $self = shift;
409             my ($move) = @_;
410            
411             my $loc = $self->calcAA($move);
412             my $group = $self->fetchGroup($loc);
413              
414             my $node = $group->addNewChild(undef,'use');
415             $node->setAttribute('xlink:href','#MarkerTriangle');
416             $node->setAttribute('class', 'marker');
417              
418             my @attr = $group->attributes();
419             my $color = 'white';
420             foreach my $attr(@attr) {
421             if (($attr->name() eq 'class') and ($attr->getValue() =~ /black/i)) {
422             $color = 'black';
423             }
424             }
425             if ($color eq 'black') {
426             $node->setAttribute('style','stroke:white;stroke-width:2;fill-opacity:0;');
427             } else {
428             $node->setAttribute('style','stroke:black;stroke-width:1;fill-opacity:0;');
429             }
430             }
431             # }}}
432             # sub save {{{
433             sub save {
434             my $self = shift;
435             my ($filename) = @_;
436              
437             open IMG, ">$filename" or die "Unable to open $filename: $!, stopped ";
438              
439             print IMG $self->{'document'}->toString($self->{'pretty'});
440             close IMG;
441             }
442             # }}}
443             # sub export {{{
444             sub export {
445             my $self = shift;
446             my ($filename) = @_;
447              
448             my $rsvg = new Image::LibRSVG();
449              
450             $rsvg->loadImageFromString( $self->{'document'}->toString() );
451             $rsvg->saveAs($filename);
452             }
453             # }}}
454             # sub dump {{{
455             sub dump {
456             my $self = shift;
457             my ($format) = @_;
458              
459             $format = 'svg' unless $format;
460            
461             if ( $format =~ /png/i ) {
462             return undef;
463             # Image::LibRSVG doesn't support this yet.
464             my $rsvg = new Image::LibRSVG;
465             $rsvg->loadImageFromString( $self->{'document'}->toString() );
466             return $rsvg->getImageBitmap();
467             } else {
468             return $self->{'document'}->toString($self->{'pretty'});
469             }
470             }
471             # }}}
472              
473             __END__