Well, I figure this is more of an "artistic" endeavor, than 1 of pure mathematical fitting. So of course, I made a Tk visualizer, which allows you do drag pavers from stacks of 99, and rotate them 90 degrees with a right click. Postscript saving included.
In order to get a convenient scaling of cm-to-pixel, I've scaled it to cm.
#!/usr/bin/perl
use warnings;
use strict;
use Tk;
# USAGE: $0 x-dimension y-dimension
# in meters e.g. $0 8.65 4.1
# Your going to lay a patio using brand of precast concrete
# "stone effect" pavers that come in a range of sizes:
# in centimeters to make screen sizing easier, 1 cm per pixel
my %ss = (
'a' => [30,30,'hotpink'],
'b' => [45,30,'red'],
'c' => [45,45,'lightblue'],
'd' => [60,45,'pink'],
'e' => [60,60,'white'],
'f' => [60,30,'lightgreen'],
'g' => [75,60,'wheat1'],
'h' => [75,75,'khaki'],
'i' => [90,60,'grey50'],
);
# print $ss{'a'}->[0] , $ss{'a'}->[1], $ss{'a'}->[2],"\n";
# print @{ $ss{'a'} },"\n";
#The size of the patio as input can be 'rounded up' in either
#or both dimensions to the next of the greatest
#common divisor (GCD) of the list of sizes.
#(This puts all cuts to be made in the actual
#patio at one of the four edges).
#For example. The GCD of the 9 sizes above is 150mm.
#This forms a minimum grid for the layout. If the patio size
#entered was 8.65 x 4.1, then this would be round up to
# my $patio_xin = 8.65;
# my $patio_yin = 4.1;
# my $patio_x = 0.15 * ( 1 + int( $patio_xin / 0.15 ) );
# my $patio_y = 0.15 * ( 1 + int( $patio_yin / 0.15 ) );
# print "$patio_x $patio_y\n"; # 8.7 x 4.2
my $patio_xin = shift || 8.65;
my $patio_yin = shift || 4.1;
my $patio_x = 0.15 * ( 1 + int( $patio_xin / 0.15 ) );
my $patio_y = 0.15 * ( 1 + int( $patio_yin / 0.15 ) );
my $dx; #globals used for dragging
my $dy;
my $ptag;
my $patio;
my $mw = MainWindow->new;
#$mw->geometry("700x600");
$mw->fontCreate('big',
-family=>'arial',
-weight=>'bold',
-size=>int(-18*18/14));
my $topframe = $mw->Frame(-bg=>'black')->pack(-fill=>'x');
$topframe->Label(-text=>"X-Y = $patio_x - $patio_y (rounded)",
-bg => 'black',
-fg => 'lightblue',
)->pack(-side=>'left');
$topframe->Label(-text=>' ',
-bg => 'black',
)->pack(-side=>'left');
$topframe->Label(-text=>'Drag pavers with left button, rotate them wit
+h a right click',
-bg => 'black',
-fg => 'lightgreen',
)->pack(-side=>'left');
my $S_canvas = $mw->Scrolled('Canvas',
-width => 600, -height => 600,
-bg => 'black',
-borderwidth => 3,
-relief => 'sunken',
-scrollbars => 'osoe',
-scrollregion => [ 0, 0, 1000, 1000 ],
)->pack(-expand => 1, -fill =>'both');
my $canvas = $S_canvas->Subwidget('scrolled');
my $closebutton = $mw->Button(-text => 'Exit', -command => sub{Tk::exi
+t(0)})
->pack;
my $xdiff = 0;
my $x0 = 20; #slight offset
my @count = reverse(1..99);
foreach my $num (@count){
foreach my $paver ('a'..'i'){
my($x,$y,$color) = @{ $ss{ $paver } };
# print "$x,$y,$color\n";
$canvas->createRectangle($x0, 0, $x0 + $x, $y,
-fill => $color,
-tags => ['move', "$paver$num", 'paver'],
);
$canvas->createText($x0 + ($x/2) , $y/2,
-anchor=>'center',
-fill => 'black',
-text => "$paver$num",
-font => 'big',
-tags => ['move', "$paver$num", 'paver'],
);
$x0 += $x;
}
$x0 = 20;
}
# rectangle to simulate patio for dragging pavers to
#$canvas->createRectangle(15, 150, 15+$patio_x*100, $patio_y*100,
# Bugfix here... added 150 to second y point
$canvas->createRectangle(15, 150, 15+$patio_x*100, 150 + $patio_y*100,
+
-fill => 'grey60',
-outline => 'white',
-width => 2,
-tags => ['patio'],
);
$canvas->lower('patio', 'paver');
$canvas->configure(-width => $patio_x * 100 + 20,
-height=> $patio_y * 100 + 150);
$canvas->configure(-scrollregion => [0,0,$patio_x * 100 + 120,$patio_y
+ * 100 + 250]);
$canvas->bind('move', '<1>', sub {&mobileStart();});
$canvas->bind('move', '<B1-Motion>', sub {&mobileMove();});
$canvas->bind('move', '<ButtonRelease-1>', sub {&mobileStop();});
$canvas->bind('paver','<3>', sub {
my(@tags) = $canvas->gettags("current");
@tags = grep{/^\w{1}\d+$/} @tags;
my $paver_tag = $tags[0];
&rotate_rect($paver_tag,-90);
});
#postscript save
$topframe->Button(
-text => "Save as postscript",
-command => [sub {
$canvas->update;
my @capture=();
my ($x0,$y0,$x1,$y1)=$canvas->bbox('all');
$y0 = 150; #lop off paver stacks
@capture=('-x'=>$x0,'-y'=>$y0,-height=>$y1-$y0,-width=>$x1-$x0);
my $filename = $patio_x.'-'.$patio_y.'--'.time.'.ps';
$canvas->postscript(-colormode=>'color',
-file=> $filename,
-rotate=>0,
-width=>$patio_x + 100,
-height=>$patio_y + 100,
@capture);
}
] )->pack;
MainLoop;
#####################################################
sub mobileStart {
my $ev = $canvas->XEvent;
($dx, $dy) = (0 - $ev->x, 0 - $ev->y);
my(@tags) = $canvas->gettags("current");
@tags = grep{/^\w{1}\d+$/} @tags;
$ptag = $tags[0];
# print "START MOVE-> $dx $dy\n";
}
############################################################
sub mobileMove {
my $ev = $canvas->XEvent;
$canvas->move($ptag, $ev->x + $dx, $ev->y +$dy);
($dx, $dy) = (0 - $ev->x, 0 - $ev->y);
# print "MOVING-> $dx $dy\n";
}
##############################################################
sub mobileStop{ $ptag = ''; &mobileMove;}
#############################################################
sub rotate_rect {
# adapted from Ala Qumsieh's ROTcanvas
my ( $tag, $angle) = @_;
# print "@_\n";
# Get the old coordinates.
my @coords = $canvas->coords($tag);
# Get the center of the rect. We use this to translate the
# above coords back to the origin, and then rotate about
# the origin, then translate back. (old)
my $midx =$coords[0] + ($coords[2] - $coords[0])/2;
my $midy =$coords[1] + ($coords[3] - $coords[1])/2;
# print "@coords $midx $midy\n";
my @new;
# Precalculate the sin/cos of the angle, since we'll call
# them a few times.
my $rad = 3.1416*$angle/180;
my $sin = sin $rad;
my $cos = cos $rad;
# Calculate the new coordinates of the line.
while (my ($x, $y) = splice @coords, 0, 2) {
my $x1 = $x - $midx;
my $y1 = $y - $midy;
push @new => $midx + ($x1 * $cos - $y1 * $sin);
push @new => $midy + ($x1 * $sin + $y1 * $cos);
}
# Redraw the rect
$canvas->coords($tag, @new);
}