## Realizing still life constraints as a planar tiling

For general discussion about Conway's Game of Life.
simsim314
Posts: 1740
Joined: February 10th, 2014, 1:27 pm

### Re: Realizing still life constraints as a planar tiling

pcallahan wrote:(OOPS: that doesn't have 45 degree rotational symmetry. Need to work on it.)
I don't think the square needs 45 degrees symmetry.

The only problem I see with this solution - is that the spacefilling tiles will become even more complex than they are. But interlocking is first priority for me - so this definitely overtakes extra complexity.

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

### Re: Realizing still life constraints as a planar tiling

I fixed the symmetry. See previous posting. 45 degree symmetry is not essential for the square, but it is for the octagon. Anyway there are many variations and I agree that the downside is added complexity. Though in practice, the empty part of the tiling is mostly a matter of imagination. You only really need to tile neighborhoods of live cells.

UPDATE: The effect on spacefilling tiles is also pretty limited. It adds some extra triangular spikes. The interlocking version of the square also has the property that it cannot be used to tile the plane by itself.

simsim314
Posts: 1740
Joined: February 10th, 2014, 1:27 pm

### Re: Realizing still life constraints as a planar tiling

Here are the new fully interlocking Tiles.
tiles_v2.0.png (192.34 KiB) Viewed 12222 times

Code: Select all

``````//!OpenSCAD

//main parameters
emptyToothWidth = 0.4;
emptyToothLength = 0.1;
filledToothWidth = 0.5;

rfilled = 0.55;
cutFilled = 0.1;

r1Jig = 0.2;
r2Jig = 0.14;
slopeJig = 0.4;

//3d structural teeth
cubeSizeW = 0.17;
cubeAngle = 30;
toothLocation = 0.07;
//Height
H = 1;
r = 10;
d = r * cos(45 / 2);
side = 2 * r * sin(45 / 2);
eps = 0.001;

dFactor = 0.1;
scalefactor1 = 0.935;
scalefactor2 = 0.95;
scaleEmpty = 1.03;

cutSlice = 0.09;
cutAngle = 70;

module Tooth()
{
translate([0, 0, -H / 2])rotate([90, 0, 0])linear_extrude(height = cubeSizeW * r) polygon([[-eps,-eps], [-eps ,H / 2],[H / 2 * sin(cubeAngle), -eps]]);
}

module Slice(use3D = false, r1 = r, applyDiff = true)
{
factor = cutSlice;
{
difference()
{
union()
{
linear_extrude(height = H) rotate([0,0,-45/2])polygon([[0,0],[r1, 0],[r1 * cos(360/8), r1* sin(360/8)]] );
translate([d, - side * toothLocation, H / 2]) Tooth();
translate([d, side * toothLocation, H / 2]) rotate([180, 0, 0]) Tooth();
}

translate([d, - side * toothLocation, H / 2]) rotate([0, 180, 0]) Tooth();
translate([d, side * toothLocation, H / 2]) rotate([180, 180, 0]) Tooth();
}
}
else
{

difference()
{
linear_extrude(height = H) rotate([0,0,-45/2])polygon([[0,0],[r, 0],[r * cos(360/8), r* sin(360/8)]] );
if(applyDiff)
{
translate([d, side/2])rotate([0,0,cutAngle])translate([-r*factor, -r*factor])cube([r*2*factor, r*2*factor, H]);
translate([d, -side/2])rotate([0,0,-cutAngle])translate([-r*factor, -r*factor])cube([r*2*factor, r*2*factor, H]);
}
}
}
};

module SliceWithTooth()
{

union()
{
Slice(false, r, true);
linear_extrude(height = H) translate([d,  -r * emptyToothWidth / 2]) square([r * emptyToothLength,r * emptyToothWidth]);
}
};

//Tiles from slices

module SquareTilePiece()
{
difference()
{
union()
{
linear_extrude(height = H) polygon([[0,0], [side/2,side/2], [side/2,-side/2]]);
{
translate([side / 2, - side * toothLocation, H / 2]) Tooth();
translate([side / 2, side * toothLocation, H / 2]) rotate([180, 0, 0]) Tooth();
}
}

{
translate([side / 2, - side * toothLocation, H / 2]) rotate([0, 180, 0]) Tooth();
translate([side / 2, side * toothLocation, H / 2]) rotate([180, 180, 0]) Tooth();
}
}

}
module SquareTile()
{
rotate([0,0,45/2])union()
{
SquareTilePiece();
rotate([0,0,90]) SquareTilePiece();
rotate([0,0,180]) SquareTilePiece();
rotate([0,0,-90]) SquareTilePiece();
}
};

//Simple Living parts
module LiveSlice()
{
r1 = (r * cos(45/2) - emptyToothLength * r) / cos(45/2);

color([0, 0.4, 0.9]) linear_extrude(height = H) rotate([0,0,-45/2])polygon([[0,0],[r1, 0],[r1 * cos(360/8), r1 * sin(360/8)]] );

};

//Simple Living parts
module LiveSlicePartial(dir)
{
r1 = (r * cos(45/2) - emptyToothLength * r) / cos(45/2);
r2 = r1 * rfilled;
r3 = r * cutFilled;

difference()
{
color([0, 0.4, 0.9]) linear_extrude(height = H) rotate([0,0,-45/2])polygon([[0,0],[r1, 0],[r1 * cos(360/8), r1 * sin(360/8)]] );
linear_extrude(height = H) rotate([0,0,-45/2])polygon([[0,0],[r2, 0],[r2 * cos(360/8), r2 * sin(360/8)]]);
linear_extrude(height = H) polygon([[0,0],[r2 * cos(360 / 16) + r3, 0],[r2 * cos(360 / 16) + r3, r * dir]]);

}
};

module LiveSliceWithTooth()
{
size = r/6;
dtooth = r * filledToothWidth;

color([0, 0.4, 0.9]) union()
{
LiveSlice();
linear_extrude(height = H)translate([r * cos(45/2) - size,  -dtooth /2])square([size,dtooth ]);
}
};

module LiveSliceWithToothPartial(dir)
{
size = r/6;
dtooth = r * filledToothWidth;

color([0, 0.4, 0.9]) union()
{
LiveSlicePartial(dir);
linear_extrude(height = H)translate([r * cos(45/2) - size,  -dtooth /2])square([size,dtooth ]);
}
};

module HelperLife_InternalTooth()
{
rotate([0,0,45])
union()
{
LiveSliceWithTooth();
rotate([0,0,45]) LiveSliceWithTooth();
rotate([0,0,-45]) LiveSliceWithTooth();
}
};

module Helper_InternalTooth()
{
rotate([0,0,45])
union()
{
Slice();
rotate([0,0,45]) Slice();
rotate([0,0,-45]) Slice();
}
};

module TileLife_InternalTooth()
{
rotate([0,0,45/2])
color([0, 0.4, 0.9])
difference()
{
linear_extrude(height = H)polygon([[-d, d], [-d, -d], [d, -d], [d, d]]);
translate([-d, -d]) HelperLife_InternalTooth();
translate([d, -d]) rotate([0,0,90]) HelperLife_InternalTooth();
translate([-d, d]) rotate([0,0,-90]) HelperLife_InternalTooth();
translate([d, d]) rotate([0,0,180]) HelperLife_InternalTooth();
translate([d - filledToothWidth * r / 2, -d/2])cube([side, side, H]);
translate([-side -d + filledToothWidth * r / 2, -d/2])cube([side, side, H]);
translate([-d/2, -side -d + filledToothWidth * r / 2 ])cube([side, side, H]);
translate([-d/2, d - filledToothWidth * r / 2 ])cube([side, side, H]);
}
};

module Tile_Internal()
{
rotate([0,0,45/2])
color([0.8, 0.8, 0.2])
difference()
{
linear_extrude(height = H)polygon([[-d, d], [-d, -d], [d, -d], [d, d]]);
translate([-d, -d]) Helper_InternalTooth();
translate([d, -d]) rotate([0,0,90]) Helper_InternalTooth();
translate([-d, d]) rotate([0,0,-90]) Helper_InternalTooth();
translate([d, d]) rotate([0,0,180]) Helper_InternalTooth();

}
}

module TileSpaceFiller_Type4()
{
rotate([0,0,-45/2])
color([0, 0.4, 0.9])
difference()
{
linear_extrude(height = H) polygon([[-side, side], [-side, -side], [side, -side], [side, side]]);

translate([d, -d]) rotate([0,0,45 + 135]) SliceWithTooth()  ;
translate([d, -d]) rotate([0,0,45 + 90]) Slice()  ;
translate([d, -d]) rotate([0,0,45 + 45]) SliceWithTooth()  ;

translate([d, d]) rotate([0,0,45 + 180])LiveSliceWithTooth() ;
translate([d, d]) rotate([0,0,45 + 135 ])LiveSlice() ;
translate([d, d]) rotate([0,0,45 + -135])LiveSlice() ;

translate([-d, d]) rotate([0,0,45 + -45]) SliceWithTooth()  ;
translate([-d, d]) rotate([0,0,45 + -90]) Slice()  ;
translate([-d, d]) rotate([0,0,45 + -135]) SliceWithTooth();

translate([-d, -d])rotate([0,0,45]) LiveSliceWithTooth() ;
translate([-d,-d]) rotate([0,0,45 + 45])LiveSlice() ;
translate([-d, -d]) rotate([0,0,45 + -45])LiveSlice() ;
}

};

module TileSpaceFiller_Type3()
{

rotate([0,0,-45/2])
color([0, 0.4, 0.9])
difference()
{
linear_extrude(height = H) polygon([[-side, side], [-side, -side], [side, -side], [side, side]]);

translate([d, -d]) rotate([0,0,45 + 135]) LiveSlice()  ;
scale(1 + eps)translate([d, -d]) rotate([0,0,45 + 90]) LiveSlice()  ;
translate([d, -d]) rotate([0,0,45 + 45]) LiveSlice()  ;

translate([d, d]) rotate([0,0,90 + 180])SliceWithTooth() ;
scale(1 + eps)translate([d, d]) rotate([0,0,45 + 180])Slice() ;
translate([d, d]) rotate([0,0,45 + 135])Slice() ;

translate([-d, d]) rotate([0,0,45 + -45]) Slice()  ;
scale(1 + eps)translate([-d, d]) rotate([0,0,45 + -90]) SliceWithTooth()  ;
translate([-d, d]) rotate([0,0,45 + -135]) Slice();

translate([-d,-d]) rotate([0,0,45 + 45])Slice() ;
scale(1 + eps)translate([-d, -d])rotate([0,0,45]) Slice() ;
translate([-d, -d]) rotate([0,0,45 + -45])SliceWithTooth() ;

}

};

module TileSpaceFiller_Type2()
{

rotate([0,0,-45/2])
color([0, 0.4, 0.9])
difference()
{
linear_extrude(height = H) polygon([[-side, side], [-side, -side], [side, -side], [side, side]]);

translate([d, -d]) rotate([0,0,45 + 135]) LiveSliceWithTooth()  ;
scale(1 + eps)translate([d, -d]) rotate([0,0,45 + 90]) LiveSlice()  ;
translate([d, -d]) rotate([0,0,45 + 45]) LiveSlice()  ;

translate([d, d]) rotate([0,0,90 + 180])SliceWithTooth() ;
scale(1 + eps)translate([d, d]) rotate([0,0,45 + 180])SliceWithTooth() ;
translate([d, d]) rotate([0,0,45 + 135])Slice() ;

translate([-d, d]) rotate([0,0,45 + -45]) Slice()  ;
scale(1 + eps)translate([-d, d]) rotate([0,0,45 + -90]) SliceWithTooth()  ;
translate([-d, d]) rotate([0,0,45 + -135]) SliceWithTooth();

translate([-d,-d]) rotate([0,0,45 + 45])LiveSlice() ;
scale(1 + eps)translate([-d, -d])rotate([0,0,45]) LiveSlice() ;
translate([-d, -d]) rotate([0,0,45 + -45])LiveSliceWithTooth() ;

}

};

module TileSpaceFiller_Type1()
{

rotate([0,0,-45/2])
color([0, 0.4, 0.9])
difference()
{
linear_extrude(height = H) polygon([[-side, side], [-side, -side], [side, -side], [side, side]]);

translate([d, -d]) rotate([0,0,45 + 135]) SliceWithTooth()  ;
scale(1 + eps)translate([d, -d]) rotate([0,0,45 + 90]) SliceWithTooth()  ;
translate([d, -d]) rotate([0,0,45 + 45]) SliceWithTooth()  ;

translate([d, d]) rotate([0,0,90 + 180])LiveSlice() ;
scale(1 + eps)translate([d, d]) rotate([0,0,45 + 180])LiveSliceWithTooth() ;
translate([d, d]) rotate([0,0,45 + 135])LiveSliceWithTooth() ;

translate([-d, d]) rotate([0,0,45 + -45]) LiveSliceWithTooth()  ;
scale(1 + eps)translate([-d, d]) rotate([0,0,45 + -90]) LiveSlice()  ;
translate([-d, d]) rotate([0,0,45 + -135]) LiveSliceWithTooth();

translate([-d,-d]) rotate([0,0,45 + 45])LiveSliceWithTooth() ;
scale(1 + eps)translate([-d, -d])rotate([0,0,45]) LiveSliceWithTooth() ;
translate([-d, -d]) rotate([0,0,45 + -45])LiveSlice() ;

}

};

module TileLife_TwoSlicePartial()
{
color([0, 0.4, 0.9])  union()
{
LiveSliceWithToothPartial(1);
rotate([0, 0, 45])LiveSlicePartial(-1);
}
}

module TileLife_FourSlicePartial()
{
r1j = r * r1Jig;
r2j = r * r2Jig;

color([0, 0.4, 0.9]) rotate([0,0,-45/2])difference()
{
rotate([0,0,45/2])difference()
{
union()
{
LiveSlice();
rotate([0, 0, 45])LiveSlice();
rotate([0, 0, 90])LiveSlice();
rotate([0, 0, 135])LiveSliceWithTooth();
}

rotate([0, 0, 45])TileLife_TwoSlicePartial();
}

linear_extrude(height = H) polygon([[-r1j, 0], [r1j, 0], [r1j + slopeJig * r2j, r2j], [-r1j - slopeJig * r2j, r2j]]);
}
}

module TileLife_FourSlice()
{
r1j = r * r1Jig;
r2j = r * r2Jig;

color([0, 0.4, 0.9]) rotate([0,0,-45/2])difference()
{
rotate([0,0,45/2])
{
union()
{
LiveSlice();
rotate([0, 0, 45])LiveSlice();
rotate([0, 0, 90])LiveSlice();
rotate([0, 0, 135])LiveSliceWithTooth();
}
}

linear_extrude(height = H) polygon([[-r1j, 0], [r1j, 0], [r1j + slopeJig * r2j, r2j], [-r1j - slopeJig * r2j, r2j]]);
}
}

module TileLife_StaticPart()
{
color([0, 0.4, 0.9]) difference()
{
union()
{
for (i = [0 : 7])
rotate([0,0,45 * i]) LiveSlice();
}

rotate([0,0,45])TileLife_TwoSlicePartial();
rotate([0,0,180])TileLife_TwoSlicePartial();
TileLife_FourSlicePartial();
}
}

module EmptyTile4_2teeth_90()
{
difference()
{
union()
{
SliceWithTooth();
rotate([0,0,45]) SliceWithTooth();
}
TileLife_InternalTooth();
}
};

module EmptyTile4_1teeth_90()
{
difference()
{
union()
{
Slice();
rotate([0,0,45]) SliceWithTooth();
}
TileLife_InternalTooth();
}
};

module EmptyTile4_2teeth_180()
{
difference()
{
union()
{
Slice();
rotate([0,0,45]) Slice();
rotate([0,0,90]) SliceWithTooth();
rotate([0,0,135]) SliceWithTooth();
}
TileLife_InternalTooth();
}
};

module PlaceFilled(x, y, type, rot)
{
dx = dFactor * cos(45/2 - 45 * rot);
dy = dFactor  * sin(45/2 - 45 * rot);

if(type == 0)
translate([2 * d * x + dx, 2 * d * y + dy]) scale(scaleEmpty * scalefactor1) rotate([0,0,-45 - 45 * rot])TileLife_EmptyInternalEdgeTooth();
if(type == 1)
translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scaleEmpty * scalefactor1) rotate([0,180, -90 - 45 * rot])TileLife_EmptyInternalEdgeTooth();
if(type == 2)
translate([2 * d * x + dx, 2 * d * y + dy]) scale(scaleEmpty * scalefactor1) rotate([0,0,-45 - 45 * rot])TileLife_EmptyInternalTooth();
if(type == 3)
translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scaleEmpty * scalefactor1) rotate([0,180, -90 - 45 * rot])TileLife_EmptyInternalTooth();
if(type == 4)
translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,-45 - 45 * rot]) TileLife_FilledInternalEdgeTooth();
if(type == 5)
translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scalefactor2) rotate([0,180, -90 - 45 * rot])TileLife_FilledInternalEdgeTooth();
if(type == 6)
translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,-45 - 45 * rot]) TileLife_FilledInternalTooth();
if(type == 7)
translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scalefactor2) rotate([0,180, -90 - 45 * rot])TileLife_FilledInternalTooth();
};

