Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Lots of improvements and handy things #16

Merged
merged 2 commits into from

2 participants

@nitbix
  • I made it so data can have hole - this should be inclusive of the patch that epa submitted
  • Made it so that the Line renderer can plot holes properly, even when there is only one point surrounded by holes
  • Fixed a problem with Range that made it impossible to render completely flat lines
  • Added a 'fast mode' that skips unnecessary data when there are lots of points per pixel
Alan Mosca added some commits
Alan Mosca fixed tests for changes in divvy() 34a86fb
Alan Mosca data can have holes (undef values), if there are holes around data th…
…ey are visible, we can plot straight lines and there is a fast mode (Line only for now)
49acfcc
@gphat gphat merged commit cdefc9f into gphat:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 8, 2013
  1. fixed tests for changes in divvy()

    Alan Mosca authored
  2. data can have holes (undef values), if there are holes around data th…

    Alan Mosca authored
    …ey are visible, we can plot straight lines and there is a fast mode (Line only for now)
This page is out of date. Refresh to see the latest.
View
13 lib/Chart/Clicker.pm
@@ -403,6 +403,19 @@ has 'format' => (
default => sub { 'PNG' }
);
+=attr plot_mode
+
+Fast or slow plot mode. When in fast mode, data elements that are deemed to be
+superfluous or invisible will not be drawn. Default is 'slow'
+
+=cut
+
+has 'plot_mode' => (
+ is => 'rw',
+ isa => 'Str',
+ default => sub { 'slow' }
+);
+
=attr grid_over
Flag controlling if the grid is rendered B<over> the data. Defaults to 0.
View
8 lib/Chart/Clicker/Axis.pm
@@ -466,7 +466,12 @@ override('prepare', sub {
my $label = $val;
if(defined($self->tick_labels)) {
- $label = $self->tick_labels->[$i];
+ if (defined $self->tick_labels->[$i]) {
+ $label = $self->tick_labels->[$i];
+ }
+ else {
+ $label = "";
+ }
} else {
$label = $self->format_value($val);
}
@@ -562,6 +567,7 @@ Given a span and a value, returns it's pixel position on this Axis.
sub mark {
my ($self, $span, $value) = @_;
+ return undef if not defined $value;
if($self->has_skip_range) {
# We must completely ignore values that fall inside the skip range,
# so we return an undef.
View
54 lib/Chart/Clicker/Data/Range.pm
@@ -1,5 +1,8 @@
package Chart::Clicker::Data::Range;
use Moose;
+use Moose::Util::TypeConstraints;
+
+use constant EPSILON => 0.0001;
# ABSTRACT: A range of Data
@@ -22,7 +25,15 @@ Set/Get the lower bound for this Range
=cut
-has 'lower' => ( is => 'rw', isa => 'Num' );
+subtype 'Lower'
+ => as 'Num|Undef'
+ => where { defined($_) };
+
+coerce 'Lower'
+ => from 'Undef'
+ => via { - EPSILON };
+
+has 'lower' => ( is => 'rw', isa => 'Lower', coerce => 1);
=attr max
@@ -48,7 +59,16 @@ Set/Get the upper bound for this Range
=cut
-has 'upper' => ( is => 'rw', isa => 'Num' );
+subtype 'Upper'
+ => as 'Num|Undef'
+ => where { defined($_) };
+
+coerce 'Upper'
+ => from 'Num|Undef'
+ => via { EPSILON };
+
+has 'upper' => ( is => 'rw', isa => 'Upper', coerce => 1);
+
=attr ticks
@@ -65,6 +85,15 @@ after 'lower' => sub {
if(defined($self->{'min'})) {
$self->{'lower'} = $self->{'min'};
}
+
+ $self->{'lower'} = $self->{'min'} unless (defined($self->{'lower'}));
+ $self->{'upper'} = $self->{'max'} unless (defined($self->{'upper'}));
+
+ if(defined($self->{'lower'}) && defined($self->{'upper'}) && $self->{'lower'} == $self->{'upper'}) {
+ $self->{'lower'} = $self->{'lower'} - EPSILON;
+ $self->{'lower'} = $self->{'lower'} + EPSILON;
+ }
+
};
after 'upper' => sub {
@@ -73,6 +102,15 @@ after 'upper' => sub {
if(defined($self->{'max'})) {
$self->{'upper'} = $self->{'max'};
}
+
+ $self->{'lower'} = $self->{'min'} unless (defined($self->{'lower'}));
+ $self->{'upper'} = $self->{'max'} unless (defined($self->{'upper'}));
+
+ if(defined($self->{'lower'}) && defined($self->{'upper'}) && $self->{'lower'} == $self->{'upper'}) {
+ $self->{'upper'} = $self->{'upper'} - EPSILON;
+ $self->{'upper'} = $self->{'upper'} + EPSILON;
+ }
+
};
after 'min' => sub {
@@ -161,11 +199,19 @@ Returns the span of this range, or UPPER - LOWER.
sub span {
my ($self) = @_;
- return $self->upper - $self->lower;
+ my $span = $self->upper - $self->lower;
+
+ #we still want to be able to see flat lines!
+ if ($span <= EPSILON) {
+ $self->upper($self->upper() + EPSILON);
+ $self->lower($self->lower() - EPSILON);
+ $span = $self->upper - $self->lower;
+ }
+ return $span;
}
__PACKAGE__->meta->make_immutable;
no Moose;
-1;
+1;
View
5 lib/Chart/Clicker/Data/Series.pm
@@ -110,7 +110,7 @@ Get the count of values in this series.
has 'values' => (
traits => [ 'Array' ],
is => 'rw',
- isa => 'ArrayRef[Num]',
+ isa => 'ArrayRef[Num|Undef]',
default => sub { [] },
handles => {
'add_to_values' => 'push',
@@ -127,7 +127,8 @@ sub _build_range {
unless scalar(@{ $values });
return Chart::Clicker::Data::Range->new(
- lower => min(@{ $values }), upper => max(@{ $values})
+ lower => min(grep { defined } @{ $values }),
+ upper => max(grep { defined } @{ $values })
);
}
View
5 lib/Chart/Clicker/Decoration/Legend.pm
@@ -86,6 +86,9 @@ override('prepare', sub {
my $font = $self->font;
my $ii = $self->item_padding;
+
+ #this makes sure that wrapping works
+ $self->width($self->clicker->width);
if($self->is_vertical) {
# This assumes you aren't changing the layout manager...
@@ -122,4 +125,4 @@ __PACKAGE__->meta->make_immutable;
no Moose;
-1;
+1;
View
59 lib/Chart/Clicker/Renderer/Line.pm
@@ -8,6 +8,12 @@ extends 'Chart::Clicker::Renderer';
use Geometry::Primitive::Point;
use Graphics::Primitive::Brush;
use Graphics::Primitive::Operation::Stroke;
+use Geometry::Primitive::Circle;
+
+#number of defined points we must have around another point
+#to render a line instead of a scatter
+#
+use constant MIN_DEFINED_SURROUNDING_POINTS => 5;
=head1 DESCRIPTION
@@ -108,12 +114,18 @@ sub finalize {
my $kcount = $series->key_count - 1;
+ my $skip = 0;
+ my $previous_x = -1;
+ my $previous_y = -1;
+ my $min_y_delta_on_same_x = $height / 100;
+
for(0..$kcount) {
my $key = $keys[$_];
my $x = $domain->mark($width, $key);
next unless defined($x);
+ $skip = 1 unless defined $vals[$_];
my $ymark = $range->mark($height, $vals[$_]);
next unless defined($ymark);
@@ -127,12 +139,49 @@ sub finalize {
}
my $y = $height - $ymark;
-
- if($_ == 0) {
+ if( $_ == 0 || $skip ) {
+ my $lineop = Graphics::Primitive::Operation::Stroke->new(
+ brush => $self->brush->clone
+ );
+ $lineop->brush->color($color);
+ $self->do($lineop);
$self->move_to($x, $y);
- } else {
- $self->line_to($x, $y);
+ my $start_new_line = 1;
+ foreach my $i ($_..($_ + MIN_DEFINED_SURROUNDING_POINTS)) {
+ if ($i > 0 && $i < @vals && !defined($vals[$i])) {
+ $start_new_line = 0;
+ }
+ }
+ if ($start_new_line){
+ $skip = 0;
+ }
+ else {
+ my $shape = Geometry::Primitive::Circle->new(radius => 3);
+ $shape->origin(Geometry::Primitive::Point->new(x => $x, y => $y));
+ $self->path->add_primitive($shape);
+ my $fill = Graphics::Primitive::Operation::Fill->new(
+ paint => Graphics::Primitive::Paint::Solid->new(
+ color => $color
+ )
+ );
+ $self->do($fill);
+ }
}
+ else {
+ # when in fast mode, we plot only if we moved by more than
+ # 1 of a pixel on the X axis or we moved by more than 1%
+ # of the size of the Y axis.
+ if( $clicker->plot_mode ne 'fast' ||
+ $x - $previous_x > 1 ||
+ abs($y - $previous_y) > $min_y_delta_on_same_x
+ )
+ {
+ $self->line_to($x, $y);
+ $previous_x = $x;
+ $previous_y = $y;
+ }
+ }
+
}
my $op = Graphics::Primitive::Operation::Stroke->new;
$op->brush($self->brush->clone);
@@ -203,4 +252,4 @@ __PACKAGE__->meta->make_immutable;
no Moose;
-1;
+1;
View
15 t/axis-division-rounded.t
@@ -19,7 +19,7 @@ my $label = 'Foo';
$axis->range->lower(3);
$axis->range->upper(105);
is( $axis->ticks, '5', 'Default number of ticks' );
- is_deeply( $axis->divvy(), [ 25, 50, 75, 100 ], 'Nicely rounded tick values - medium scale' );
+ is_deeply( $axis->divvy(), [ 20, 40, 60, 80, 100 ], 'Nicely rounded tick values - medium scale' );
}
# Larger Range
@@ -34,7 +34,9 @@ my $label = 'Foo';
$axis->range->upper(999123421);
is_deeply(
$axis->divvy(),
- [ 0, 200000000, 400000000, 600000000, 800000000, 1000000000 ],
+ [ 0, 100000000, 200000000, 300000000, 400000000, 500000000,
+ 600000000, 700000000, 800000000, 900000000, 1000000000],
+
'Nicely rounded tick values - large scale 5 ticks'
);
}
@@ -50,7 +52,8 @@ my $label = 'Foo';
$axis->range->lower(1);
$axis->range->upper(999123421);
$axis->ticks(3);
- is_deeply( $axis->divvy(), [ 0, 400000000, 800000000 ], 'Nicely rounded tick values - large scale 3 ticks' );
+ is_deeply( $axis->divvy(), [ 0, 250000000, 500000000, 750000000, 1000000000 ],
+ 'Nicely rounded tick values - large scale 3 ticks' );
}
# Very small range below 1
@@ -64,7 +67,8 @@ my $label = 'Foo';
$axis->range->lower(0.0072);
$axis->range->upper(0.0078);
$axis->ticks(3);
- is_deeply( $axis->divvy(), [ 0.00725, 0.00750, 0.00775 ], 'Nicely rounded tick values - large scale 3 ticks' );
+ is_deeply( $axis->divvy(), [ 0.0072, 0.0073, 0.0074, 0.0075, 0.0076, 0.0077, 0.0078 ],
+ 'Nicely rounded tick values - large scale 3 ticks' );
}
# Very small range above 1
@@ -78,7 +82,8 @@ my $label = 'Foo';
$axis->range->lower(1.5672);
$axis->range->upper(1.5679);
$axis->ticks(4);
- is_deeply( $axis->divvy(), [ 1.5672, 1.5674, 1.5676, 1.5678 ], 'Nicely rounded tick values - large scale 3 ticks' );
+ is_deeply( $axis->divvy(), [ 1.5672, 1.5673, 1.5674, 1.5675, 1.5676, 1.5677, 1.5678, 1.5679 ],
+ 'Nicely rounded tick values - large scale 3 ticks' );
}
done_testing;
Something went wrong with that request. Please try again.