diff options
52 files changed, 1074 insertions, 386 deletions
diff --git a/challenge-162/james-smith/README.md b/challenge-162/james-smith/README.md index 248d432347..3ecb201fd9 100644 --- a/challenge-162/james-smith/README.md +++ b/challenge-162/james-smith/README.md @@ -51,7 +51,7 @@ sub decrypt { return _crypt(-1,@_); } sub _crypt { my($off,$key,$p,$out,@r,%l) = (shift,shift,0,''); ## Initialise variables and get mapping... - ($_ eq 'j' && ($_='i')), exists $l{$_} || ($l{$_}=[int $p/5,($p++)%5]) for grep { /[a-z]/ } split(//,$key),'a'..'i','j'..'z'; + ($_ eq 'j' && ($_='i')), exists $l{$_} || ($l{$_}=[int $p/5,($p++)%5]) for grep { /[a-z]/ } split(//,$key),'a'..'i','k'..'z'; $r[$l{$_}[0]][$l{$_}[1]]=$_ for keys %l; my @seq = grep {/[a-z]/} split //, shift =~ s{j}{i}gr; ## Prep sequence diff --git a/challenge-165/james-smith/README.md b/challenge-165/james-smith/README.md index 065063da5b..13cccb2af9 100644 --- a/challenge-165/james-smith/README.md +++ b/challenge-165/james-smith/README.md @@ -310,6 +310,12 @@ sub add_best_fit_line { ## ## 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 +## +## We add a transform round everything to flip the direction of the points to the usual low to high rather than high to low +## ( y-axis -ve values are above +ve values ) +## +## This and the scaling allows us to plot points with the "correct" value but at the same time scale the image to the +## appropriate image size... sub render_svg { my( $ps, $ls, $config ) = @_; @@ -328,19 +334,218 @@ sub render_svg { <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 transform="scale(1,-1) translate(0,%s)"> <g stroke="%s" stroke-width="%s"> %s </g> <g fill="%s"> %s </g> + </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, + -$min_y-$max_y, $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} ) } ``` + +# Object oriented version + +Although recently object-oriented coding has been discussed on the perl mailing lists - this is an example where OO code can make some things easier. + +Rather than using global variables - and some "hacky" passing around we can store a lot more of these on the object itself. + +We will create an object of class "SVG", this class has methods which will initialise, load data, and render with or without the best fit line. +The code is this. + +```perl +#!/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 ; +``` + +Tthe important lines are: + +```perl +my $method = $0 eq 'fit.pl' ? 'render_with_best_fit' : 'render'; +say SVG->new( $config )->load_data( $fn )->$method; +``` + +which choose which method we are going to use to render (fit.pl - fit line). Once we have decided this we create and configure the SVG object, load the data in and render... + +The class code is similar to the functional code but the arrays of points and lines, the configuration and additional information about the size of the image are stored in the object so don't have to be passed around as function parameters or left as global variables which makes things. + +There is some advantages to the logic this way to allow us to avoid some code duplication - without having to routinely pass additional variables into functions. + +A good example of this is that we store the bounding box information in the object itself - so we don't have to recalculate this twice in the regression example. + +Other advantages are having the direct accessors to the bounding box of the image {based on the range of points + the marging} + +```perl +package SVG; +use warnings; +use feature qw(say); +use Data::Dumper qw(Dumper); +use Const::Fast qw(const); + +## ----------------------------------- +## Constants - configuration & SVG templates... +## ----------------------------------- + +const my %DEF_CNF => ( 'margin' => 40, 'max_w' => 960, 'max_h' => 540, 'color' => '#000', 'stroke' => 3, + 'fill' => '#ccc', 'radius' => 10, 'border' => '#000', 'bg' => '#eee' ); +const my $JOIN => "\n "; +const my $LN_TMPL => '<line x1="%0.4f" y1="%0.4f" x2="%0.4f" y2="%0.4f" />'; +const my $PT_TMPL => '<circle cx="%0.4f" cy="%0.4f" r="%0.4f" />'; +const my $SVG_TMPL => '<?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="%0.4f" width="%0.4f" viewBox="%0.4f %0.4f %0.4f %0.4f" 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="%0.4f" fill="%s" x="%0.4f" y="%0.4f" width="%0.4f" height="%0.4f" /> + <g transform="scale(1,-1) translate(0,%0.4f)"> + <g stroke="%s" stroke-width="%0.4f"> + %s + </g> + <g fill="%s"> + %s + </g> + </g> +</svg>'; + + +## ----------------------------------- +## Constructor +## ----------------------------------- + +sub new { + bless my$s={'cnf'=>{%DEF_CNF},'scale'=>1,'points'=>[],'lines'=>[],'range'=>[],'size'=>[]},$_[0]; + $s->update_cnf(%{$_[1]||{}})->set_size($s->max_w,$s->max_h)->set_range(0,0,$s->max_w,$s->max_h ) +} + +## ----------------------------------- +## Getters.... +## ----------------------------------- + +sub min_x { $_[0]{'range'}[0] } sub min_y { $_[0]{'range'}[1] } +sub max_x { $_[0]{'range'}[2] } sub max_y { $_[0]{'range'}[3] } +sub width { $_[0]{'size'}[0] } sub height { $_[0]{'size'}[1] } +sub points { @{$_[0]{'points'}} } sub lines { @{$_[0]{'lines'}} } +sub scale { $_[0]{'scale'} } sub cnf { $_[0]{'cnf'}{$_[1]} } +sub max_w { $_[0]->cnf('max_w') } sub max_h { $_[0]->cnf('max_h') } +sub color { $_[0]->cnf('color') } sub stroke { $_[0]->cnf('stroke') } +sub fill { $_[0]->cnf('fill') } sub radius { $_[0]->cnf('radius') } +sub border { $_[0]->cnf('border') } sub bg { $_[0]->cnf('bg') } +sub margin { $_[0]->cnf('margin') } +sub bb_l { $_[0]->min_x-$_[0]->margin } sub bb_r { $_[0]->max_x+$_[0]->margin } +sub bb_b { $_[0]->min_y-$_[0]->margin } sub bb_t { $_[0]->max_y+$_[0]->margin } +sub bb_w { $_[0]->bb_r-$_[0]->bb_l } sub bb_h { $_[0]->bb_t-$_[0]->bb_b } + +## ----------------------------------- +## Setters.. +## ----------------------------------- + +sub add_point { my $s = shift; push @{$s->{'points'}}, [@_]; $s } +sub add_points { my $s = shift; push @{$s->{'points'}}, @_; $s } +sub add_line { my $s = shift; push @{$s->{'lines'}}, [@_]; $s } +sub add_lines { my $s = shift; push @{$s->{'lines'}}, @_; $s } +sub set_size { my $s = shift; $s->{'size'} = [@_]; $s } +sub set_range { my $s = shift; $s->{'range'} = [@_]; $s } +sub set_scale { my $s = shift; $s->{'scale'} = $_[0]; $s } + +sub update_cnf {my($s,%p)=@_; exists$s->{'cnf'}{$_}&&($s->{'cnf'}{$_}=$p{$_}) for keys %p; $s} + +## -------------------------------------------------------- +## The real code - no direct references to the $s hash! +## -------------------------------------------------------- + +sub load_data { + local $/=undef, my( $s, $fn, @t ) = @_; + open my $ifh, '<', $fn; + 4==(@t = split /,/) ? ($s->add_line(@t)) : 2==@t ? ($s->add_point(@t)) : (warn "Error: $_") + for grep { /\S/ } split /\s+/, <$ifh>; + close $ifh; + $s; +} + +sub compute_range { + my $s = shift; + my( $min_x,$min_y ) = my( $max_x,$max_y ) = @{ $s->points ? ($s->points)[0] : ($s->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 $s->points, map { ($_, [$_->[2],$_->[3]]) } $s->lines; + + $s->set_range( $min_x, $min_y, $max_x, $max_y ); +} + +sub add_line_of_best_fit { + my $s = shift; + my $sx = my $sy = my $sxy = my $sxx = 0, my $n = $s->points; + + $sx += $_->[0], $sxy += $_->[0]*$_->[1], $sy += $_->[1], $sxx += $_->[0]*$_->[0] for $s->points; + + return $s->add_line( $sx/$n, $s->bb_b, $sx/$n, $s->bb_t ) unless $n*$sxx - $sx*$sx; + + my $b = ( $n*$sxy-$sx*$sy ) / ( $n*$sxx - $sx*$sx ); + my ( $a, $l, $r, $d, $t ) = ( ( $sy-$b*$sx ) / $n, $s->bb_l, $s->bb_r, $s->bb_b, $s->bb_t ); + my ( $l_y, $r_y ) = ( $a+$b*$l, $a+$b*$r ); + my ( $l_x, $r_x ) = ( $l_y<$d ? (($l_y=$d)-$a)/$b : $l_y>$t ? (($l_y=$t)-$a)/$b : $l, + $r_y<$d ? (($r_y=$d)-$a)/$b : $r_y>$t ? (($r_y=$t)-$a)/$b : $r ); + + $s->add_line( $l_x, $l_y, $r_x, $r_y ); +} + +sub calculate_image_size { + my $s = shift; + + my( $W, $H, $w, $h ) = ( $s->max_w, $s->max_h, $s->bb_w, $s->bb_h ); + + ( $w/$h > $W/$H ) ? $s->set_size($W,$h/$w*$W)->set_scale($w/$W) + : $s->set_size($w/$h*$H,$H)->set_scale($h/$H); +} + +sub render_with_best_fit { shift->compute_range->add_line_of_best_fit->_render } +sub render { shift->compute_range->_render } + +sub _render { + my $s = shift; + my $m = $s->calculate_image_size->margin; + + sprintf $SVG_TMPL, $s->height, $s->width, $s->bb_l, $s->bb_b, $s->bb_w, $s->bb_h, + $s->border, $s->scale, $s->bg, $s->bb_l, $s->bb_b, $s->bb_w, $s->bb_h, -$s->bb_b-$s->bb_t, + $s->color, $s->stroke * $s->scale, join( $JOIN, map { sprintf $LN_TMPL, @{$_} } $s->lines ), + $s->fill, join( $JOIN, map { sprintf $PT_TMPL, @{$_}, $s->radius*$s->scale } $s->points ) +} + +1; +``` diff --git a/challenge-165/james-smith/perl/SVG.pm b/challenge-165/james-smith/perl/SVG.pm new file mode 100644 index 0000000000..99c03be04a --- /dev/null +++ b/challenge-165/james-smith/perl/SVG.pm @@ -0,0 +1,136 @@ +package SVG; +use warnings; +use feature qw(say); +use Data::Dumper qw(Dumper); +use Const::Fast qw(const); + +## ----------------------------------- +## Constants - configuration & SVG templates... +## ----------------------------------- + +const my %DEF_CNF => ( 'margin' => 40, 'max_w' => 960, 'max_h' => 540, 'color' => '#000', 'stroke' => 3, + 'fill' => '#ccc', 'radius' => 10, 'border' => '#000', 'bg' => '#eee' ); +const my $JOIN => "\n "; +const my $LN_TMPL => '<line x1="%0.4f" y1="%0.4f" x2="%0.4f" y2="%0.4f" />'; +const my $PT_TMPL => '<circle cx="%0.4f" cy="%0.4f" r="%0.4f" />'; +const my $SVG_TMPL => '<?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="%0.4f" width="%0.4f" viewBox="%0.4f %0.4f %0.4f %0.4f" 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="%0.4f" fill="%s" x="%0.4f" y="%0.4f" width="%0.4f" height="%0.4f" /> + <g transform="scale(1,-1) translate(0,%0.4f)"> + <g stroke="%s" stroke-width="%0.4f"> + %s + </g> + <g fill="%s"> + %s + </g> + </g> +</svg>'; + + +## ----------------------------------- +## Constructor +## ----------------------------------- + +sub new { + bless my$s={'cnf'=>{%DEF_CNF},'scale'=>1,'points'=>[],'lines'=>[],'range'=>[],'size'=>[]},$_[0]; + $s->update_cnf(%{$_[1]||{}})->set_size($s->max_w,$s->max_h)->set_range(0,0,$s->max_w,$s->max_h ) +} + +## ----------------------------------- +## Getters.... +## ----------------------------------- + +sub min_x { $_[0]{'range'}[0] } sub min_y { $_[0]{'range'}[1] } +sub max_x { $_[0]{'range'}[2] } sub max_y { $_[0]{'range'}[3] } +sub width { $_[0]{'size'}[0] } sub height { $_[0]{'size'}[1] } +sub points { @{$_[0]{'points'}} } sub lines { @{$_[0]{'lines'}} } +sub scale { $_[0]{'scale'} } sub cnf { $_[0]{'cnf'}{$_[1]} } +sub max_w { $_[0]->cnf('max_w') } sub max_h { $_[0]->cnf('max_h') } +sub color { $_[0]->cnf('color') } sub stroke { $_[0]->cnf('stroke') } +sub fill { $_[0]->cnf('fill') } sub radius { $_[0]->cnf('radius') } +sub border { $_[0]->cnf('border') } sub bg { $_[0]->cnf('bg') } +sub margin { $_[0]->cnf('margin') } +sub bb_l { $_[0]->min_x-$_[0]->margin } sub bb_r { $_[0]->max_x+$_[0]->margin } +sub bb_b { $_[0]->min_y-$_[0]->margin } sub bb_t { $_[0]->max_y+$_[0]->margin } +sub bb_w { $_[0]->bb_r-$_[0]->bb_l } sub bb_h { $_[0]->bb_t-$_[0]->bb_b } + +## ----------------------------------- +## Setters.. +## ----------------------------------- + +sub add_point { my $s = shift; push @{$s->{'points'}}, [@_]; $s } +sub add_points { my $s = shift; push @{$s->{'points'}}, @_; $s } +sub add_line { my $s = shift; push @{$s->{'lines'}}, [@_]; $s } +sub add_lines { my $s = shift; push @{$s->{'lines'}}, @_; $s } +sub set_size { my $s = shift; $s->{'size'} = [@_]; $s } +sub set_range { my $s = shift; $s->{'range'} = [@_]; $s } +sub set_scale { my $s = shift; $s->{'scale'} = $_[0]; $s } + +sub update_cnf {my($s,%p)=@_; exists$s->{'cnf'}{$_}&&($s->{'cnf'}{$_}=$p{$_}) for keys %p; $s} + +## -------------------------------------------------------- +## The real code - no direct references to the $s hash! +## -------------------------------------------------------- + +sub load_data { + local $/=undef, my( $s, $fn, @t ) = @_; + open my $ifh, '<', $fn; + 4==(@t = split /,/) ? ($s->add_line(@t)) : 2==@t ? ($s->add_point(@t)) : (warn "Error: $_") + for grep { /\S/ } split /\s+/, <$ifh>; + close $ifh; + $s; +} + +sub compute_range { + my $s = shift; + my( $min_x,$min_y ) = my( $max_x,$max_y ) = @{ $s->points ? ($s->points)[0] : ($s->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 $s->points, map { ($_, [$_->[2],$_->[3]]) } $s->lines; + + $s->set_range( $min_x, $min_y, $max_x, $max_y ); +} + +sub add_line_of_best_fit { + my $s = shift; + my $sx = my $sy = my $sxy = my $sxx = 0, my $n = $s->points; + + $sx += $_->[0], $sxy += $_->[0]*$_->[1], $sy += $_->[1], $sxx += $_->[0]*$_->[0] for $s->points; + + return $s->add_line( $sx/$n, $s->bb_b, $sx/$n, $s->bb_t ) unless $n*$sxx - $sx*$sx; + + my $b = ( $n*$sxy-$sx*$sy ) / ( $n*$sxx - $sx*$sx ); + my ( $a, $l, $r, $d, $t ) = ( ( $sy-$b*$sx ) / $n, $s->bb_l, $s->bb_r, $s->bb_b, $s->bb_t ); + my ( $l_y, $r_y ) = ( $a+$b*$l, $a+$b*$r ); + my ( $l_x, $r_x ) = ( $l_y<$d ? (($l_y=$d)-$a)/$b : $l_y>$t ? (($l_y=$t)-$a)/$b : $l, + $r_y<$d ? (($r_y=$d)-$a)/$b : $r_y>$t ? (($r_y=$t)-$a)/$b : $r ); + + $s->add_line( $l_x, $l_y, $r_x, $r_y ); +} + +sub calculate_image_size { + my $s = shift; + + my( $W, $H, $w, $h ) = ( $s->max_w, $s->max_h, $s->bb_w, $s->bb_h ); + + ( $w/$h > $W/$H ) ? $s->set_size($W,$h/$w*$W)->set_scale($w/$W) + : $s->set_size($w/$h*$H,$H)->set_scale($h/$H); +} + +sub render_with_best_fit { shift->compute_range->add_line_of_best_fit->_render } +sub render { shift->compute_range->_render } + +sub _render { + my $s = shift; + my $m = $s->calculate_image_size->margin; + + sprintf $SVG_TMPL, $s->height, $s->width, $s->bb_l, $s->bb_b, $s->bb_w, $s->bb_h, + $s->border, $s->scale, $s->bg, $s->bb_l, $s->bb_b, $s->bb_w, $s->bb_h, -$s->bb_b-$s->bb_t, + $s->color, $s->stroke * $s->scale, join( $JOIN, map { sprintf $LN_TMPL, @{$_} } $s->lines ), + $s->fill, join( $JOIN, map { sprintf $PT_TMPL, @{$_}, $s->radius*$s->scale } $s->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 |