module PlaceInternalConnector(x, y, type, rot)
{
dx = 0;
dy = 0;

if(type == 0)
translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,- 45 * rot])TileSpaceFiller_Type4();
if(type == 12)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45 - 45 * rot])SquareTile();
if(type == 4)
{
if(abs(rot - floor(rot)) < 0.1)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45 - 45 * rot])TileLife_InternalTooth();
else
color([0, 0.4, 0.9]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor1) rotate([0,0, 45 /2 - 45 * rot])TileLife_InternalTooth();
}

}

module PlaceExternalConnector(x, y, type, rot)
{
dx = 0;
dy = 0;

if(type == 0)
translate([d + 2 * d * x + dx, d + 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45/2 - 90 * rot])TileSpaceFiller_Type4();
if(type == 1)
color([0.8, 0.8, 0.2]) translate([d + 2 * d * x + dx, d + 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45/2 - 90 - 90 * rot])TileSpaceFiller_Type3();
if(type == 2)
color([0.8, 0.8, 0.2]) translate([d + 2 * d * x + dx, d + 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45/2 - 90 - 90 * rot])TileSpaceFiller_Type1();
if(type == 3)
color([0.8, 0.8, 0.2]) translate([d + 2 * d * x + dx, d + 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45/2 - 90 -  90 * rot])TileSpaceFiller_Type2();

if(type == 12)
color([0.8, 0.8, 0.2]) translate([d + 2 * d * x + dx, d + 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45/2 - 45 * rot])SquareTile();
if(type == 4)
color([0, 0.4, 0.9])  translate([d +2 * d * x + dx, d +2 * d * y + dy]) scale(scalefactor1) rotate([0,0,-45/2 ])TileLife_InternalTooth();

}

