From bb889e0bca1aeef079100892170eed0fd2a1cff5 Mon Sep 17 00:00:00 2001 From: drbaggy Date: Mon, 16 May 2022 22:22:09 +0100 Subject: first commit --- challenge-165/james-smith/perl/ch-1.pl | 116 +++++++++++++++++++++++++++++++ challenge-165/james-smith/perl/data.txt | 3 + challenge-165/james-smith/perl/data2.txt | 6 ++ 3 files changed, 125 insertions(+) create mode 100644 challenge-165/james-smith/perl/ch-1.pl create mode 100644 challenge-165/james-smith/perl/data.txt create mode 100644 challenge-165/james-smith/perl/data2.txt diff --git a/challenge-165/james-smith/perl/ch-1.pl b/challenge-165/james-smith/perl/ch-1.pl new file mode 100644 index 0000000000..3e09f76ceb --- /dev/null +++ b/challenge-165/james-smith/perl/ch-1.pl @@ -0,0 +1,116 @@ +#!/usr/local/bin/perl + +use strict; + +use warnings; +use feature qw(say); +use Test::More; +use Benchmark qw(cmpthese timethis); +use Data::Dumper qw(Dumper); + +my $margin = 10; +my $extn = 20; +my $radius = 10; +my $stroke = 5; + +my(@t,@lines,@pts); + +## 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; + warn $_; + 4==(@t = split /,/) ? (push @lines,[@t]) : (push @pts,[@t]) for split; +} + +## If the code is programme 2 we then calculate the line of best fit, +## and compute where it crosses the sizes of our box.... + +if( $0 eq 'ch-2.pl' ) { + 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 = $min_x - $extn; + my $r_x = $max_x + $extn; + + $l_x = ( ($l_y = $min_y - $extn ) - $a)/$b if $l_y < $min_y - $extn; + $l_x = ( ($l_y = $max_y + $extn ) - $a)/$b if $l_y > $max_y + $extn; + $r_x = ( ($r_y = $min_y - $extn ) - $a)/$b if $r_y < $min_y - $extn; + $r_x = ( ($r_y = $max_y + $extn ) - $a)/$b if $r_y > $max_y + $extn; + + push @lines, [ $l_x,$l_y,$r_x,$r_y ]; +} + +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 ); +} + +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 $sf = $width/$W; + + ## Finally we pull this altogether in our SVG template using sprintf, join/map/sprintf for the sizes, lines & points. + return sprintf ' + + + + + %s + + + %s + + +', $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 '', @{$_} } @{$lines} ), + join( qq(\n ), map { sprintf '', @{$_}, $radius*$sf } @{$pts} ); +} diff --git a/challenge-165/james-smith/perl/data.txt b/challenge-165/james-smith/perl/data.txt new file mode 100644 index 0000000000..42acc2b70b --- /dev/null +++ b/challenge-165/james-smith/perl/data.txt @@ -0,0 +1,3 @@ +53,10 +53,10,23,30 +23,30 diff --git a/challenge-165/james-smith/perl/data2.txt b/challenge-165/james-smith/perl/data2.txt new file mode 100644 index 0000000000..9e5ecc346a --- /dev/null +++ b/challenge-165/james-smith/perl/data2.txt @@ -0,0 +1,6 @@ +333,129 39,189 140,156 292,134 393,52 160,166 362,122 13,193 +341,104 320,113 109,177 203,152 343,100 225,110 23,186 282,102 +284,98 205,133 297,114 292,126 339,112 327,79 253,136 61,169 +128,176 346,72 316,103 124,162 65,181 159,137 212,116 337,86 +215,136 153,137 390,104 100,180 76,188 77,181 69,195 92,186 +275,96 250,147 34,174 213,134 186,129 189,154 361,82 363,89 -- cgit