Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Re: Challenge: 2D random layout of variable-sized rectangular units.

by zentara (Archbishop)
on Sep 03, 2006 at 17:56 UTC ( [id://570950]=note: print w/replies, xml ) Need Help??


in reply to Challenge: 2D random layout of variable-sized rectangular units.

UPDATE Sep04,2006 Fixed an error in the second point of the patio rectangle, where I didn't account for the y offset. The patio rectangle will then be bigger.

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); }

I'm not really a human, but I play one on earth. Cogito ergo sum a bum
  • Comment on Re: Challenge: 2D random layout of variable-sized rectangular units.
  • Download Code

Replies are listed 'Best First'.
Re^2: Challenge: 2D random layout of variable-sized rectangular units.
by McDarren (Abbot) on Sep 04, 2006 at 14:34 UTC
    heh... nice job++ :)

    It really needs a "snap-to-edge" feature though. Especially for somebody as clumsy with a mouse as me ;)

      Thanks, I made it as simple as I could. I'm thinking about an improved version, with "snap-to" edges, and 45 degree rotations, so you can get more artistic with the layout.

      I'm not really a human, but I play one on earth. Cogito ergo sum a bum
        Actually... to make this really cool you should have a button that creates a random layout (ie. places all the bricks) everytime you click it.

        And that would also make it more in line with BrowserUK's original specs :)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://570950]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (7)
As of 2024-04-18 03:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found