module PlaceEmpty0(x, y, type, rot)
{
dx = dFactor * cos(45/2 - 45 * rot);
dy = dFactor  * sin(45/2 - 45 * rot);

if(type == 0)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,-45 - 45 * rot])EmptyTile0_NoTeeth();
if(type == 1)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,-45 - 45 * rot])EmptyTile0_InternalTeeth();
if(type == 2)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scalefactor2) rotate([0,180, -90 - 45 * rot])EmptyTile0_InternalTeeth();
if(type == 3)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,-45 - 45 * rot])EmptyTile0_EdgeTeeth();
if(type == 4)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scalefactor2) rotate([0,180, -90 - 45 * rot])EmptyTile0_EdgeTeeth();

}

module PlaceEmpty4(x, y, type, rot)
{
dx = dFactor * cos(45/2 - 45 * rot);
dy = dFactor  * sin(45/2 - 45 * rot);

if(type == 0)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,-45 - 45 * rot])EmptyTile4_2teeth_180();
if(type == 1)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scalefactor2) rotate([0,180, -90 - 45 * rot])EmptyTile4_2teeth_180();
if(type == 2)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,45 - 45 * rot])EmptyTile4_1teeth_90();
if(type == 3)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scalefactor2) rotate([0,180, -90 - 45 * rot])EmptyTile4_1teeth_90();
if(type == 4)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,45 - 45 * rot])EmptyTile4_2teeth_90();
}

module EmptyTile0_NoTeeth()
{
difference()
{
union()
{
Slice();
rotate([0,0,45]) Slice();
rotate([0,0,90]) Slice();
rotate([0,0,135]) Slice();
}

rotate([0,0,45]) TileLife_InternalTooth();
}
};

module EmptyTile0_EdgeTeeth()
{
difference()
{
union()
{
SliceWithTooth();
rotate([0,0,45]) Slice();
rotate([0,0,90]) Slice();
rotate([0,0,135]) Slice();
}

rotate([0,0,45]) TileLife_InternalTooth();
}
};

module EmptyTile0_InternalTeeth()
{
difference()
{
union()
{
Slice();
rotate([0,0,45]) SliceWithTooth();
rotate([0,0,90]) Slice();
rotate([0,0,135]) Slice();
}

rotate([0,0,45]) TileLife_InternalTooth();
}
};

/*
translate([r * 3, 0])TileLife_FourSlicePartial();
translate([r * 6, 0])TileLife_TwoSlicePartial();
TileLife_StaticPart();
translate([r * 9, 0])TileLife_FourSlice();
*/

d1 = 2.4 * d;
translate([-2 * d1, 2 * d1]) TileSpaceFiller_Type4();
translate([- d1, 2 * d1])TileSpaceFiller_Type3();
translate([0, 2 * d1])TileSpaceFiller_Type1();
translate([d1, 2 * d1])TileSpaceFiller_Type2();

