diff options
Diffstat (limited to 'challenge-165')
12 files changed, 329 insertions, 119 deletions
diff --git a/challenge-165/james-smith/perl/SVG.pm b/challenge-165/james-smith/perl/SVG.pm new file mode 100644 index 0000000000..d2d8323c17 --- /dev/null +++ b/challenge-165/james-smith/perl/SVG.pm @@ -0,0 +1,150 @@ +package SVG; +use warnings; +use feature qw(say); +use Data::Dumper qw(Dumper); +use Const::Fast qw(const); + +const my $DEFAULT_CONFIG => { 'margin' => 40, 'max_w' => 960, 'max_h' => 540, + 'color' => '#000', 'stroke' => 3, + 'fill' => '#ccc', 'radius' => 10, + 'border' => '#000', 'bg' => '#eee' }; +const my $LINE_TEMPLATE => '<line x1="%s" y1="%s" x2="%s" y2="%s" />'; +const my $POINT_TEMPLATE => '<circle cx="%s" cy="%s" r="%s" />'; +const my $SVG_TEMPLATE => '<?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="%s" stroke-width="%s" fill="%s" x="%s" y="%s" width="%s" height="%s" /> + <g stroke="%s" stroke-width="%s"> + %s + </g> + <g fill="%s"> + %s + </g> +</svg>'; + + +sub new { + my ( $class, $config ) = @_; + my $self = { + 'config' => { %{$DEFAULT_CONFIG}, %{$config} }, + 'points' => [], + 'lines' => [], + 'min_x' => undef, + 'max_x' => undef, + 'min_y' => undef, + 'max_y' => undef, + 'width' => undef, + 'height' => undef, + 'scale' => undef, + }; + bless $self, $class; + $self; +} + +sub load_data { + my( $self, $fn, @t ) = @_; + local $/ = undef; + open my $ifh, '<', $fn; + 4 == (@t = split /,/) ? ( push @{$self->{'lines'}}, [@t] ) ## Length 4 - line + : 2 == @t ? ( push @{$self->{'points'}}, [@t] ) ## Length 2 - point + : ( warn "input error: $_" ) ## o/w error + for grep { /\S/ } split /\s+/, <$ifh>; + close $ifh; + $self; +} + +sub add_points { + my( $self, @points ) = @_; + push @{$self->{'points'}}, @points; + $self; +} + +sub add_lines { + my( $self, @lines ) = @_; + push @{$self->{'lines'}}, @lines; + $self; +} + +sub get_range { + my $self = shift; + + ## rather than having a special cast as the first part of the loop, we start with the + ## values for the first point (or start of line if no points) + my( $min_x,$min_y ) = my( $max_x,$max_y ) = @{$self->{'points'}} ? @{$self->{'points'}[1]} : @{$self->{'lines'}[0]}; + + ## Compute the range of all points. We comma separate conditions so we only need one postfix for + ## We use ($c)&&($a=?) to mimic if($c) { $a=? } so we can use the postfix loop... + ## Note we unravel the two ends of the line by mapping the each line ($_) to $_ + the last two values $_. + ($_->[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 @{$self->{'points'}}, map {($_,[$_->[2],$_->[3]])} @{$self->{'lines'}||[]}; + ( $self->{'min_x'}, $self->{'max_x'}, $self->{'min_y'}, $self->{'max_y'} ) = ( $min_x, $max_x, $min_y, $max_y ); + $self; +} + +sub best_fit { + my $self = shift; + my $sx = my $sy = my $sxy = my $sxx = 0, my $n = @{$self->{'points'}}; + $sx += $_->[0], $sxy += $_->[0]*$_->[1], $sy += $_->[1], $sxx += $_->[0]*$_->[0] for @{$self->{'points'}}; + return $sx/$n unless $n*$sxx - $sx*$sx; + my $b = ( $n*$sxy-$sx*$sy ) / ( $n*$sxx - $sx*$sx ); + ( ($sy-$b*$sx)/$n, $b ); +} + +sub add_line_of_best_fit { + my $self = shift; + my ($a,$b) = $self->best_fit; + my ($min_x, $max_x, $min_y, $max_y, $extn) = ( $self->{'min_x'}, $self->{'max_x'}, $self->{'min_y'}, $self->{'max_y'}, $self->{'config'}{'margin'} ); + ## special case of a vertical line + $self->add_lines( [ $a, $min_y - $extn, $a, $max_y + $extn] ), return $self unless defined $b; + + ## Normal case - get y coprdinates of end points, adjust if outside the box... + 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; + 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; + $self->add_lines( [ $l_x, $l_y, $r_x, $r_y ] ); +} + +sub calculate_image_size { + my $self = shift; + my $margin = $self->{'config'}{'margin'}; + + ## Adjust height and width so it fits the size from the self->{'config'}ig. + my($W,$H,$width,$height) = ($self->{'config'}{'max_w'},$self->{'config'}{'max_h'},$self->{'max_x'}-$self->{'min_x'}+2*$margin,$self->{'max_y'}-$self->{'min_y'}+2*$margin); + ( $width/$height > $W/$H ) ? ( $H = $height/$width*$W ) : ( $W = $width/$height*$H ); + ## Calculate the scale factor so that we keep spots/lines the same size irrespective of the ranges. + ( $self->{'width'}, $self->{'height'}, $self->{'scale'} ) = ( $W, $H, $width/$W ); + $self; +} + +sub render_with_best_fit { + my $self = shift; + return $self->get_range()->add_line_of_best_fit()->_render(); +} + +sub render { + my $self = shift; + return $self->get_range()->_render(); +} + +sub _render { + my $self = shift; + $self->calculate_image_size(); + my $margin = $self->{'config'}{'margin'}; + sprintf $SVG_TEMPLATE, + $self->{'height'}, $self->{'width'}, $self->{'min_x'} - $margin, $self->{'min_y'} - $margin, + $self->{'max_x'} - $self->{'min_x'} + 2 * $margin, + $self->{'max_y'} - $self->{'min_y'} + 2 * $margin, + $self->{'config'}{'border'}, $self->{'scale'}, $self->{'config'}{'bg'}, $self->{'min_x'} - $margin, $self->{'min_y'} - $margin, + $self->{'max_x'} - $self->{'min_x'} + 2 * $margin, + $self->{'max_y'} - $self->{'min_y'} + 2 * $margin, + $self->{'config'}{'color'}, $self->{'config'}{'stroke'} * $self->{'scale'}, join( qq(\n ), map { sprintf $LINE_TEMPLATE, @{$_} } @{$self->{'lines'}} ), ## lines + $self->{'config'}{'fill'}, join( qq(\n ), map { sprintf $POINT_TEMPLATE, @{$_}, $self->{'config'}{'radius'}*$self->{'scale'} } @{$self->{'points'}} ) ## points + +} + +1; diff --git a/challenge-165/james-smith/perl/ch-1.pl b/challenge-165/james-smith/perl/ch-1.pl index 89c27e4f6f..4e6fe42bc5 100644..120000 --- a/challenge-165/james-smith/perl/ch-1.pl +++ b/challenge-165/james-smith/perl/ch-1.pl @@ -1,118 +1 @@ -#!/usr/local/bin/perl -use strict; - -use warnings; -use feature qw(say); - -my $CONFIG = { - 'margin' => 40, '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".... -}; - -my ($pts,$lines) = get_points_and_lines( ); ## Parses file and updates lines/points -add_best_fit_line( $pts, $lines, $CONFIG->{'margin'} ) if $0 eq 'ch-2.pl'; ## Only if fitting line! -say render_svg( $pts, $lines, $CONFIG ); ## Pass in config to render correctly - -##---------------------------------------------------------------------- -## Now the code does the real work.... -##---------------------------------------------------------------------- - -## Parse stdin / files given on command line, to return a list of points and lines.. -sub get_points_and_lines { - my($ps,$ls,@t)=([],[]); - local $/ = undef; - - 4 == (@t = split /,/) ? ( push @{$ls}, [@t] ) ## Length 4 - line - : 2 == @t ? ( push @{$ps}, [@t] ) ## Length 2 - point - : ( warn "input error: $_" ) ## o/w error - for grep { $_ } split /\s+/, <>; - return ($ps,$ls); -} - -## 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]}; - return $sx/$n unless $n*$sxx - $sx*$sx; - my $b = ( $n*$sxy-$sx*$sy ) / ( $n*$sxx - $sx*$sx ); - ( ($sy-$b*$sx)/$n, $b ); -} - -## Get the range of x,y values for the given list of lines/points -## Returns a tuple of min & max x and min & max y. - -sub get_ranges { - my( $ps, $ls ) = @_; - my( $min_x,$min_y ) = my( $max_x,$max_y ) = @{$ps} ? @{$pts->[0]} : @{$ls->[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 @{$ps}, map {($_,[$_->[2],$_->[3]])} @{$ls||[]}; - ( $min_x, $max_x, $min_y, $max_y ); -} - -## Get the best fit line, and then add extend it to edge of the box - by default we assume that the line will start/end on -## the side of the box, but just to be sure - we check to see if the pts lie above or below the top/bottom of the box and -## move them appropriately. - -sub add_best_fit_line { - my ($ps,$ls,$extn) = @_; - $extn //= 40; - my( $a, $b ) = best_fit( $ps ); - my( $min_x, $max_x, $min_y, $max_y ) = get_ranges( $ps ); - unless( defined $b ) { - push @{$ls}, [ $a, $min_y - $extn, $a, $max_y + $extn]; - return; - } - 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; - 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; - push @{$ls}, [ $l_x,$l_y,$r_x,$r_y ]; -} - -## Finally the rendering of the points/lines, this uses most of the config entries to deal with colour, size etc. -## We get the range and again add the margin { we don't include the lines in the equation if we are doing challenge 2 -## the line fitting as otherwise we would extend the region twice... } -## -## Once we have the size of the image - we work out it's aspect ratio and work out whether we have to make the image -## narrower or shorter so that the image is no-bigger than the suggested size and that the image is as big as possible -## -## As we have a scaling between the x+y values and the size of the image - we need to adjust the size of dots/width of lines -## by multiplying these all by a scale factor - -sub render_svg { - my( $ps, $ls, $config ) = @_; - my( $min_x, $max_x, $min_y, $max_y ) = get_ranges( $pts, $0 eq 'ch-2.pl' ? [] : $lines ); - my $margin = $config->{'margin'}//20; - - ## Adjust height and width so it fits the size from the config. - 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 ); - ## Calculate the scale factor so that we keep spots/lines the same size irrespective of the ranges. - my $sf = $width/$W; - - 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="%s" stroke-width="%s" fill="%s" x="%s" y="%s" width="%s" height="%s" /> - <g stroke="%s" stroke-width="%s"> - %s - </g> - <g fill="%s"> - %s - </g> -</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" />', @{$_} } @{$ls} ), - $config->{'color'}//'#ccc', ## dots - join( qq(\n ), map { sprintf '<circle cx="%s" cy="%s" r="%s" />', @{$_}, ($config->{'radius'}//10)*$sf } @{$ps} ) -} +functional.pl
\ No newline at end of file diff --git a/challenge-165/james-smith/perl/ch-2.pl b/challenge-165/james-smith/perl/ch-2.pl index 8b09e2d217..4e6fe42bc5 120000 --- a/challenge-165/james-smith/perl/ch-2.pl +++ b/challenge-165/james-smith/perl/ch-2.pl @@ -1 +1 @@ -ch-1.pl
\ No newline at end of file +functional.pl
\ No newline at end of file diff --git a/challenge-165/james-smith/perl/examples/data1.txt b/challenge-165/james-smith/perl/examples/01-example-point-and_line.txt index 42acc2b70b..42acc2b70b 100644 --- a/challenge-165/james-smith/perl/examples/data1.txt +++ b/challenge-165/james-smith/perl/examples/01-example-point-and_line.txt diff --git a/challenge-165/james-smith/perl/examples/data2.txt b/challenge-165/james-smith/perl/examples/02-example-from-site.txt index 9e5ecc346a..9e5ecc346a 100644 --- a/challenge-165/james-smith/perl/examples/data2.txt +++ b/challenge-165/james-smith/perl/examples/02-example-from-site.txt diff --git a/challenge-165/james-smith/perl/examples/data3.txt b/challenge-165/james-smith/perl/examples/03-horizontal-cluster.txt index dd2b0780d5..dd2b0780d5 100644 --- a/challenge-165/james-smith/perl/examples/data3.txt +++ b/challenge-165/james-smith/perl/examples/03-horizontal-cluster.txt diff --git a/challenge-165/james-smith/perl/examples/data4.txt b/challenge-165/james-smith/perl/examples/04-vertical-cluster.txt index d22449e7df..d22449e7df 100644 --- a/challenge-165/james-smith/perl/examples/data4.txt +++ b/challenge-165/james-smith/perl/examples/04-vertical-cluster.txt diff --git a/challenge-165/james-smith/perl/examples/data5.txt b/challenge-165/james-smith/perl/examples/05-only-one-x.txt index 76802ba689..76802ba689 100644 --- a/challenge-165/james-smith/perl/examples/data5.txt +++ b/challenge-165/james-smith/perl/examples/05-only-one-x.txt diff --git a/challenge-165/james-smith/perl/fit.pl b/challenge-165/james-smith/perl/fit.pl new file mode 120000 index 0000000000..47aed1db5e --- /dev/null +++ b/challenge-165/james-smith/perl/fit.pl @@ -0,0 +1 @@ +object-orientated.pl
\ No newline at end of file diff --git a/challenge-165/james-smith/perl/functional.pl b/challenge-165/james-smith/perl/functional.pl new file mode 100644 index 0000000000..6064e5dff7 --- /dev/null +++ b/challenge-165/james-smith/perl/functional.pl @@ -0,0 +1,147 @@ +#!/usr/local/bin/perl +use strict; + +use warnings; +use feature qw(say); + +my $DEF = { 'margin' => 40, 'max_w' => 960, 'max_h' => 540, + 'color' => '#000', 'stroke' => 3, + 'fill' => '#ccc', 'radius' => 10, + 'border' => '#000', 'bg' => '#eee' }; +my $LINE_TEMPLATE = '<line x1="%s" y1="%s" x2="%s" y2="%s" />'; +my $POINT_TEMPLATE = '<circle cx="%s" cy="%s" r="%s" />'; +my $SVG_TEMPLATE = '<?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="%s" stroke-width="%s" fill="%s" x="%s" y="%s" width="%s" height="%s" /> + <g stroke="%s" stroke-width="%s"> + %s + </g> + <g fill="%s"> + %s + </g> +</svg>'; + +my $config = { + 'margin' => 40, 'max_w' => 960, 'max_h' => 540, # Size of image & margins + 'stroke' => 5, 'color' => '#900', # Style for lines + 'radius' => 10, 'fill' => 'rgba(0,153,0,0.5)', # Style for dots + 'border' => '#009', 'bg' => '#ffd', # Style for "page".... +}; + +my $html = '<html><head><title>Examples</title></head><body><h1>SVG examples</h1>'; +for ( @ARGV ) { + my ($pts,$lines) = get_points_and_lines( $_ ); ## Parses file and updates lines/points + add_best_fit_line( $pts, $lines, $config->{'margin'} ) if $0 eq 'ch-2.pl'; ## Only if fitting line! + $html .= "<h2>$_</h2>". render_svg( $pts, $lines, $config ); ## add to HTML +} +say $html.'</body></html>'; + +##---------------------------------------------------------------------- +## Now the code does the real work.... +##---------------------------------------------------------------------- + +## Parse stdin / files given on command line, to return a list of points and lines.. +sub get_points_and_lines { + my($ps,$ls,@t)=([],[]); + local $/ = undef; + open my $ifh, '<', $_[0]; + + 4 == (@t = split /,/) ? ( push @{$ls}, [@t] ) ## Length 4 - line + : 2 == @t ? ( push @{$ps}, [@t] ) ## Length 2 - point + : ( warn "input error: $_" ) ## o/w error + for grep { $_ } split /\s+/, <$ifh>; + close $ifh; + ($ps,$ls); +} + +## 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]}; + return $sx/$n unless $n*$sxx - $sx*$sx; + my $b = ( $n*$sxy-$sx*$sy ) / ( $n*$sxx - $sx*$sx ); + ( ($sy-$b*$sx)/$n, $b ); +} + +## Get the range of x,y values for the given list of lines/points +## Returns a 4-tuple of min & max x and min & max y. + +sub get_ranges { + my( $ps, $ls ) = @_; + + ## rather than having a special cast as the first part of the loop, we start with the + ## values for the first point (or start of line if no points) + my( $min_x,$min_y ) = my( $max_x,$max_y ) = @{$ps} ? @{$ps->[0]} : @{$ls->[0]}; + + ## Compute the range of all points. We comma separate conditions so we only need one postfix for + ## We use ($c)&&($a=?) to mimic if($c) { $a=? } so we can use the postfix loop... + ## Note we unravel the two ends of the line by mapping the each line ($_) to $_ + the last two values $_. + ($_->[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 @{$ps}, map {($_,[$_->[2],$_->[3]])} @{$ls||[]}; + + ( $min_x, $max_x, $min_y, $max_y ); +} + +## Get the best fit line, and then extend it to edge of the box - by default we start with the line going from the left +## hand edge of the box to the right hand end. If either of these points lies outside the box we adjust the y-coord to +## the top/bottom of the box, and then alter the x coordinate. +## We treat the special case where the line is vertical ($b then contains the x-coordinate of all the points... by drawing +## a vertical line... +## +## Note we use the trick of assigning values (the new y-position) with in the ternary operators computing the x-position +## of the ends... + +sub add_best_fit_line { + my ($ps,$ls,$extn) = @_; + $extn //= $DEF->{'margin'}; + + my( $a, $b ) = best_fit( $ps ); + my( $min_x, $max_x, $min_y, $max_y ) = get_ranges( $ps ); + + ## special case of a vertical line + push( @{$ls}, [ $a, $min_y - $extn, $a, $max_y + $extn]), return unless defined $b; + + ## Normal case - get y coprdinates of end points, adjust if outside the box... + 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; + 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; + + push @{$ls}, [ $l_x,$l_y,$r_x,$r_y ]; +} + +## Finally the rendering of the points/lines, this uses most of the config entries to deal with colour, size etc. +## We get the range and again add the margin { we don't include the lines in the equation if we are doing challenge 2 +## the line fitting as otherwise we would extend the region twice... } +## +## Once we have the size of the image - we work out it's aspect ratio and work out whether we have to make the image +## narrower or shorter so that the image is no-bigger than the suggested size and that the image is as big as possible +## +## As we have a scaling between the x+y values and the size of the image - we need to adjust the size of dots/width of lines +## by multiplying these all by a scale factor + +sub render_svg { + my( $ps, $ls, $config ) = @_; + my %conf = (%{$DEF}, %{$config}); + my( $min_x, $max_x, $min_y, $max_y ) = get_ranges( $ps, $0 eq 'ch-2.pl' ? [] : $ls ); + my $margin = $conf{'margin'}; + + ## Adjust height and width so it fits the size from the config. + my($W,$H,$width,$height) = ($conf{'max_w'},$conf{'max_h'},$max_x-$min_x+2*$margin,$max_y-$min_y+2*$margin); + ( $width/$height > $W/$H ) ? ( $H = $height/$width*$W ) : ( $W = $width/$height*$H ); + ## Calculate the scale factor so that we keep spots/lines the same size irrespective of the ranges. + my $sf = $width/$W; + + sprintf $SVG_TEMPLATE, + $H, $W, $min_x - $margin, $min_y - $margin, $width, $height, ## svg element + $conf{'border'}, $sf, $conf{'bg'}, $min_x - $margin, $min_y - $margin, $width, $height, ## bg rect + $conf{'color'}, $conf{'stroke'} * $sf, join( qq(\n ), map { sprintf $LINE_TEMPLATE, @{$_} } @{$ls} ), ## lines + $conf{'fill'}, join( qq(\n ), map { sprintf $POINT_TEMPLATE, @{$_}, $conf{'radius'}*$sf } @{$ps} ) ## points +} + diff --git a/challenge-165/james-smith/perl/object-orientated.pl b/challenge-165/james-smith/perl/object-orientated.pl new file mode 100644 index 0000000000..790507747b --- /dev/null +++ b/challenge-165/james-smith/perl/object-orientated.pl @@ -0,0 +1,28 @@ +#!/usr/local/bin/perl +use strict; + +use warnings; +use feature qw(say); +use Cwd qw(getcwd); + +BEGIN { push @INC, getcwd; }; +use SVG; + +my $config = { + 'margin' => 40, 'max_w' => 960, 'max_h' => 540, # Size of image & margins + 'stroke' => 5, 'color' => '#900', # Style for lines + 'radius' => 10, 'fill' => 'rgba(0,153,0,0.5)', # Style for dots + 'border' => '#009', 'bg' => '#ffd', # Style for "page".... +}; + +my $method = $0 eq 'fit.pl' ? 'render_with_best_fit' : 'render'; +say sprintf '<html> + <head> + <title>Examples: $0</title> + </head> + <body> + <h1>SVG examples: %s</h1> + %s + </body> +</html>', $0, join "\n ", map { "<h2>$_</h2>". SVG->new( $config )->load_data( $_ )->$method } @ARGV ; + diff --git a/challenge-165/james-smith/perl/plot.pl b/challenge-165/james-smith/perl/plot.pl new file mode 120000 index 0000000000..47aed1db5e --- /dev/null +++ b/challenge-165/james-smith/perl/plot.pl @@ -0,0 +1 @@ +object-orientated.pl
\ No newline at end of file |
