commit 5cfb7d471001264e573e86cbd8027e9c5e2043d9
parent 2a08c834aab71af59167c60f71aa060bafc67c96
Author: Kyle Milz <kyle@getaddrinfo.net>
Date:   Mon,  4 May 2015 23:41:21 -0600
pc_html: use matrix multiplication instead of hand rolling
Diffstat:
| M | DEPS |  |  | 1 | + | 
| M | pc_html |  |  | 75 | ++++++++++++++++++++++++++++++++++----------------------------------------- | 
2 files changed, 35 insertions(+), 41 deletions(-)
diff --git a/DEPS b/DEPS
@@ -7,6 +7,7 @@ p5-Getopt-Std
 p5-HTML-Grabber
 p5-IO-Tee
 p5-Lingua-EN-Inflect
+p5-Math-MatrixReal
 p5-SVG
 p5-Template
 p5-Term-ReadKey
diff --git a/pc_html b/pc_html
@@ -6,6 +6,7 @@ use warnings;
 use Config::Grammar;
 use Getopt::Std;
 use Lingua::EN::Inflect qw(PL);
+use Math::MatrixReal;
 use POSIX;
 use PriceChart;
 use SVG;
@@ -35,6 +36,13 @@ my $where_stale = $args{a} ? "" : "where svg_stale = 1";
 my $part_equality = qq{prices.manufacturer = products.manufacturer and
 	prices.part_num = products.part_num};
 
+# catmull-rom to cubic bezier conversion matrix
+my $catrom_to_bezier = Math::MatrixReal->new_from_rows([[0,    1,   0,    0],
+                                                        [-1/6,  1, 1/6,    0],
+					                [0,   1/6,   1, -1/6],
+					                [0,     0,   1,    0]]);
+my $m_t = ~$catrom_to_bezier;
+
 #
 # manufacturers
 #
@@ -358,22 +366,20 @@ sub make_svg
 		my $retailer_id = lc($retailer);
 		$retailer_id =~ s/ /_/;
 
-		my (@xs, @ys, @pts);
+		my (@xs, @ys);
 		for (sort keys %{$values}) {
 			my ($x, $y) = ($_, $values->{$_}{price});
 			push @xs, sprintf "%.3f", ($x - $x_min) * $x_scale + $left;
 			push @ys, sprintf "%.3f", $height - $bottom - ($y - $y_min) * $y_scale;
-			push @pts, $xs[-1];
-			push @pts, $ys[-1];
 		}
 
-		if (@pts < 6) {
+		if (@xs < 3) {
 			my $points = $svg->get_path(x => \@xs, y => \@ys, -type => "path");
 			$defs->path(%$points, id => "path_$retailer_id");
 		}
 		else {
 			# catmull rom time
-			my $d = catmullrom_to_bezier(\@pts);
+			my $d = catmullrom_to_bezier(\@xs, \@ys);
 			$defs->tag("path", "d" => $d, id => "path_$retailer_id");
 		}
 
@@ -426,47 +432,34 @@ sub make_svg
 #
 sub catmullrom_to_bezier
 {
-	my $pts_ref = shift;
-	my @pts = @$pts_ref;
-
-	# catmull-rom to cubic bezier conversion matrix
-	# 0    1    0    0
-	# -1/6 1    1/6  0
-	# 0    1/6  1    -1/6
-	# 0    0    1    0
-
-	my $d = "M $pts[0], $pts[1] ";
-	my $iLen = @pts;
-	for (my $i = 0; $iLen - 2 > $i; $i += 2) {
-		my (@xs, @ys);
+	my $xs_ref = shift;
+	my $ys_ref = shift;
+
+	my $d = "M $xs_ref->[0], $ys_ref->[0] ";
+	my $iLen = @$xs_ref;
+	for (my $i = 0; $iLen - 1 > $i; $i++) {
+
+		my @offsets = (-1, 0, 1, 2);
 		if ($i == 0) {
-			push @xs, $pts[$i];	push @ys, $pts[$i + 1];
-			push @xs, $pts[$i];	push @ys, $pts[$i + 1];
-			push @xs, $pts[$i + 2];	push @ys, $pts[$i + 3];
-			push @xs, $pts[$i + 4];	push @ys, $pts[$i + 5];
+			@offsets = (0, 0, 1, 2);
+		} elsif ($i == ($iLen - 2)) {
+			@offsets = (-1, 0, 1, 1);
 		}
-		elsif ($i == ($iLen - 4)) {
-			push @xs, $pts[$i - 2];	push @ys, $pts[$i - 1];
-			push @xs, $pts[$i];	push @ys, $pts[$i + 1];
-			push @xs, $pts[$i + 2];	push @ys, $pts[$i + 3];
-			push @xs, $pts[$i + 2];	push @ys, $pts[$i + 3];
-		}
-		else {
-			push @xs, $pts[$i - 2];	push @ys, $pts[$i - 1];
-			push @xs, $pts[$i];	push @ys, $pts[$i + 1];
-			push @xs, $pts[$i + 2];	push @ys, $pts[$i + 3];
-			push @xs, $pts[$i + 4];	push @ys, $pts[$i + 5];
+
+		my (@xs, @ys);
+		for my $idx (@offsets) {
+			push @xs, $xs_ref->[$i + $idx];
+			push @ys, $ys_ref->[$i + $idx];
 		}
 
-		my (@bp_x, @bp_y);
-		push @bp_x, $xs[1];	push @bp_y, $ys[1];
-		push @bp_x, ((-$xs[0] + 6*$xs[1] + $xs[2]) / 6);
-		push @bp_y, ((-$ys[0] + 6*$ys[1] + $ys[2]) / 6);
-		push @bp_x, (($xs[1] + 6*$xs[2] - $xs[3]) / 6);
-		push @bp_y, (($ys[1] + 6*$ys[2] - $ys[3]) / 6);
-		push @bp_x, $xs[2];	push @bp_y, $ys[2];
+		my $x_row = Math::MatrixReal->new_from_rows([[@xs]]);
+		my $y_row = Math::MatrixReal->new_from_rows([[@ys]]);
+
+		$x_row = $x_row * $m_t;
+		$y_row = $y_row * $m_t;
 
-		$d .= "C $bp_x[1], $bp_y[1] $bp_x[2], $bp_y[2] $bp_x[3], $bp_y[3] ";
+		my ($x, $y) = ($x_row->[0][0], $y_row->[0][0]);
+		$d .= "C $x->[1], $y->[1] $x->[2], $y->[2] $x->[3], $y->[3] ";
 	}
 
 	return $d;