color([0.8, 0.8, 0.2])translate([- 2 * d1,  d1])TileLife_InternalTooth();
translate([-  d1,  d1])TileLife_FourSlicePartial();
translate([0,  d1])TileLife_TwoSlicePartial();
translate([d1,  d1])TileLife_StaticPart();

translate([- 2 * d1,  0])TileLife_FourSlice();
color([0.8, 0.8, 0.2])translate([- d1,  0])EmptyTile4_2teeth_180();
color([0.8, 0.8, 0.2])translate([0,  0])EmptyTile4_1teeth_90();
color([0.8, 0.8, 0.2])translate([d1,  0])EmptyTile4_2teeth_90();

color([0.8, 0.8, 0.2])translate([- 2 * d1,  -d1])Tile_Internal();
color([0.8, 0.8, 0.2])translate([- d1,  -d1])EmptyTile0_NoTeeth();
color([0.8, 0.8, 0.2])translate([0,  -d1])EmptyTile0_InternalTeeth();
color([0.8, 0.8, 0.2])translate([d1,  -d1])EmptyTile0_EdgeTeeth();
``````

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

### Re: Realizing still life constraints as a planar tiling

Is there any reason you didn't use the replacement for the square to join the empty tiles? It would keep the color scheme consistent. It would also (I think) remove slightly less area and result in sturdier pieces.

Another random idea. The planar pieces rely on a relatively small number of control points (polygon vertices). These are constrained by symmetry, and the "spacefiller" pieces do not introduce any new control points. Given a fixed topology, the control points can be moved to adjust desirable characteristics such as maximizing the minimal acute angle, the minimal width of "bridges" where (informally speaking) it looks like you could snap the piece in half, and the maximal length of teeth. It would be interesting to look for an optimal tile automatically using some general-purpose approach like simulated annealing (since an exact solution is probably intractable). This might result in something a little better than you would get by hand.

simsim314
Posts: 1740
Joined: February 10th, 2014, 1:27 pm

### Re: Realizing still life constraints as a planar tiling

pcallahan wrote:Is there any reason you didn't use the replacement for the square to join the empty tiles?
Woops you're absolutely right - which brings me to another idea: it's possible to remove completely the square, and use only the "fully symmetrical spacefiller" for this purpose as well. It would suer overloaded tile with 4 (!) purposes and by far the most popular piece in this set.

This will bring back the coloring issue - on the other hand I think until now minimizing the number of different tiles was more important than coloring, and this brings us to 15 (!) tiles.

Think that the only case this space-filler should be "live" is for block, and this is the only case, so I think keeping it "dead" is fine.
pcallahan wrote: It would be interesting to look for an optimal tile automatically using some general-purpose approach like simulated annealing
I think a better idea would be just to print the tiles and see what goes wrong - we might be missing stuff we think are fine. It's also possible to use some finite element program for stress analysis to find the optimal configuration - but printing few different sets to see what can go wrong and how it "feels" is simpler and necessary anyway.

I agree that QA analysis for any specific set should also check the fragility of the system.

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

### Re: Realizing still life constraints as a planar tiling

