aboutsummaryrefslogtreecommitdiff
path: root/challenge-165/james-smith/perl
diff options
context:
space:
mode:
authordrbaggy <js5@sanger.ac.uk>2022-05-17 11:17:05 +0100
committerdrbaggy <js5@sanger.ac.uk>2022-05-17 11:17:05 +0100
commit7dc2265216049d8377258b07db4c75d64525331f (patch)
treeff183e1af607f9d807c3be852f806dcb3bc1961b /challenge-165/james-smith/perl
parent25df1a353ac34fe2621ff977ad24e040c451929a (diff)
downloadperlweeklychallenge-club-7dc2265216049d8377258b07db4c75d64525331f.tar.gz
perlweeklychallenge-club-7dc2265216049d8377258b07db4c75d64525331f.tar.bz2
perlweeklychallenge-club-7dc2265216049d8377258b07db4c75d64525331f.zip
pulled config into hash
Diffstat (limited to 'challenge-165/james-smith/perl')
-rw-r--r--challenge-165/james-smith/perl/ch-1.pl136
1 files changed, 54 insertions, 82 deletions
diff --git a/challenge-165/james-smith/perl/ch-1.pl b/challenge-165/james-smith/perl/ch-1.pl
index a753218eb4..652f70d780 100644
--- a/challenge-165/james-smith/perl/ch-1.pl
+++ b/challenge-165/james-smith/perl/ch-1.pl
@@ -5,114 +5,86 @@ use strict;
use warnings;
use feature qw(say);
-my $margin = 10;
-my $extn = 20;
-my $radius = 10;
-my $stroke = 5;
+my(@lines,@pts);
-my(@t,@lines,@pts);
+get_points_and_lines( );
+add_best_fit_line( 20 ) if $0 eq 'ch-2.pl';
+say render_svg( \@pts, \@lines, {
+ 'margin' => 10, 'max_w' => 960, 'max_h' => 540, # Size of image & margins
+ 'stroke' => 5, 'color' => '#900' # Style for lines
+ 'radius' => 10, 'fill' => '#090', # Style for dots
+ 'border' => '#009', 'bg' => '#ffd', # Style for "page"....
+} );
-## Swallow the input into two arrays - one of pts and one of lines
-## Note we allow multiple data points on the same line (as per example 2)
-while(<>) {
- chomp;
- 4==(@t = split /,/) ? (push @lines,[@t]) : (push @pts,[@t]) for split;
+## Compute the best fit line for the points array (using linear regression...
+## Assumes a dependency of y on x....
+
+sub best_fit {
+ my $sx = my $sy = my $sxy = my $sxx = 0, my $n = @{$_[0]};
+ $sx += $_->[0], $sxy += $_->[0]*$_->[1], $sy += $_->[1], $sxx += $_->[0]*$_->[0] foreach @{$_[0]};
+ my $b = ( $n*$sxy-$sx*$sy ) / ( $n*$sxx - $sx*$sx );
+ ( ($sy-$b*$sx)/$n, $b );
}
-## If the code is programme 2 we then calculate the line of best fit,
-## and compute where it crosses the sizes of our box....
+sub get_points_and_lines {
+ my @t;
+ while(<>) {
+ chomp;
+ 4 == (@t = split /,/) ? ( push @lines, [@t] ) : ( push @pts, [@t] ) for split;
+ }
+}
-if( $0 eq 'ch-2.pl' ) {
-## This computes the line of best fix,
-## then works out where the lines cross the edge of the region
-## around the points {including a small margin}
-## and adds it to the list of lines to render...
+sub add_best_fit_line {
+ my $extn = shift // 40;
my( $a, $b ) = best_fit( \@pts );
my( $min_x, $max_x, $min_y, $max_y ) = get_ranges( \@pts );
-
my $l_y = $a + $b * ($min_x - $extn);
my $r_y = $a + $b * ($max_x + $extn);
my $l_x = $l_y < $min_y - $extn ? ( ($l_y = $min_y - $extn ) - $a)/$b
- : $l_y > $max_y + $extn ? ( ($l_y = $max_y + $extn ) - $a)/$b
- : $min_x - $extn
- ;
+ : $l_y > $max_y + $extn ? ( ($l_y = $max_y + $extn ) - $a)/$b : $min_x - $extn;
my $r_x = $r_y < $min_y - $extn ? ( ($r_y = $min_y - $extn ) - $a)/$b
- : $r_y > $max_y + $extn ? ( ($r_y = $max_y + $extn ) - $a)/$b
- : $max_x + $extn
- ;
-
+ : $r_y > $max_y + $extn ? ( ($r_y = $max_y + $extn ) - $a)/$b : $max_x + $extn;
push @lines, [ $l_x,$l_y,$r_x,$r_y ];
}
-## We call the "renderer"
-say render_svg( \@pts, \@lines, { 'max_w' => 960, 'max_h' => 540 } );
-
-## This computes the equation for the regression line...
-sub best_fit {
- my $sx = my $sy = my $sxy = my $sxx = 0, my $n = @{$_[0]};
- $sx += $_->[0], $sxy += $_->[0]*$_->[1],
- $sy += $_->[1], $sxx += $_->[0]*$_->[0] foreach @{$_[0]};
- my $b = ( $n*$sxy-$sx*$sy ) / ( $n*$sxx - $sx*$sx );
- my $a = ($sy - $b * $sx)/$n;
- return ( ($sy-$b*$sx)/$n, $b );
-}
-
-## Get max and min values for array of points/lines
sub get_ranges {
my( $pts, $lines ) = @_;
-
- ## Set min/max x/y to the first point (or the start of the first line)
- my $min_x = my $max_x = @pts ? $pts->[0][0] : $lines->[0][0];
- my $min_y = my $max_y = @pts ? $pts->[0][1] : $lines->[0][1];
-
- ## We loop through all points + all starts/ends of lines { this is what the map does }
- ## We can make this a single postfix for by using "," to separate the statements and
- ## using A && B as a proxy to B if A.....
- ( $_->[0] < $min_x ) && ( $min_x = $_->[0] ),
- ( $_->[0] > $max_x ) && ( $max_x = $_->[0] ),
- ( $_->[1] < $min_y ) && ( $min_y = $_->[1] ),
- ( $_->[1] > $max_y ) && ( $max_y = $_->[1] ) for @{$pts}, map { ($_, [$_->[2],$_->[3]]) } @{$lines};
-
- return ( $min_x, $max_x, $min_y, $max_y );
+ my( $min_x,$min_y ) = my( $max_x,$max_y ) = @{$pts} ? @{$pts->[0]} : @{$lines->[0]};
+ ($_->[0]<$min_x)&&($min_x=$_->[0]), ($_->[0]>$max_x)&&($max_x=$_->[0]),
+ ($_->[1]<$min_y)&&($min_y=$_->[1]), ($_->[1]>$max_y)&&($max_y=$_->[1]) for @{$pts}, map {($_,[$_->[2],$_->[3]])} @{$lines};
+ ( $min_x, $max_x, $min_y, $max_y );
}
sub render_svg {
my( $pts, $lines, $config ) = @_;
- ## We don't want to expand around line of best fit so don't include it in
- ## the range expansion.
my( $min_x, $max_x, $min_y, $max_y ) = get_ranges( $pts, $0 eq 'ch-2.pl' ? [] : $lines );
-
- my( $width,$height ) = ( $max_x - $min_x + 2 * $margin, $max_y - $min_y + 2 * $margin );
-
- ## Compute the bounding box of the graph such that is limited by the W/H specified
- my $W = $config->{'max_w'}//800; my $H = $config->{'max_h'}//600;
- ## If it's aspect ratio wider than the given aspect ratio we make the height shorter
- ## Otherwise we make the width narrower...
- if( $width/$height > $W/$H ) {
- $H = $height/$width*$W;
- } else {
- $W = $width/$height*$H;
- }
- ## Scale-factor we resize points and lines so that the points/lines will look the same size irrespective of the
- ## ratio of virtual size of region and physical size of SVG.
+ my $margin = $config->{'margin'}//20;
+ my($W,$H,$width,$height) = ($config->{'max_w'}//800,$config->{'max_h'}//600,$max_x-$min_x+2*$margin,$max_y-$min_y+2*$margin);
+ ( $width/$height > $W/$H ) ? ( $H = $height/$width*$W ) : ( $W = $width/$height*$H );
my $sf = $width/$W;
-
- ## Finally we pull this altogether in our SVG template using sprintf, join/map/sprintf for the sizes, lines & points.
return sprintf '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
-<svg height="%s" width="%s"
- viewBox="%s %s %s %s" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <rect stroke="#000" stroke-width="%s" fill="#eee" x="%s" y="%s" width="%s" height="%s" />
- <g stroke="#900" stroke-width="%s">
+<svg height="%s" width="%s" viewBox="%s %s %s %s" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <rect stroke="%s" stroke-width="%s" fill="%s" x="%s" y="%s" width="%s" height="%s" />
+ <g stroke="%s" stroke-width="%s">
%s
</g>
- <g fill="#090">
+ <g fill="%s">
%s
</g>
-</svg>
-', $H, $W,
- $min_x - $margin, $min_y - $margin, $width, $height,
- $sf, $min_x - $margin, $min_y - $margin, $width, $height, $stroke*$sf,
- join( qq(\n ), map { sprintf '<line x1="%s" y1="%s" x2="%s" y2="%s" />', @{$_} } @{$lines} ),
- join( qq(\n ), map { sprintf '<circle cx="%s" cy="%s" r="%s" />', @{$_}, $radius*$sf } @{$pts} );
+</svg>',
+ $H, $W, $min_x - $margin, $min_y - $margin, $width, $height, ## svg element
+ $config->{'border'}//'#000', $sf, $config->{'bg'}//'#eee', ## background rectangle
+ $min_x - $margin, $min_y - $margin, $width, $height,
+ $config->{'fill'}//'#000', ($config->{'stroke'}//5) * $sf, ## lines
+ join( qq(\n ), map { sprintf '<line x1="%s" y1="%s" x2="%s" y2="%s" />', @{$_} } @{$lines} ),
+ $config->{'color'}//'#ccc', ## dots
+ join( qq(\n ), map { sprintf '<circle cx="%s" cy="%s" r="%s" />', @{$_}, ($config->{'radius'}//10)*$sf } @{$pts} );
+
+ $config->{'border'}//'#000', $sf, $min_x - $margin, $min_y - $margin, $width, $height,
+ $config->{'fill'}//'#000', ($config->{'stroke'}//5) * $sf, $config->{'bg'}//'#eee',
+ join( qq(\n ), map { sprintf '<line x1="%s" y1="%s" x2="%s" y2="%s" />', @{$_} } @{$lines} ),
+ $config->{'color'}//'#ccc',
+ join( qq(\n ), map { sprintf '<circle cx="%s" cy="%s" r="%s" />', @{$_}, ($config->{'radius'}//10)*$sf } @{$pts} );
}