PageRenderTime 35ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/SVG-Graph-0.02/Graph/Frame.pm

#
Perl | 687 lines | 652 code | 34 blank | 1 comment | 4 complexity | e0b604cc2982bf58fb48060b5c57f6f1 MD5 | raw file
  1. package SVG::Graph::Frame;
  2. use base SVG::Graph::Data;
  3. use strict;
  4. use Data::Dumper;
  5. =head2 new
  6. Title : new
  7. Usage : you should not be calling new, see add_frame
  8. Function:
  9. Example :
  10. Returns :
  11. Args :
  12. =cut
  13. sub new {
  14. my($class, %args) = @_;
  15. my $self = bless {}, $class;
  16. $self->init(%args);
  17. return $self;
  18. }
  19. =head2 init
  20. Title : init
  21. Usage :
  22. Function:
  23. Example :
  24. Returns :
  25. Args :
  26. =cut
  27. sub init {
  28. my($self, %args) = @_;
  29. # die "you must provide a 'data' arg to new()" unless $args{data};
  30. $self->_parent_svg($args{svg});
  31. foreach my $arg (keys %args){
  32. my $meth = 'add_'.$arg;
  33. $self->$meth($args{$arg});
  34. }
  35. my $id = 'n'.sprintf("%07d",int(rand(9999999)));
  36. my $group;
  37. if($self->frame_transform eq "top") {
  38. $group=$self->_parent_svg->svg->group(id=> $id);
  39. }
  40. elsif($self->frame_transform eq "left") {
  41. my $scaley = $self->xsize/$self->ysize;
  42. my $scalex = $self->ysize/$self->xsize;
  43. my $translateoffx = -$self->xoffset;
  44. my $translateoffy = -$self->yoffset;
  45. my $translatey = $self->ysize + $self->yoffset;
  46. my $translatex = $self->xoffset;
  47. $group = $self->_parent_svg->svg->group(id => $id, transform =>"translate($translatex, $translatey) scale($scaley, $scalex) rotate(-90) translate($translateoffx, $translateoffy)");
  48. }
  49. elsif($self->frame_transform eq "right") {
  50. my $scalex = $self->xsize/$self->ysize;
  51. my $scaley = $self->ysize/$self->xsize;
  52. my $translateoffx = -$self->xoffset;
  53. my $translateoffy = -$self->yoffset;
  54. my $translatey = $self->yoffset;
  55. my $translatex = $self->xsize + $self->xoffset;
  56. $group=$self->_parent_svg->svg->group(id => $id, transform => "translate($translatex, $translatey) scale($scalex, $scaley) rotate(90) translate($translateoffx, $translateoffy)");
  57. }
  58. elsif($self->frame_transform eq "bottom") {
  59. my $translateoffx = -$self->xoffset;
  60. my $translateoffy = -$self->yoffset;
  61. my $translatex = $self->xsize + $self->xoffset;
  62. my $translatey = $self->ysize + $self->yoffset;
  63. $group=$self->_parent_svg->svg->group(id => $id, transform => "translate($translatex, $translatey) rotate(180) translate($translateoffx, $translateoffy)");
  64. }
  65. else {
  66. $group=$self->_parent_svg->svg->group(id=> $id);
  67. }
  68. $self->svg($group);
  69. $self->is_changed(1);
  70. }
  71. =head2 add_glyph
  72. Title : add_glyph
  73. Usage : $frame->add_glyph( 'glyph_name', glyph_args=>arg)
  74. Function: adds a glyph to the Frame object
  75. Returns : a SVG::Graph::Glyph::glyph_name object
  76. Args : glyph dependent
  77. =cut
  78. sub add_glyph {
  79. my($self, $glyphtype, %args) = @_;
  80. my $class = 'SVG::Graph::Glyph::'.$glyphtype || 'generic';
  81. eval "require $class"; if($@){ die "couldn't load $class: $@" };
  82. my $glyph = $class->new(%args, svg => $self->svg, group => $self,
  83. xsize=>$self->xsize,
  84. ysize=>$self->ysize,
  85. xoffset=>$self->xoffset,
  86. yoffset=>$self->yoffset,
  87. );
  88. push @{$self->{glyphs}}, $glyph;
  89. return $glyph;
  90. }
  91. =head2 add_frame
  92. Title : add_frame
  93. Usage : my $frame = $graph->add_frame
  94. Function: adds a Frame to the current Frame
  95. Returns : a SVG::Graph::Frame object
  96. Args : (optional) frame_transform => 'top' default orientation
  97. 'bottom' rotates graph 180 deg (about the center)
  98. 'right' points top position towards right
  99. 'left' points top position towards left
  100. =cut
  101. sub add_frame {
  102. my($self,$frames) = @_;
  103. my $epitaph = "only SVG::Graph::Frame objects accepted";
  104. #die $epitaph unless ref $frames->{frame} eq 'SVG::Graph::Frame';
  105. my $frame_arg = $frames->{frame};
  106. if (ref($frame_arg) eq 'ARRAY') {
  107. foreach my $frame (@$frame_arg) {
  108. die $epitaph unless ref $frame eq 'SVG::Graph::Frame';
  109. push @{$self->{frames}}, $frame;
  110. }
  111. }
  112. elsif(ref($frame_arg) eq __PACKAGE__) {
  113. push @{$self->{frames}}, $frame_arg;
  114. }
  115. else {
  116. my $frame = SVG::Graph::Frame->new(svg=>$self->_parent_svg,
  117. _parent_frame=>$self,
  118. xoffset=>$self->_parent_svg->margin,
  119. yoffset=>$self->_parent_svg->margin,
  120. xsize=>$self->_parent_svg->width - (2 * $self->_parent_svg->margin),
  121. ysize=>$self->_parent_svg->height - (2 * $self->_parent_svg->margin),
  122. frame_transform=>$frames->{frame_transform}
  123. );
  124. $frame->stack($self->stack) if $self->stack;
  125. $frame->ystat($self->ystat) if $self->stack;
  126. push @{$self->{frames}}, $frame;
  127. return $frame;
  128. }
  129. $self->is_changed(1);
  130. }
  131. =head2 frames
  132. Title : frames
  133. Usage :
  134. Function:
  135. Example :
  136. Returns :
  137. Args :
  138. =cut
  139. sub frames {
  140. my $self = shift;
  141. return $self->{frames} ? @{$self->{frames}} : ();
  142. }
  143. =head2 add_data
  144. Title : add_data
  145. Usage : $frame->add_data($data)
  146. Function: adds a SVG::Graph::Data object to the current Frame
  147. Returns : none
  148. Args : SVG::Graph::Data object
  149. =cut
  150. sub add_data {
  151. my($self,@datas) = @_;
  152. my $epitaph = "only SVG::Graph::Data objects accepted";
  153. foreach my $data (@datas){
  154. if(ref $data eq 'ARRAY'){
  155. foreach my $d (@$data){
  156. die $epitaph unless ref $d eq 'SVG::Graph::Data' || ref $data eq 'SVG::Graph::Data::Tree';
  157. push @{$self->{data}}, $d;
  158. }
  159. } else {
  160. die $epitaph unless ref $data eq 'SVG::Graph::Data' || ref $data eq 'SVG::Graph::Data::Tree';
  161. push @{$self->{data}}, $data;
  162. }
  163. }
  164. $self->is_changed(1);
  165. }
  166. =head2 all_data
  167. Title : all_data
  168. Usage :
  169. Function:
  170. Example :
  171. Returns :
  172. Args :
  173. =cut
  174. sub all_data {
  175. my $self = shift;
  176. my $flag = shift;
  177. if(($self->_parent_frame && $flag) || !$self->_parent_frame){
  178. my @data = $self->data;
  179. #recurse down into subframes...
  180. foreach my $subframe ($self->frames){
  181. push @data, $subframe->all_data(1);
  182. }
  183. return map {$_->can('data') ? $_->all_data(1) : $_ } @data;
  184. } elsif($self->_parent_frame) {
  185. return $self->_parent_frame->all_data;
  186. }
  187. }
  188. =head2 data
  189. Title : data
  190. Usage :
  191. Function:
  192. Example :
  193. Returns :
  194. Args :
  195. =cut
  196. sub data {
  197. my $self = shift;
  198. #these are SVG::Graph::Data objects
  199. my @data = $self->{data} ? @{$self->{data}} : ();
  200. #recurse down into subframes...
  201. foreach my $subframe ($self->frames){
  202. push @data, $subframe->data;
  203. }
  204. return map {$_->can('data') ? $_->data : $_ } @data;
  205. }
  206. =head2 glyphs
  207. Title : glyphs
  208. Usage :
  209. Function:
  210. Example :
  211. Returns :
  212. Args :
  213. =cut
  214. sub glyphs {
  215. my $self = shift;
  216. return $self->{glyphs} ? @{$self->{glyphs}} : ();
  217. }
  218. =head2 data_chunks
  219. Title : data_chunks
  220. Usage :
  221. Function:
  222. Example :
  223. Returns :
  224. Args :
  225. =cut
  226. sub data_chunks {
  227. my $self = shift;
  228. my @data = $self->{data} ? @{$self->{data}} : ();
  229. #recurse down into subframes...
  230. foreach my $subframe ($self->frames){
  231. push @data, $subframe->data_chunks;
  232. }
  233. return @data;
  234. }
  235. =head2 draw
  236. Title : draw
  237. Usage : should not directly call this method, see SVG::Graph->draw
  238. Function: depends on child glyph implementations
  239. Example :
  240. Returns :
  241. Args :
  242. =cut
  243. sub draw {
  244. my($self, $svg) = @_;
  245. foreach my $frame ($self->frames){
  246. #warn $frame;
  247. $frame->draw($self);
  248. }
  249. foreach my $glyph ($self->glyphs){
  250. #warn $glyph;
  251. $glyph->draw($self);
  252. }
  253. }
  254. =head2 _recalculate_stats
  255. Title : _recalculate_stats
  256. Usage :
  257. Function:
  258. Example :
  259. Returns :
  260. Args :
  261. =cut
  262. sub _recalculate_stats{
  263. my ($self,@args) = @_;
  264. return undef unless $self->is_changed;
  265. my $xstat = Statistics::Descriptive::Full->new();
  266. my $ystat = Statistics::Descriptive::Full->new();
  267. my $zstat = Statistics::Descriptive::Full->new();
  268. #right now we only support y-stacking. this may need to be extended in the future
  269. if($self->stack){
  270. my @ystack;
  271. foreach my $data ($self->data_chunks){
  272. my $i = 0;
  273. foreach my $datum ($data->data){
  274. $ystack[$i] += $datum->y;
  275. $i++;
  276. }
  277. }
  278. $ystat->add_data($_) foreach @ystack;
  279. } else {
  280. $ystat->add_data(map {ref($_) && $_->can('y') ? $_->y : $_} map {$_->can('data') ? $_->data : $_->y} $self->all_data);
  281. }
  282. $xstat->add_data(map {ref($_) && $_->can('x') ? $_->x : $_} map {$_->can('data') ? $_->data : $_->x} $self->all_data);
  283. $zstat->add_data(map {ref($_) && $_->can('z') ? $_->z : $_} map {$_->can('data') ? $_->data : $_->z} $self->all_data);
  284. $self->xstat($xstat);
  285. $self->ystat($ystat);
  286. $self->zstat($zstat);
  287. $self->is_changed(0);
  288. }
  289. =head2 _parent_svg
  290. Title : _parent_svg
  291. Usage : $obj->_parent_svg($newval)
  292. Function:
  293. Example :
  294. Returns : value of _parent_svg (a scalar)
  295. Args : on set, new value (a scalar or undef, optional)
  296. =cut
  297. sub _parent_svg{
  298. my $self = shift;
  299. return $self->{'_parent_svg'} = shift if @_;
  300. return $self->{'_parent_svg'};
  301. }
  302. =head2 _parent_frame
  303. Title : _parent_frame
  304. Usage : $obj->_parent_frame($newval)
  305. Function:
  306. Example :
  307. Returns : value of _parent_frame (a scalar)
  308. Args : on set, new value (a scalar or undef, optional)
  309. =cut
  310. sub add__parent_frame{return shift->_parent_frame(@_)}
  311. sub _parent_frame{
  312. my $self = shift;
  313. return $self->{'_parent_frame'} = shift if @_;
  314. return $self->{'_parent_frame'};
  315. }
  316. =head2 svg
  317. Title : svg
  318. Usage : $obj->svg($newval)
  319. Function:
  320. Example :
  321. Returns : value of svg (a scalar)
  322. Args : on set, new value (a scalar or undef, optional)
  323. =cut
  324. sub svg{
  325. my $self = shift;
  326. return $self->{'svg'} = shift if @_;
  327. return $self->{'svg'};
  328. }
  329. =head2 xsize
  330. Title : xsize
  331. Usage : $obj->xsize($newval)
  332. Function:
  333. Example :
  334. Returns : value of xsize (a scalar)
  335. Args : on set, new value (a scalar or undef, optional)
  336. =cut
  337. sub add_xsize {return shift->xsize(@_)}
  338. sub xsize{
  339. my $self = shift;
  340. return $self->{'xsize'} = shift if @_;
  341. return $self->{'xsize'};
  342. }
  343. =head2 ysize
  344. Title : ysize
  345. Usage : $obj->ysize($newval)
  346. Function:
  347. Example :
  348. Returns : value of ysize (a scalar)
  349. Args : on set, new value (a scalar or undef, optional)
  350. =cut
  351. sub add_ysize {return shift->ysize(@_)}
  352. sub ysize{
  353. my $self = shift;
  354. return $self->{'ysize'} = shift if @_;
  355. return $self->{'ysize'};
  356. }
  357. =head2 xoffset
  358. Title : xoffset
  359. Usage : $obj->xoffset($newval)
  360. Function:
  361. Example :
  362. Returns : value of xoffset (a scalar)
  363. Args : on set, new value (a scalar or undef, optional)
  364. =cut
  365. sub add_xoffset {return shift->xoffset(@_)}
  366. sub xoffset{
  367. my $self = shift;
  368. return $self->{'xoffset'} = shift if @_;
  369. return $self->{'xoffset'};
  370. }
  371. =head2 yoffset
  372. Title : yoffset
  373. Usage : $obj->yoffset($newval)
  374. Function:
  375. Example :
  376. Returns : value of yoffset (a scalar)
  377. Args : on set, new value (a scalar or undef, optional)
  378. =cut
  379. sub add_yoffset {return shift->yoffset(@_)}
  380. sub yoffset{
  381. my $self = shift;
  382. return $self->{'yoffset'} = shift if @_;
  383. return $self->{'yoffset'};
  384. }
  385. =head2 xmin
  386. Title : xmin
  387. Usage : $obj->xmin($newval)
  388. Function:
  389. Example :
  390. Returns : value of xmin (a scalar)
  391. Args : on set, new value (a scalar or undef, optional)
  392. =cut
  393. sub xmin{
  394. my $self = shift;
  395. return $self->{'xmin'} = shift if @_;
  396. return $self->{'xmin'} if defined $self->{'xmin'};
  397. $self->_recalculate_stats();
  398. return $self->xstat->min;
  399. }
  400. =head2 xmax
  401. Title : xmax
  402. Usage : $obj->xmax($newval)
  403. Function:
  404. Example :
  405. Returns : value of xmax (a scalar)
  406. Args : on set, new value (a scalar or undef, optional)
  407. =cut
  408. sub xmax{
  409. my $self = shift;
  410. return $self->{'xmax'} = shift if @_;
  411. return $self->{'xmax'} if defined $self->{'xmax'};
  412. $self->_recalculate_stats();
  413. return $self->xstat->max;
  414. }
  415. =head2 ymin
  416. Title : ymin
  417. Usage : $obj->ymin($newval)
  418. Function:
  419. Example :
  420. Returns : value of ymin (a scalar)
  421. Args : on set, new value (a scalar or undef, optional)
  422. =cut
  423. sub ymin{
  424. my $self = shift;
  425. return $self->{'ymin'} = shift if @_;
  426. return $self->{'ymin'} if defined $self->{'ymin'};
  427. $self->_recalculate_stats();
  428. return $self->ystat->min;
  429. }
  430. =head2 ymax
  431. Title : ymax
  432. Usage : $obj->ymax($newval)
  433. Function:
  434. Example :
  435. Returns : value of ymax (a scalar)
  436. Args : on set, new value (a scalar or undef, optional)
  437. =cut
  438. sub ymax{
  439. my $self = shift;
  440. return $self->{'ymax'} = shift if @_;
  441. return $self->{'ymax'} if defined $self->{'ymax'};
  442. $self->_recalculate_stats();
  443. return $self->ystat->max;
  444. }
  445. =head2 xrange
  446. Title : xrange
  447. Usage : $obj->xrange($newval)
  448. Function:
  449. Example :
  450. Returns : value of xrange (a scalar)
  451. =cut
  452. sub xrange{
  453. my $self = shift;
  454. return $self->xmax - $self->xmin;
  455. }
  456. =head2 yrange
  457. Title : yrange
  458. Usage : $obj->yrange($newval)
  459. Function:
  460. Example :
  461. Returns : value of yrange (a scalar)
  462. =cut
  463. sub yrange{
  464. my $self = shift;
  465. return $self->ymax - $self->ymin;
  466. }
  467. =head2 stack
  468. Title : stack
  469. Usage : $obj->stack($newval)
  470. Function:
  471. Example :
  472. Returns : value of stack (a scalar)
  473. Args : on set, new value (a scalar or undef, optional)
  474. =cut
  475. sub stack{
  476. my $self = shift;
  477. return $self->{'stack'} = shift if @_;
  478. return $self->{'stack'};
  479. }
  480. sub add_frame_transform{return shift->frame_transform(@_)}
  481. sub frame_transform {
  482. my $self = shift;
  483. return $self->{'frame_transform'} = shift if @_;
  484. return $self->{'frame_transform'} if defined $self->{'frame_transform'};
  485. }
  486. 1;