simsim314 wrote:Woops you're absolutely right - which brings me to another idea: it's possible to remove completely the square, and use only the "fully symmetrical spacefiller" for this purpose as well. It would suer overloaded tile with 4 (!) purposes and by far the most popular piece in this set.
I'm skeptical of this, because the spacefillers don't just fill up space. They apply the diagonal constraints, so I believe that the corner shared by 4 live cells would have to be different from the one shared by four empty cells. I don't have a proof and I'd be happy to be surprised (but wouldn't it also imply that empty cells surrounded by eight empty cells are identical to live cells surrounded by eight live cells?).

simsim314
Posts: 1740
Joined: February 10th, 2014, 1:27 pm

### Re: Realizing still life constraints as a planar tiling

pcallahan wrote:but wouldn't it also imply that empty cells surrounded by eight empty cells are identical to live cells surrounded by eight live cells?
Think of this tile as grey tile (like you had at first). You don't have a problem of using the grey tile in different situations as long as the situations are incompatible with each other "globally".

This configuration pretty surely will not imply some sort of strange possibility to connect live cells with empty cells in some invalid way, this could maybe work locally, but this connection is so "irrational" that will probably fail to connect further.

I think valid tiling is such that insures that any global space coverage is valid one - not that it's impossible to misfit some shape to another locally.

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

### Re: Realizing still life constraints as a planar tiling

simsim314 wrote: I think valid tiling is such that insures that any global space coverage is valid one - not that it's impossible to misfit some shape to another locally.
Agreed, but I would still be surprised if it did not permit invalid configurations. As to your general point, it wouldn't surprise me if the tile set can be reduced further and that non-local constraints would have to come into play to prove it. It's just getting well beyond any experience I have in constructing tilings. (Some time I'll have to dig out my copy of http://www.amazon.com/Tilings-Patterns- ... 071671194X which I browsed through but never really read.)

simsim314
Posts: 1740
Joined: February 10th, 2014, 1:27 pm

### Re: Realizing still life constraints as a planar tiling

pcallahan wrote:Agreed, but I would still be surprised if it did not permit invalid configurations.
I was thinking of it again - and it seems like if my idea would be implemented the 8 empty tiles would be equivalent to 8 tooth tile. This doesn't seems to be too good of a feature.

So back to the drawing board - 16 tiles with better internal interlocking, and color preserving. The best of all worlds with "only 16 simple tiles".

simsim314
Posts: 1740
Joined: February 10th, 2014, 1:27 pm

### Re: Realizing still life constraints as a planar tiling

Here are the new tiles:
tiles_v2.0.png (188.21 KiB) Viewed 12193 times
I've played with the parameters to optimize this configuration.

Code: Select all

``````//!OpenSCAD

//main parameters
emptyToothWidth = 0.4;
emptyToothLength = 0.08;
filledToothWidth = 0.25;

rfilled = 0.55;
cutFilled = 0.1;

r1Jig = 0.2;
r2Jig = 0.14;
slopeJig = 0.4;

//3d structural teeth
cubeSizeW = 0.17;
cubeAngle = 30;
toothLocation = 0.07;
//Height
H = 1;
r = 10;
d = r * cos(45 / 2);
side = 2 * r * sin(45 / 2);
eps = 0.001;

dFactor = 0.1;
scalefactor1 = 0.935;
scalefactor2 = 0.95;
scaleEmpty = 1.03;

cutSlice = 0.09;
cutAngle = 67;

//Simple Living parts
module LiveSlice()
{
r1 = (r * cos(45/2) - emptyToothLength * r) / cos(45/2);

color([0, 0.4, 0.9]) linear_extrude(height = H) rotate([0,0,-45/2])polygon([[0,0],[r1, 0],[r1 * cos(360/8), r1 * sin(360/8)]] );

};

//Simple Living parts
module LiveSlicePartial(dir)
{
r1 = (r * cos(45/2) - emptyToothLength * r) / cos(45/2);
r2 = r1 * rfilled;
r3 = r * cutFilled;

difference()
{
color([0, 0.4, 0.9]) linear_extrude(height = H) rotate([0,0,-45/2])polygon([[0,0],[r1, 0],[r1 * cos(360/8), r1 * sin(360/8)]] );
linear_extrude(height = H) rotate([0,0,-45/2])polygon([[0,0],[r2, 0],[r2 * cos(360/8), r2 * sin(360/8)]]);
linear_extrude(height = H) polygon([[0,0],[r2 * cos(360 / 16) + r3, 0],[r2 * cos(360 / 16) + r3, r * dir]]);

}
};

module LiveSliceWithTooth()
{
size = r/6;
dtooth = r * filledToothWidth;

color([0, 0.4, 0.9]) union()
{
LiveSlice();
linear_extrude(height = H)translate([r * cos(45/2) - size,  -dtooth /2])square([size,dtooth ]);
}
};

module LiveSliceWithToothPartial(dir)
{
size = r/6;
dtooth = r * filledToothWidth;

color([0, 0.4, 0.9]) union()
{
LiveSlicePartial(dir);
linear_extrude(height = H)translate([r * cos(45/2) - size,  -dtooth /2])square([size,dtooth ]);
}
};

module HelperLife_InternalTooth()
{
rotate([0,0,45])
union()
{
LiveSliceWithTooth();
rotate([0,0,45]) LiveSliceWithTooth();
rotate([0,0,-45]) LiveSliceWithTooth();
}
};

module Helper_InternalTooth()
{
rotate([0,0,45])
union()
{
Slice();
rotate([0,0,45]) Slice();
rotate([0,0,-45]) Slice();
}
};

module TileLife_InternalTooth()
{
rotate([0,0,45/2])
color([0, 0.4, 0.9])
difference()
{
linear_extrude(height = H)polygon([[-d, d], [-d, -d], [d, -d], [d, d]]);
translate([-d, -d]) HelperLife_InternalTooth();
translate([d, -d]) rotate([0,0,90]) HelperLife_InternalTooth();
translate([-d, d]) rotate([0,0,-90]) HelperLife_InternalTooth();
translate([d, d]) rotate([0,0,180]) HelperLife_InternalTooth();
translate([d - filledToothWidth * r / 2, -d/2])cube([side, side, H]);
translate([-side -d + filledToothWidth * r / 2, -d/2])cube([side, side, H]);
translate([-d/2, -side -d + filledToothWidth * r / 2 ])cube([side, side, H]);
translate([-d/2, d - filledToothWidth * r / 2 ])cube([side, side, H]);
}
};

module Tooth()
{
translate([0, 0, -H / 2])rotate([90, 0, 0])linear_extrude(height = cubeSizeW * r) polygon([[-eps,-eps], [-eps ,H / 2],[H / 2 * sin(cubeAngle), -eps]]);
}

module Slice(use3D = false, r1 = r, applyDiff = true)
{
factor = cutSlice;
{
difference()
{
union()
{
linear_extrude(height = H) rotate([0,0,-45/2])polygon([[0,0],[r1, 0],[r1 * cos(360/8), r1* sin(360/8)]] );
translate([d, - side * toothLocation, H / 2]) Tooth();
translate([d, side * toothLocation, H / 2]) rotate([180, 0, 0]) Tooth();
}

translate([d, - side * toothLocation, H / 2]) rotate([0, 180, 0]) Tooth();
translate([d, side * toothLocation, H / 2]) rotate([180, 180, 0]) Tooth();
}
}
else
{

difference()
{
linear_extrude(height = H) rotate([0,0,-45/2])polygon([[0,0],[r, 0],[r * cos(360/8), r* sin(360/8)]] );
if(applyDiff)
{
translate([d, side/2])rotate([0,0,cutAngle])translate([-r*factor, -r*factor])cube([r*2*factor, r*2*factor, H]);
translate([d, -side/2])rotate([0,0,-cutAngle])translate([-r*factor, -r*factor])cube([r*2*factor, r*2*factor, H]);
}
}
}
};

module SliceWithTooth()
{

union()
{
Slice(false, r, true);
linear_extrude(height = H) translate([d,  -r * emptyToothWidth / 2]) square([r * emptyToothLength,r * emptyToothWidth]);
}
};

//Tiles from slices

module SquareTilePiece()
{
difference()
{
union()
{
linear_extrude(height = H) polygon([[0,0], [side/2,side/2], [side/2,-side/2]]);
{
translate([side / 2, - side * toothLocation, H / 2]) Tooth();
translate([side / 2, side * toothLocation, H / 2]) rotate([180, 0, 0]) Tooth();
}
}

{
translate([side / 2, - side * toothLocation, H / 2]) rotate([0, 180, 0]) Tooth();
translate([side / 2, side * toothLocation, H / 2]) rotate([180, 180, 0]) Tooth();
}
}

}
module SquareTile()
{
rotate([0,0,45/2])union()
{
SquareTilePiece();
rotate([0,0,90]) SquareTilePiece();
rotate([0,0,180]) SquareTilePiece();
rotate([0,0,-90]) SquareTilePiece();
}
};

module Tile_Internal()
{
rotate([0,0,45/2])
color([0.8, 0.8, 0.2])
difference()
{
linear_extrude(height = H)polygon([[-d, d], [-d, -d], [d, -d], [d, d]]);
translate([-d, -d]) Helper_InternalTooth();
translate([d, -d]) rotate([0,0,90]) Helper_InternalTooth();
translate([-d, d]) rotate([0,0,-90]) Helper_InternalTooth();
translate([d, d]) rotate([0,0,180]) Helper_InternalTooth();

}
}

module TileSpaceFiller_Type4()
{
rotate([0,0,-45/2])
color([0, 0.4, 0.9])
difference()
{
linear_extrude(height = H) polygon([[-side, side], [-side, -side], [side, -side], [side, side]]);

translate([d, -d]) rotate([0,0,45 + 135]) SliceWithTooth()  ;
translate([d, -d]) rotate([0,0,45 + 90]) Slice()  ;
translate([d, -d]) rotate([0,0,45 + 45]) SliceWithTooth()  ;

translate([d, d]) rotate([0,0,45 + 180])LiveSliceWithTooth() ;
translate([d, d]) rotate([0,0,45 + 135 ])LiveSlice() ;
translate([d, d]) rotate([0,0,45 + -135])LiveSlice() ;

translate([-d, d]) rotate([0,0,45 + -45]) SliceWithTooth()  ;
translate([-d, d]) rotate([0,0,45 + -90]) Slice()  ;
translate([-d, d]) rotate([0,0,45 + -135]) SliceWithTooth();

translate([-d, -d])rotate([0,0,45]) LiveSliceWithTooth() ;
translate([-d,-d]) rotate([0,0,45 + 45])LiveSlice() ;
translate([-d, -d]) rotate([0,0,45 + -45])LiveSlice() ;
}

};

module TileSpaceFiller_Type3()
{

rotate([0,0,-45/2])
color([0, 0.4, 0.9])
difference()
{
linear_extrude(height = H) polygon([[-side, side], [-side, -side], [side, -side], [side, side]]);

translate([d, -d]) rotate([0,0,45 + 135]) LiveSlice()  ;
scale(1 + eps)translate([d, -d]) rotate([0,0,45 + 90]) LiveSlice()  ;
translate([d, -d]) rotate([0,0,45 + 45]) LiveSlice()  ;

translate([d, d]) rotate([0,0,90 + 180])SliceWithTooth() ;
scale(1 + eps)translate([d, d]) rotate([0,0,45 + 180])Slice() ;
translate([d, d]) rotate([0,0,45 + 135])Slice() ;

translate([-d, d]) rotate([0,0,45 + -45]) Slice()  ;
scale(1 + eps)translate([-d, d]) rotate([0,0,45 + -90]) SliceWithTooth()  ;
translate([-d, d]) rotate([0,0,45 + -135]) Slice();

translate([-d,-d]) rotate([0,0,45 + 45])Slice() ;
scale(1 + eps)translate([-d, -d])rotate([0,0,45]) Slice() ;
translate([-d, -d]) rotate([0,0,45 + -45])SliceWithTooth() ;

}

};

module TileSpaceFiller_Type2()
{

rotate([0,0,-45/2])
color([0, 0.4, 0.9])
difference()
{
linear_extrude(height = H) polygon([[-side, side], [-side, -side], [side, -side], [side, side]]);

translate([d, -d]) rotate([0,0,45 + 135]) LiveSliceWithTooth()  ;
scale(1 + eps)translate([d, -d]) rotate([0,0,45 + 90]) LiveSlice()  ;
translate([d, -d]) rotate([0,0,45 + 45]) LiveSlice()  ;

translate([d, d]) rotate([0,0,90 + 180])SliceWithTooth() ;
scale(1 + eps)translate([d, d]) rotate([0,0,45 + 180])SliceWithTooth() ;
translate([d, d]) rotate([0,0,45 + 135])Slice() ;

translate([-d, d]) rotate([0,0,45 + -45]) Slice()  ;
scale(1 + eps)translate([-d, d]) rotate([0,0,45 + -90]) SliceWithTooth()  ;
translate([-d, d]) rotate([0,0,45 + -135]) SliceWithTooth();

translate([-d,-d]) rotate([0,0,45 + 45])LiveSlice() ;
scale(1 + eps)translate([-d, -d])rotate([0,0,45]) LiveSlice() ;
translate([-d, -d]) rotate([0,0,45 + -45])LiveSliceWithTooth() ;

}

};

module TileSpaceFiller_Type1()
{

rotate([0,0,-45/2])
color([0, 0.4, 0.9])
difference()
{
linear_extrude(height = H) polygon([[-side, side], [-side, -side], [side, -side], [side, side]]);

translate([d, -d]) rotate([0,0,45 + 135]) SliceWithTooth()  ;
scale(1 + eps)translate([d, -d]) rotate([0,0,45 + 90]) SliceWithTooth()  ;
translate([d, -d]) rotate([0,0,45 + 45]) SliceWithTooth()  ;

translate([d, d]) rotate([0,0,90 + 180])LiveSlice() ;
scale(1 + eps)translate([d, d]) rotate([0,0,45 + 180])LiveSliceWithTooth() ;
translate([d, d]) rotate([0,0,45 + 135])LiveSliceWithTooth() ;

translate([-d, d]) rotate([0,0,45 + -45]) LiveSliceWithTooth()  ;
scale(1 + eps)translate([-d, d]) rotate([0,0,45 + -90]) LiveSlice()  ;
translate([-d, d]) rotate([0,0,45 + -135]) LiveSliceWithTooth();

translate([-d,-d]) rotate([0,0,45 + 45])LiveSliceWithTooth() ;
scale(1 + eps)translate([-d, -d])rotate([0,0,45]) LiveSliceWithTooth() ;
translate([-d, -d]) rotate([0,0,45 + -45])LiveSlice() ;

}

};

module TileLife_TwoSlicePartial()
{
color([0, 0.4, 0.9])  union()
{
LiveSliceWithToothPartial(1);
rotate([0, 0, 45])LiveSlicePartial(-1);
}
}

module TileLife_FourSlicePartial()
{
r1j = r * r1Jig;
r2j = r * r2Jig;

color([0, 0.4, 0.9]) rotate([0,0,-45/2])difference()
{
rotate([0,0,45/2])difference()
{
union()
{
LiveSlice();
rotate([0, 0, 45])LiveSlice();
rotate([0, 0, 90])LiveSlice();
rotate([0, 0, 135])LiveSliceWithTooth();
}

rotate([0, 0, 45])TileLife_TwoSlicePartial();
}

linear_extrude(height = H) polygon([[-r1j, 0], [r1j, 0], [r1j + slopeJig * r2j, r2j], [-r1j - slopeJig * r2j, r2j]]);
}
}

module TileLife_FourSlice()
{
r1j = r * r1Jig;
r2j = r * r2Jig;

color([0, 0.4, 0.9]) rotate([0,0,-45/2])difference()
{
rotate([0,0,45/2])
{
union()
{
LiveSlice();
rotate([0, 0, 45])LiveSlice();
rotate([0, 0, 90])LiveSlice();
rotate([0, 0, 135])LiveSliceWithTooth();
}
}

linear_extrude(height = H) polygon([[-r1j, 0], [r1j, 0], [r1j + slopeJig * r2j, r2j], [-r1j - slopeJig * r2j, r2j]]);
}
}

module TileLife_StaticPart()
{
color([0, 0.4, 0.9]) difference()
{
union()
{
for (i = [0 : 7])
rotate([0,0,45 * i]) LiveSlice();
}

rotate([0,0,45])TileLife_TwoSlicePartial();
rotate([0,0,180])TileLife_TwoSlicePartial();
TileLife_FourSlicePartial();
}
}

module EmptyTile4_2teeth_90()
{
difference()
{
union()
{
SliceWithTooth();
rotate([0,0,45]) SliceWithTooth();
}
Tile_Internal();

}
};

module EmptyTile4_1teeth_90()
{
difference()
{
union()
{
Slice();
rotate([0,0,45]) SliceWithTooth();
}
Tile_Internal();
}
};

module EmptyTile4_2teeth_180()
{
difference()
{
union()
{
Slice();
rotate([0,0,45]) Slice();
rotate([0,0,90]) SliceWithTooth();
rotate([0,0,135]) SliceWithTooth();
}
Tile_Internal();
}
};

module PlaceFilled(x, y, type, rot)
{
dx = dFactor * cos(45/2 - 45 * rot);
dy = dFactor  * sin(45/2 - 45 * rot);

if(type == 0)
translate([2 * d * x + dx, 2 * d * y + dy]) scale(scaleEmpty * scalefactor1) rotate([0,0,-45 - 45 * rot])TileLife_EmptyInternalEdgeTooth();
if(type == 1)
translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scaleEmpty * scalefactor1) rotate([0,180, -90 - 45 * rot])TileLife_EmptyInternalEdgeTooth();
if(type == 2)
translate([2 * d * x + dx, 2 * d * y + dy]) scale(scaleEmpty * scalefactor1) rotate([0,0,-45 - 45 * rot])TileLife_EmptyInternalTooth();
if(type == 3)
translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scaleEmpty * scalefactor1) rotate([0,180, -90 - 45 * rot])TileLife_EmptyInternalTooth();
if(type == 4)
translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,-45 - 45 * rot]) TileLife_FilledInternalEdgeTooth();
if(type == 5)
translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scalefactor2) rotate([0,180, -90 - 45 * rot])TileLife_FilledInternalEdgeTooth();
if(type == 6)
translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,-45 - 45 * rot]) TileLife_FilledInternalTooth();
if(type == 7)
translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scalefactor2) rotate([0,180, -90 - 45 * rot])TileLife_FilledInternalTooth();
};

