From 38000c6f72163eba496485b2eb4cc542ed2f0734 Mon Sep 17 00:00:00 2001 From: "E. Choroba" Date: Mon, 23 May 2022 10:06:16 +0200 Subject: Solve 165: Scalable Vector Graphics (SVG) & Line of Best Fit --- challenge-165/e-choroba/perl/ch-1.pl | 105 +++++++++++++++++++++++++++++++++++ challenge-165/e-choroba/perl/ch-2.pl | 60 ++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100755 challenge-165/e-choroba/perl/ch-1.pl create mode 100755 challenge-165/e-choroba/perl/ch-2.pl diff --git a/challenge-165/e-choroba/perl/ch-1.pl b/challenge-165/e-choroba/perl/ch-1.pl new file mode 100755 index 0000000000..9a22702560 --- /dev/null +++ b/challenge-165/e-choroba/perl/ch-1.pl @@ -0,0 +1,105 @@ +#!/usr/bin/perl +use warnings; +use strict; + +use Template; + +my $HELP = << '__HELP__'; +Usage: $0 input_file [width height] + +Use - for input_file to read standard input. +Width and height default to 400x300. +__HELP__ + +my $TEMPLATE = << '__SVG__'; + + + +[% IF lines -%] + + [%- FOREACH line IN lines %] + + [%- END %] + +[% END- %] +[%- IF points -%] + + [%- FOREACH point IN points %] + + [%- END %] + +[%- END %] + +__SVG__ + +my $file = shift; +if (! defined $file || $file =~ /^(?:-h|--help)$/) { + print $HELP; + exit ! defined $file +} + +my $width = shift || 400; +my $height = shift || 300; + +my ($min_x, $max_x, $min_y, $max_y); +my (@points, @lines); +my $FLOAT = qr/-?(?:[0-9]*(?:\.[0-9]+)?+)/; + +my $in; +if ('-' eq $file) { + $in = *STDIN; +} else { + open $in, '<', $file or die "$file: $!"; +} + +while (<$in>) { + if (/^($FLOAT),($FLOAT),($FLOAT),($FLOAT)$/) { + my ($x0, $y0, $x1, $y1) = ($1, $2, $3, $4); + push @lines, [$x0, $y0, $x1, $y1]; + for my $x ($x0, $x1) { + $min_x = $x if ! defined $min_x || $x < $min_x; + $max_x = $x if ! defined $max_x || $x > $max_x; + } + for my $y ($y0, $y1) { + $min_y = $y if ! defined $min_y || $y < $min_y; + $max_y = $y if ! defined $max_y || $y > $max_y; + } + + } elsif (/^($FLOAT),($FLOAT)$/) { + my ($x, $y) = ($1, $2); + push @points, [$x, $y]; + $min_x = $x if ! defined $min_x || $x < $min_x; + $max_x = $x if ! defined $max_x || $x > $max_x; + $min_y = $y if ! defined $min_y || $y < $min_y; + $max_y = $y if ! defined $max_y || $y > $max_y; + + } else { + warn "WARN: Ignoring: $_"; + } +} + +my $scale_x = ($max_x - $min_x) / $width; +my $scale_y = ($max_y - $min_y) / $height; +$_ ||= 1 for $scale_x, $scale_y; + +for my $point (@points) { + $point->[0] = ($point->[0] - $min_x) / $scale_x; + $point->[1] = ($point->[1] - $min_y) / $scale_y; +} +for my $line (@lines) { + $line->[$_] = ($line->[$_] - $min_x) / $scale_x for 0, 2; + $line->[$_] = ($line->[$_] - $min_y) / $scale_y for 1, 3; +} + +my $template = 'Template'->new; +$template->process(\$TEMPLATE, + {height => $height, + width => $width, + points => \@points, + lines => \@lines} +) or die $template->error; + +__DATA__ +53,10 +53,10,23,30 +23,30 diff --git a/challenge-165/e-choroba/perl/ch-2.pl b/challenge-165/e-choroba/perl/ch-2.pl new file mode 100755 index 0000000000..700420a82c --- /dev/null +++ b/challenge-165/e-choroba/perl/ch-2.pl @@ -0,0 +1,60 @@ +#!/usr/bin/perl +use warnings; +use strict; +use feature qw{ say }; + +use ARGV::OrDATA; +use List::Util qw{ sum }; + +=head1 Usage + +ch-2.pl input_file | ch-1.pl - > out.svg + +=cut + +my @points; +while (<>) { + push @points, map [split /,/], split; +} + +my ($min_x, $max_x, $min_y, $max_y) + = (($points[0][0]) x 2, ($points[0][1]) x 2); +for my $point (@points[1 .. $#points]) { + $min_x = $point->[0] if $point->[0] < $min_x; + $min_y = $point->[1] if $point->[1] < $min_y; + $max_x = $point->[0] if $point->[0] > $max_x; + $max_y = $point->[1] if $point->[1] > $max_y; +} + +my $sum_x = sum(map $_->[0], @points); +my $sum_y = sum(map $_->[1], @points); +my $sum_x_square = sum(map $_->[0] * $_->[0], @points); +my $sum_xy = sum(map $_->[0] * $_->[1], @points); + +my $divisor = @points * $sum_x_square - $sum_x * $sum_x; +my $slope = (@points * $sum_xy - $sum_x * $sum_y) + / ($divisor || 1); + +my $intercept = ($sum_y - $slope * $sum_x) / @points; + +my @line; +push @line, ($_, $intercept + $_ * $slope) for $min_x, $max_x; + +# Vertical. +if ($line[0] == $line[2] && $line[1] == $line[3]) { + $line[1] = $points[0][1]; + $line[3] = $points[1][1]; +} + +{ local $, = ','; + say @$_ for @points; + say @line; +} + +__DATA__ +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