P4m Reference Implementation
back to Exploring_a_Design_Space_for_Patterns_and_Tilings_Competition_2015
Let $image be an image from which a right angle isosceles triangle part should be cut out and used as prototile for the p4m tile. The triangle has with the width $t_w one free parameter, the hight is the same $t_h = $t_w.
In the following relative translation parameters are used that are referencing to the one prototile that is used viz. to $t_w and $t_h. If patterns are generated with more than one prototile relative parameters should referencing to the width and height of the pattern image that itself is constructed as a function of the widths and heights of the prototile set and a general multiplicator or two multiplicators for width and height. Such a function ($w_P, $h_P) = f($mult, $w_S_i, $h_S_i | i = 1, …, n) or f($mult_w, $mult_h, $w_S_i, $h_S_i | i = 1, …, n) is specified in the design space definition.
If a random part of $image should be selected for the rectangle that includes the future prototile this is made with:
# RANDOM CROP A RECTANGLE WITH $t_w x $t_w ($w, $h) = $image->Get('columns', 'rows'); $t_w = $crop_w; $t_h = $crop_w; $x_allowed = $w - $t_w; $x = int(rand($x_allowed)); $y_allowed = $h - $t_h; $y = int(rand($y_allowed)); $image->Crop(geometry=>"$t_w x $t_h + $x + $y", background=>'transparent'); print "crop rand rectangle from image with (geometry=>$t_w x $t_h + $x + $y)\n"; $image->Set(page=>'0x0+0+0'); #$image->Write(filename=>"$output_meta_folder/image_AfterRectCrop.jpg");
The right angle isosceles triangle mask is generated with:
# GENERATE p4m MASK/S $mask->Set(size=>"$t_w x $t_h"); $mask->Read('xc:none'); $mask->Draw(primitive=>'polyline', points => "0,0 $t_w,$t_h 0,$t_h", fill=>'red'); $mask->Write(filename=>"$output_meta_folder/p4m_mask_AfterGeneration.png");
The prototile is generated with the triangle mask and the image and the rusult is pushed in a image list for all the prototiles to reflect the general case that a pattern is made of more than one prototile type:
# GENERATE PROTOTILE/S $mask->Composite(image=>$image, compose=>'in', color=>'transparent', matte=>'true'); print "generate prototile \n"; $mask->Set(page=>'0x0+0+0'); # $mask->Write(filename=>"$output_meta_folder/mask_AfterCompose.png");
# PUSH PROTOTILE INTO $Shape_list push(@$Shape_list, $mask);#$Shape_list->Write(filename=>"$output_meta_folder/Shape_list.png"); $nr_Shape_list = @$Shape_list;
In the next step an empty p4m tile is generated for example with transparent background. It has 2 times the width and 2 times the hight of the prototile:
# GENERATE EMPTY p4m-TILE $tile AS A FUNCTION OF $mask or $prototile $tile_w = 2 * $t_w; $tile_h = 2 * $t_h; $tile->Set(size=>"$tile_w x $tile_h"); print "generate empty tile with tile_w x tile_h = $tile_w x $tile_h \n"; $tile->Read('xc:none'); #$tile->Write(filename=>"$output_meta_folder/tile_AfterGeneration.png");
What follows is a sequence of 8 image processing operations, the CRMT commands that takes a clone of the prototile, rotates it, Flop and/or Flip it and makes a composite operation with compose=>over that includes a translation of the prototile over the tile image. The images are shown in jpg which means that the transparent background is transformed to black.
# CRMT-COMMAND-LIST_01: (C0, x0, y0) @$prototile_crm = (); $prototile_crm = $Shape_list->[0]->Clone(); $tile->Composite(image=>$prototile_crm, compose=>'over', x=>'0', y=>'0', color=>'transparent', matte=>'true'); $tile->Set(page=>'0x0+0+0'); #$tile->Write(filename=>"$output_meta_folder/tile_after_crmt_01.png"); $tile->Write(filename=>"$output_meta_folder/tile_after_crmt_01.jpg");
# CRMT-COMMAND-LIST_02: (C0, Fo, R-90, x0, y0) @$prototile_crm = (); $prototile_crm = $Shape_list->[0]->Clone(); $prototile_crm->Flop(); $prototile_crm->Rotate(degrees=>-90, color=>'transparent'); $prototile_crm->Set(page=>'0x0+0+0'); $tile->Composite(image=>$prototile_crm, compose=>'over', x=>'0', y=>'0', color=>'transparent', matte=>'true'); $tile->Set(page=>'0x0+0+0'); #$tile->Write(filename=>"$output_meta_folder/tile_after_crmt_02.png"); $tile->Write(filename=>"$output_meta_folder/tile_after_crmt_02.jpg");
# CRMT-COMMAND-LIST_03: (C0, Fo, xt_w, y0) @$prototile_crm = (); $prototile_crm = $Shape_list->[0]->Clone(); $prototile_crm->Flop(); $tile->Composite(image=>$prototile_crm, compose=>'over', x=>$t_w, y=>'0', color=>'transparent', matte=>'true'); $tile->Set(page=>'0x0+0+0'); #$tile->Write(filename=>"$output_meta_folder/tile_after_crmt_03.png"); $tile->Write(filename=>"$output_meta_folder/tile_after_crmt_03.jpg");
# CRMT-COMMAND-LIST_04: (C0, R90, xt_w, y0) @$prototile_crm = (); $prototile_crm = $Shape_list->[0]->Clone(); $prototile_crm->Rotate(degrees=>90, color=>'transparent'); $prototile_crm->Set(page=>'0x0+0+0'); $tile->Composite(image=>$prototile_crm, compose=>'over', x=>$t_w, y=>'0', color=>'transparent', matte=>'true'); $tile->Set(page=>'0x0+0+0'); #$tile->Write(filename=>"$output_meta_folder/tile_after_crmt_04.png"); $tile->Write(filename=>"$output_meta_folder/tile_after_crmt_04.jpg");
# CRMT-COMMAND-LIST_05: (C0, Fi, x0, yt_h) @$prototile_crm = (); $prototile_crm = $Shape_list->[0]->Clone(); $prototile_crm->Flip(); $tile->Composite(image=>$prototile_crm, compose=>'over', x=>'0', y=>$t_h, color=>'transparent', matte=>'true'); $tile->Set(page=>'0x0+0+0'); #$tile->Write(filename=>"$output_meta_folder/tile_after_crmt_05.png"); $tile->Write(filename=>"$output_meta_folder/tile_after_crmt_05.jpg");
# CRMT-COMMAND-LIST_06: (C0, Fo, R-90, Fi, x0, yt_h) @$prototile_crm = (); $prototile_crm = $Shape_list->[0]->Clone(); $prototile_crm->Flop(); $prototile_crm->Rotate(degrees=>-90, color=>'transparent'); $prototile_crm->Set(page=>'0x0+0+0'); $prototile_crm->Flip(); $tile->Composite(image=>$prototile_crm, compose=>'over', x=>'0', y=>$t_h, color=>'transparent', matte=>'true'); $tile->Set(page=>'0x0+0+0'); #$tile->Write(filename=>"$output_meta_folder/tile_after_crmt_06.png"); $tile->Write(filename=>"$output_meta_folder/tile_after_crmt_06.jpg");
# CRMT-COMMAND-LIST_07: (C0, Fo, Fi, xt_w, yt_h) @$prototile_crm = (); $prototile_crm = $Shape_list->[0]->Clone(); $prototile_crm->Flop(); $prototile_crm->Flip(); $tile->Composite(image=>$prototile_crm, compose=>'over', x=>$t_w, y=>$t_h, color=>'transparent', matte=>'true'); $tile->Set(page=>'0x0+0+0'); #$tile->Write(filename=>"$output_meta_folder/tile_after_crmt_07.png"); $tile->Write(filename=>"$output_meta_folder/tile_after_crmt_07.jpg");
# CRMT-COMMAND-LIST_08: (C0, R90, Fi, xt_w, yt_h) @$prototile_crm = (); $prototile_crm = $Shape_list->[0]->Clone(); $prototile_crm->Rotate(degrees=>90, color=>'transparent'); $prototile_crm->Set(page=>'0x0+0+0'); $prototile_crm->Flip(); $tile->Composite(image=>$prototile_crm, compose=>'over', x=>$t_w, y=>$t_h, color=>'transparent', matte=>'true'); $tile->Set(page=>'0x0+0+0'); #$tile->Write(filename=>"$output_meta_folder/tile_after_crmt_08.png"); $tile->Write(filename=>"$output_meta_folder/tile_after_crmt_08.jpg");
Because a square is used ($t_w = $t_h) for the prototile there are no gap artefacts. The diagonal has a 45° angle and this non rectangular angle produces no (or the less) gap artefacts if an other object with 45° is composed over it along the diagonal. Therefore no correction of gap artefacts are necessary in contrast to other prototile types from other eucledian plane groups that are based on 30°, 60°, ... angles like p3m1 or p6m.
If the image processing commands were combined and integrated one get a string that codes the building of a p4m tile from a given prototile triangle:
$CRMT_string = 'C0, x0, y0, C0, Fo, R-90, x0, y0, C0, Fo, xt_w, y0, C0, R90, xt_w, y0, C0, Fi, x0, yt_h, C0, Fo, R-90, Fi, x0, yt_h, C0, Fo, Fi, xt_w, yt_h, C0, R90, Fi, xt_w, yt_h';
back to Exploring_a_Design_Space_for_Patterns_and_Tilings_Competition_2015