module PlaceInternalConnector(x, y, type, rot)
{
dx = 0;
dy = 0;

if(type == 0)
translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,- 45 * rot])TileSpaceFiller_Type4();
if(type == 12)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45 - 45 * rot])SquareTile();
if(type == 4)
{
if(abs(rot - floor(rot)) < 0.1)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45 - 45 * rot])TileLife_InternalTooth();
else
color([0, 0.4, 0.9]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor1) rotate([0,0, 45 /2 - 45 * rot])TileLife_InternalTooth();
}

}

module PlaceExternalConnector(x, y, type, rot)
{
dx = 0;
dy = 0;

if(type == 0)
translate([d + 2 * d * x + dx, d + 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45/2 - 90 * rot])TileSpaceFiller_Type4();
if(type == 1)
color([0.8, 0.8, 0.2]) translate([d + 2 * d * x + dx, d + 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45/2 - 90 - 90 * rot])TileSpaceFiller_Type3();
if(type == 2)
color([0.8, 0.8, 0.2]) translate([d + 2 * d * x + dx, d + 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45/2 - 90 - 90 * rot])TileSpaceFiller_Type1();
if(type == 3)
color([0.8, 0.8, 0.2]) translate([d + 2 * d * x + dx, d + 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45/2 - 90 -  90 * rot])TileSpaceFiller_Type2();

if(type == 12)
color([0.8, 0.8, 0.2]) translate([d + 2 * d * x + dx, d + 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,45/2 - 45 * rot])SquareTile();
if(type == 4)
color([0, 0.4, 0.9])  translate([d +2 * d * x + dx, d +2 * d * y + dy]) scale(scalefactor1) rotate([0,0,-45/2 ])TileLife_InternalTooth();

}

module PlaceEmpty0(x, y, type, rot)
{
dx = dFactor * cos(45/2 - 45 * rot);
dy = dFactor  * sin(45/2 - 45 * rot);

if(type == 0)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,-45 - 45 * rot])EmptyTile0_NoTeeth();
if(type == 1)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,-45 - 45 * rot])EmptyTile0_InternalTeeth();
if(type == 2)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scalefactor2) rotate([0,180, -90 - 45 * rot])EmptyTile0_InternalTeeth();
if(type == 3)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,-45 - 45 * rot])EmptyTile0_EdgeTeeth();
if(type == 4)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scalefactor2) rotate([0,180, -90 - 45 * rot])EmptyTile0_EdgeTeeth();

}

module PlaceEmpty4(x, y, type, rot)
{
dx = dFactor * cos(45/2 - 45 * rot);
dy = dFactor  * sin(45/2 - 45 * rot);

if(type == 0)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,-45 - 45 * rot])EmptyTile4_2teeth_180();
if(type == 1)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scalefactor2) rotate([0,180, -90 - 45 * rot])EmptyTile4_2teeth_180();
if(type == 2)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,45 - 45 * rot])EmptyTile4_1teeth_90();
if(type == 3)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scalefactor2) rotate([0,180, -90 - 45 * rot])EmptyTile4_1teeth_90();
if(type == 4)
color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor2) rotate([0,0,45 - 45 * rot])EmptyTile4_2teeth_90();
}

module EmptyTile0_NoTeeth()
{
difference()
{
union()
{
Slice();
rotate([0,0,45]) Slice();
rotate([0,0,90]) Slice();
rotate([0,0,135]) Slice();
}

rotate([0,0,45]) Tile_Internal();
}
};

module EmptyTile0_EdgeTeeth()
{
difference()
{
union()
{
SliceWithTooth();
rotate([0,0,45]) Slice();
rotate([0,0,90]) Slice();
rotate([0,0,135]) Slice();
}

rotate([0,0,45]) Tile_Internal();
}
};

module EmptyTile0_InternalTeeth()
{
difference()
{
union()
{
Slice();
rotate([0,0,45]) SliceWithTooth();
rotate([0,0,90]) Slice();
rotate([0,0,135]) Slice();
}

rotate([0,0,45]) Tile_Internal();
}
};

/*
translate([r * 3, 0])TileLife_FourSlicePartial();
translate([r * 6, 0])TileLife_TwoSlicePartial();
TileLife_StaticPart();
translate([r * 9, 0])TileLife_FourSlice();
*/

d1 = 2.4 * d;
translate([-2 * d1, 2 * d1]) TileSpaceFiller_Type4();
translate([- d1, 2 * d1])TileSpaceFiller_Type3();
translate([0, 2 * d1])TileSpaceFiller_Type1();
translate([d1, 2 * d1])TileSpaceFiller_Type2();

translate([- 2 * d1,  d1])TileLife_InternalTooth();
translate([-  d1,  d1])TileLife_FourSlicePartial();
translate([0,  d1])TileLife_TwoSlicePartial();
translate([d1,  d1])TileLife_StaticPart();

translate([- 2 * d1,  0])TileLife_FourSlice();
color([0.8, 0.8, 0.2])translate([- d1,  0])EmptyTile4_2teeth_180();
color([0.8, 0.8, 0.2])translate([0,  0])EmptyTile4_1teeth_90();
color([0.8, 0.8, 0.2])translate([d1,  0])EmptyTile4_2teeth_90();

color([0.8, 0.8, 0.2])translate([- 2 * d1,  -d1])Tile_Internal();
color([0.8, 0.8, 0.2])translate([- d1,  -d1])EmptyTile0_NoTeeth();
color([0.8, 0.8, 0.2])translate([0,  -d1])EmptyTile0_InternalTeeth();
color([0.8, 0.8, 0.2])translate([d1,  -d1])EmptyTile0_EdgeTeeth();
``````

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

### Re: Realizing still life constraints as a planar tiling

Those look really good (and are now back to 2D fittings). Here is my rough assembly of the 2-3 live neighborhoods.
Screen Shot 2016-06-02 at 7.02.23 PM.png (46.69 KiB) Viewed 12178 times
Are you planning to update your tiling layout script and the thingiverse page?

I'm also wondering what is the quickest way to get your openjscad script to produce 16 lists of polygon vertices (I could reverse engineer it in python but I don't seem to have the time I had a month ago).

simsim314
Posts: 1740
Joined: February 10th, 2014, 1:27 pm

### Re: Realizing still life constraints as a planar tiling

pcallahan wrote:Are you planning to update your tiling layout script and the thingiverse page?
I'm planning to make a print of the new tiles, make sure everything fits alright after some trimming in another CAD software, reproduce some simple SL with physical tiles, and post another "thing", as tiles v2.0. Obviously I'll update the scripts as well. Just want to focus more on the physical properties for now (I've a 3d printer).
pcallahan wrote:I'm also wondering what is the quickest way to get your openjscad script to produce 16 lists of polygon vertices (I could reverse engineer it in python but I don't seem to have the time I had a month ago).
It's kinda tricky. Many of the polygons are calculated using boolean operations (on solid bodies), and openscad is not a software that gives you access to the vertices (it's scripting language which basically provides API to boolean operations, basic transformations, and extrudes).

I'm considering to rewrite the whole thing in blender, because the trimming (offset - due to thickness of material), gets more tedious than making the tiles themselves. Blender does give access to all vertices, so this might be another reason for me to switch to blender.

simsim314
Posts: 1740
Joined: February 10th, 2014, 1:27 pm

### Re: Realizing still life constraints as a planar tiling

I've managed to print the first working (more or less) tiles:
20160603_103309.jpg (116.32 KiB) Viewed 12155 times
20160603_103211.jpg (171.9 KiB) Viewed 12155 times
20160603_124007.jpg (37.92 KiB) Viewed 12155 times

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

### Re: Realizing still life constraints as a planar tiling

Cool! How much time does it take to print one? Is that your intended final size? It seems very large to me, though I understand the drawback to small tiles.

By the way, I think these two tiles work out better when they match empty cells.
Screen Shot 2016-06-03 at 12.01.10 PM.png (22.33 KiB) Viewed 12118 times
I noticed this when starting to lay out an eater. The one on the right extends the live cell area in way that doesn't seem visually appealing (to me anyway). More objectively, these tiles do not implement live-live diagonal connection. The three that do this have matching rectangular dents on opposite sides.
Last edited by pcallahan on June 3rd, 2016, 3:09 pm, edited 1 time in total.

simsim314
Posts: 1740
Joined: February 10th, 2014, 1:27 pm

### Re: Realizing still life constraints as a planar tiling

pcallahan wrote: How much time does it take to print one?
It took 30 min to print 3 copies of the live cells set (4 tiles). so it's probably something like 1 per 3 minute or so.

Another point is that I'm printing with some settings which might not be optimal - more than that, I see that some pieces need different setting than other, specifically the small parts need better resolution and denser infill.

The problem with 3d printing that you can easily get factor of 10, just due to different settings, like speed, layer height, infill density etc.

Think for example that piece twice bigger requires X8 more time. So only scaling down by 75% shortens the time by factor of two.
pcallahan wrote: Is that your intended final size?
I came up already with best recipe - the tiles should be higher (5mm), and smaller (something like 2/3 of what I posted). My current tiles are now sticking pretty well, and their size is more intuitive. But I want to check all the options before I post the optimal size, because some spacefillers have pretty complex shape, and they could change things.

-----

Another question that bothers me is the tiles on the edge of the whole construction - can they stick? or maybe we need some frame that will help to hold them all together? Frame has also some aesthetic properties, and binding properties, so the design would be more stable and portable, without fear of detaching. I guess will know soon...

EDIT About the two tiles you posted: are you suggesting to make them yellow instead of blue? I thought about it, and I think both of the options are fine - I just didn't like the grey option, those two I don't really see too much of a difference - I'll check it more when I get to the tiling script.

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

### Re: Realizing still life constraints as a planar tiling

Yes, yellow. But I wanted to avoid mentioning a color, since you printed your "yellow" tiles in blue. You can try it yourself and see which you prefer. I agree that two colors is sufficient.

simsim314
Posts: 1740
Joined: February 10th, 2014, 1:27 pm

### Re: Realizing still life constraints as a planar tiling

I printed in blue just because i didn't wanted to change the filament for debugging - and there were problems regardless of the color. I do think that the dead cells should be with "worm" color and the live with "cold".

Although thinking of it - in golly we have the live cells white, and the dead cells black, so maybe it's fine to switch the colors?

mniemiec
Posts: 1106
Joined: June 1st, 2013, 12:00 am

### Re: Realizing still life constraints as a planar tiling

simsim314 wrote:Although thinking of it - in golly we have the live cells white, and the dead cells black, so maybe it's fine to switch the colors?
If you press B, Golly's color scheme is reversed, so the choice of colors is really totally arbitrary. The most important criterion should be visibility - live cells should be visibly distinct from dead cells, without being visually jarring (e.g. red and green).

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

### Re: Realizing still life constraints as a planar tiling

Here's my hand-rendering of the eater with the new tiles.
Screen Shot 2016-06-04 at 9.49.58 AM.png (122.72 KiB) Viewed 12063 times
UPDATE: I changed the tile coloring to match calcyman's. I think I now understand the reasoning behind using blue only for the tile that connects two live cells on a diagonal or connects four live cells inside the boundaries of a block. That is the minimal set of blue "spacefiller" tiles that results in connected live area (including not orphaning a yellow tile inside a block). The other ones extend the live area but are not needed to connect it.

simsim314
Posts: 1740
Joined: February 10th, 2014, 1:27 pm

### Re: Realizing still life constraints as a planar tiling

Indeed calcyman version looks more intuitive to me as well.

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

### Re: Realizing still life constraints as a planar tiling

simsim314, just curious if you have had time to print any more of these.

simsim314
Posts: 1740
Joined: February 10th, 2014, 1:27 pm

### Re: Realizing still life constraints as a planar tiling

pcallahan wrote:simsim314, just curious if you have had time to print any more of these.
I'm intending to do it soon (probably this weekend)... I was thinking to add chamfer to make them look more professional. I still need to fine tune the tolerances, but I think I'm pretty close already.

shouldsee
Posts: 406
Joined: April 8th, 2016, 8:29 am

### Re: Realizing still life constraints as a planar tiling

A dumb question maybe, but if we can realise still lives as planar tiling, could we then possiblly model space-time trajectories as 3D tiling?

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

### Re: Realizing still life constraints as a planar tiling

shouldsee wrote:A dumb question maybe, but if we can realise still lives as planar tiling, could we then possiblly model space-time trajectories as 3D tiling?
The short answer is yes, but it would require more tiles. I sketched an approach in viewtopic.php?f=7&t=2188&start=50#p31265

Instead of only allowing neighborhoods that preserve the state, you could represent the next state as a nub or dent on top of the tile.

pcallahan
Posts: 490
Joined: April 26th, 2013, 1:04 pm

### Re: Realizing still life constraints as a planar tiling

In the original tiling, I put the focus on octagons centered at cell positions, and the other six tiles are just there to fit together diagonal neighbors. The horizontal and vertical neighbors are always touching.

I realized, and maybe it's obvious to others, that the six diagonal tiles represent an entire 2x2 neighborhood to the northeast, southeast, southwest, or northwest of the Life cell. It is just as reasonable to put the focus on these tiles and use them to constrain all the neighborhoods. The octagons do not have to touch at all. All the constraints can be carried out through the tiles placed at the corners. This gives some flexibility to make them the same size or bigger than the octagons. There are still six of them, because there are 6 distinct 2x2 bitmaps up to rotation. So consider this tile set:
Screen Shot 2020-01-13 at 1.20.05 PM.png (40.8 KiB) Viewed 1187 times
They can be fit together with the narrow sides touching, leaving octagons instead. Use the rule that tiles can only touch if the adjacent dots match up (this can be implemented as a jigsaw contour). So they can be fit together leaving octagons for assigning still life constraints.
Screen Shot 2020-01-13 at 1.20.22 PM.png (155.67 KiB) Viewed 1187 times
To complete the construction of a tiling set, we would just create contours for all the octagons consistent with a still life, and assign complementary contours to the "+"-shaped tiles. All the same minimizations would apply. However, the pieces might be easier to work with.

Another consideration is that the "+"-shaped tiles represent flip-symmetric neighborhoods, so their realization as tiles need not be flip-symmetric. If the touching edges of these tiles interlock like jigsaw pieces, it becomes less important for any of the other pieces to interlock. They just need to fill in the octagon gaps.

Anyway, just another option to consider. I am inclined to ignore most of the ideas about decomposing the octagons if I ever produce these tiles. It's unclear if minimizing the number of jigsaw pieces makes a difference in usability, while having pieces that are all about the same size does make a difference.