Realizing still life constraints as a planar tiling

For general discussion about Conway's Game of Life.
User avatar
pcallahan
Posts: 845
Joined: April 26th, 2013, 1:04 pm

Re: Realizing still life constraints as a planar tiling

Post by pcallahan » May 16th, 2016, 8:40 pm

simsim314 wrote:I was thinking, if we want to make some sort of physical puzzle game out of these tiles - we need to make sure they can be attached properly.
You raise some good points and I have been wondering about similar issues. There are some unavoidable constraints as I see it. Matching edges need to be flip-symmetric, but some edges need to match themselves, which means some edges have to be straight line segments, like the matching boundaries of blue-teeth and toothless white octagon edges. Diagonal tiles need to be connected and non-intersecting, which imposes some additional constraints, and fitting the cross-tile inside a blue neighborhood imposes constraints as well. I think calcyman made the blue teeth even flatter to handle this.

One exotic solution I considered is that if you have thick enough tiles, then to match edges, you can think of the edge as a 2-d face and use a rotationally symmetric pattern to accommodate flips. E.g., imagine the flat boundary to be a rectangle with a bump on one side and a matching dent on the other. (EDIT: no this won't work, but I mean a vertically aligned bump/dent pair on one side and an opposite dent/bump pair on the other so the surfaces always match up under rotations and flips). These would be harder to fabricate and just seemed to move away from the spirit of simplicity.

I'm open to any suggestions. I think these tiles would be at least a little fun to play with, though I'm not sure how far the appeal would extend past the Life community.

EDIT: a cursory look suggests to me that the best opportunity for interlock (like a jigsaw puzzle) is to change the teeth from rectangles into trapezoids that widen as they extend out. The white teeth could be lengthened at a cost of having to add grooves to the blue tiles where they meet (but there is not much room in the narrowly connected blue tiles). It should also be possible to put grooves on the right and left sides of the teeth, which would only change the shape of the space-filling tiles and could be made interlocking. Unfortunately, it all gets complex and skinny. I think these pieces might be very fragile.
Last edited by pcallahan on May 17th, 2016, 9:17 pm, edited 1 time in total.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 17th, 2016, 7:29 am

3d shape is an interesting option. Think that you can scale up and down, and stack in Z axis, and this will naturally improve the attachment capacity. Another idea is to use magnetic paint.

I think that low tek solutions + magnetic paint, or high-tek 3d printed solution should be our main options.

Any ideas for "good" 3d shape design which will stick well, but will also not break and easy to turn apart? Adding bump will not actually work - because some of the shapes require all 8 symmetry options, and this means you can't chose the location of the bump, so two bumps could meet. Stacking scaled options sound the best variant.

EDIT Somewhat trivial idea, yet it's the best I came up with so far. Adding another connector tile (so we get 17 tiles), and holes in every "plain" edge. The connector will connect all the "straights" parts.
Tiling proccess
Tiling proccess
exampleTile.png (28.64 KiB) Viewed 18862 times
And here are all the tiles:
16tiles_fixed_holed_full.jpg
16tiles_fixed_holed_full.jpg (138.73 KiB) Viewed 18862 times
*Sorry for the ugly paint usage, I hope the idea is clear.

EDIT2 This will make somewhat fun yet very tedious construction game, to even more tedious game. Placing all those connectors is kinda hell.

User avatar
pcallahan
Posts: 845
Joined: April 26th, 2013, 1:04 pm

Re: Realizing still life constraints as a planar tiling

Post by pcallahan » May 17th, 2016, 4:04 pm

simsim314 wrote:Adding bump will not actually work - because some of the shapes require all 8 symmetry options, and this means you can't chose the location of the bump, so two bumps could meet.
Sorry. My explanation was incorrect. This is what I was thinking of, though it is a somewhat optimized since my first thought:

Here's a more detailed idea for making self-matching edges using 3D boundaries, rendered using my best attempt at transparent colors in Google drawing.
Screen Shot 2016-05-17 at 1.23.26 PM.png
Screen Shot 2016-05-17 at 1.23.26 PM.png (53.23 KiB) Viewed 18857 times
The "tiles" now have a bump on the top layer left and bottom layer right. They have a matching dent on the bottom layer left and the top layer right. So the dents and bumps will match for all rotations and flips (and you can use a variety of bump shapes and combinations as long as the boundary is rotationally symmetric). You can also see how to construct these by gluing together ordinary tiles back-to-back. It is still a more complex fabrication process than I would like, and breaks some inherent symmetry, but it might have some advantages for working with plastic tiles.

Finally, here is my schematic rendering of calcyman's tiles (I hope I didn't make a mistake) using colors when possible to indicate matching boundaries. In the case of the 2-3 neighborhoods, you need the trick of an interior tile to prevent the formation of 4-neighborhoods.
Screen Shot 2016-05-17 at 11.53.11 AM.png
Screen Shot 2016-05-17 at 11.53.11 AM.png (16.96 KiB) Viewed 18860 times

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 17th, 2016, 8:58 pm

I've finished to model the tiles in OpenSCAD. Now people with access to 3d printer can print them.

I'll post the OpenSCAD code here, and hopefully will post the STLs on thingiverse (with link here) tomorrow.

Code: Select all

module Slice(r) 
{
polygon([[0,0],[r, 0],[r * cos(360/8), r* sin(360/8)]] )  ;
};

module SliceWithTooth(r) 
{
    union()
    {
        Slice(r);
        rotate(360/16) translate([r * 0.83,  -r/10])square([r/5,r/5]);
    }
};

module EmptyTile4_2teeth_90(r)
{
    union()
    {
        SliceWithTooth(r);
        rotate(360/8) SliceWithTooth(r);
    }
};

module EmptyTile4_1teeth_90(r)
{
    union()
    {
        Slice(r);
        rotate(360/8) SliceWithTooth(r);
    }
};

module EmptyTile4_2teeth_180(r)
{
    union()
    {
        Slice(r);
        rotate(45) Slice(r);
        rotate(90) EmptyTile4_2teeth_90(r);
    }
};


module SquareTile(r)
{
    translate([-r * sin(45.0 / 2), -r * sin(45.0 / 2)]) square([2 * r * sin(45.0 / 2), 2 * r * sin(45.0 / 2)]);
};

module EmptyTile0_NoTeeth(r)
{
    difference()
    {
        union()
        {
            Slice(r);
            rotate(45) Slice(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module EmptyTile0_EdgeTeeth(r)
{
    difference()
    {
        union()
        {
            SliceWithTooth(r);
            rotate(45) Slice(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module EmptyTile0_InternalTeeth(r)
{
    difference()
    {
        union()
        {
            Slice(r);
            rotate(45) SliceWithTooth(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module LiveTooth(r1)
{
    r = r1 * 1.25;
    union()
    {
        translate([-r/4, -r/4]) square([r/2, r/2]);
        translate([r/3.9, r/3.9]) rotate(180 + 45) circle(r/7,$fn=5);
    }    
};

module LiveSlice(r)
{
    r1 = (2 * r * cos(45/2) - (r * 0.83 + r/5)) / cos(45/2);
    color([0, 0.4, 0.9]) Slice(r1);
};

module LiveSliceWithTooth(r) 
{
    size = r/3;
    color([0, 0.4, 0.9]) union()
    {
        LiveSlice(r);
        rotate(360/16) translate([r * cos(45/2) - size,  -size/2])square([size,size]);
    }
};

module TileLife_InternalTooth(r)
{
   color([0, 0.4, 0.9]) 
    
        union()
        {
            LiveTooth(r);
            rotate(90) LiveTooth(r);
            rotate(180) LiveTooth(r);
            rotate(-90) LiveTooth(r);
        }
        
};

module TileLife_EmptyInternalEdgeTooth(r)
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSlice(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module TileLife_EmptyInternalTooth(r)
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            rotate(45) LiveSliceWithTooth(r);
            LiveSlice(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module TileLife_FilledInternalEdgeTooth(r)
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSlice(r);
            rotate(90) LiveSliceWithTooth(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module TileLife_FilledInternalTooth(r)
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSliceWithTooth(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module Helper1_Tile(r)
{
    side = r * cos(45.0 / 2);

    translate([side, -side]) rotate([0, 180, 0])rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
    translate([side, -side]) rotate(45 + 90 - 45/2) EmptyTile0_EdgeTeeth(100);

};

module Helper2_Tile(r, a = 0)
{
    side = r * cos(45.0 / 2);

   
 translate([-side, side]) rotate(a + 90 +45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, side])rotate([0, 180, 0])rotate(a + 45 / 2 + 45) TileLife_EmptyInternalEdgeTooth(100);
        translate([-side, side]) rotate(a + -45/2) TileLife_InternalTooth(100);

};


module TileSpaceFiller_Type1(r)
{
    side = r * cos(45.0 / 2);
    difference()
    {
        polygon([[-side, side], [-side, 0], [0, -side], [side, 0], [0, side]]);  
        translate([side, -side]) rotate(45 + 45/2) EmptyTile0_InternalTeeth(100);
        translate([-side, -side]) rotate([0, 180, 0]) rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
        translate([-side, side]) rotate(90 +45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, side])rotate([0, 180, 0])rotate(45 / 2 + 45) TileLife_EmptyInternalEdgeTooth(100);
        translate([-side, side]) rotate(-45/2) TileLife_InternalTooth(100);
        translate([side, side])  rotate(45/2 + 90 + 45) EmptyTile0_EdgeTeeth(100);
    }
};


module TileSpaceFiller_Type2(r)
{
    side = r * cos(45.0 / 2);
    
    difference()
    {
        polygon([[-side, side], [-side, 0], [0, -side], [side, 0], [side, side]]); 

        Helper1_Tile(100); 
        rotate(-90)Helper1_Tile(100);
        translate([-side, -side])  rotate([0, 180, 0]) rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
        
        translate([-side, side]) rotate(-90 - 45/2) rotate([0, 180, 0]) TileLife_FilledInternalTooth(100);
        translate([side, side]) rotate(90 + 45 - 45/2) TileLife_FilledInternalTooth(100);    
    }
    
};

module TileSpaceFiller_Type3(r)
{
    side = r * cos(45.0 / 2);
    
    difference()
    {
        polygon([[-side, side], [-side, -side], [side, -side], [side, side]]); 

        translate([-side, side])rotate([0, 180, 0])rotate(45/2)EmptyTile4_2teeth_180(100);
        translate([-side, side])rotate(135  + 45/2)EmptyTile4_2teeth_180(100);
        translate([side, side])rotate(180 +45/2) TileLife_FilledInternalTooth(100);
        translate([side, side])rotate(45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, -side])rotate(-45 + 45/2) TileLife_FilledInternalTooth(100);
        translate([side, -side])rotate(45 + 45/2) TileLife_FilledInternalEdgeTooth(100);
 
    }
    
};

module TileSpaceFiller_Type4(r)
{
    side = r * cos(45.0 / 2);
     color([0, 0.4, 0.9]) 
    difference()
    {
        polygon([[-side, side], [-side, -side], [side, -side], [side, side]]); 

        translate([side, -side])rotate([0, 180, 0])rotate(45/2)EmptyTile4_1teeth_90(100);
        translate([side, -side])rotate([0, 180, 0])rotate(-90 + 45/2)EmptyTile4_1teeth_90(100);
        translate([-side, side])rotate([0, 180, 0])rotate(180 + 45/2)EmptyTile4_1teeth_90(100);
        translate([-side, side])rotate([0, 180, 0])rotate(90 + 45/2)EmptyTile4_1teeth_90(100);
        translate([side, side])rotate(90 + 45 / 2) TileLife_FilledInternalEdgeTooth(100);
        translate([-side, -side])rotate(-90 + 45 / 2) TileLife_FilledInternalEdgeTooth(100);

    }
    
};

//linear_extrude(height = 10) TileSpaceFiller_Type1(100);

r = 100;
side = r * cos(45.0 / 2);
d = 2.5 * r;
color([0, 0.4, 0.9]) linear_extrude(15)translate([-2 * d, 2 * d]) TileSpaceFiller_Type4(100);
linear_extrude(15)translate([- d, 2 * d])TileSpaceFiller_Type3(100);
linear_extrude(15)translate([0, 2 * d])TileSpaceFiller_Type1(100);
linear_extrude(15)translate([d, 2 * d])TileSpaceFiller_Type2(100);

color([0, 0.4, 0.9]) linear_extrude(15)translate([- 2 * d,  d])TileLife_InternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([-  d,  d])TileLife_EmptyInternalEdgeTooth(100);
color([0, 0.4, 0.9])linear_extrude(15)translate([0,  d])TileLife_EmptyInternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([d,  d])TileLife_FilledInternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([- 2 * d,  0])TileLife_FilledInternalEdgeTooth(100);

linear_extrude(15)translate([- d,  0])EmptyTile4_2teeth_180(100);
linear_extrude(15)translate([0,  0])EmptyTile4_1teeth_90(100);
linear_extrude(15)translate([d,  0])EmptyTile4_2teeth_90(100);

linear_extrude(15)translate([- 2 * d,  -d])SquareTile(100);
linear_extrude(15)translate([- d,  -d])EmptyTile0_NoTeeth(100);
linear_extrude(15)translate([0,  -d])EmptyTile0_InternalTeeth(100);
linear_extrude(15)translate([d,  -d])EmptyTile0_EdgeTeeth(100);
And some images:
Front View
Front View
1.png (38.45 KiB) Viewed 18843 times
Isometric View
Isometric View
2.png (40.57 KiB) Viewed 18843 times
Hopefully will get to the "holding teeth" as well. Missed your idea myself.

EDIT I like the idea of the holding teeth. But I'm not sure how exactly make the tooth hold well in all 3 axes, holding well and yet can be taken apart. I thought of a "nail" on top and the bottom, but it will probably tend to brake or not hold (thin and long will break/impossible to attach, and wide and short will not hold so well). Any suggestions?

User avatar
pcallahan
Posts: 845
Joined: April 26th, 2013, 1:04 pm

Re: Realizing still life constraints as a planar tiling

Post by pcallahan » May 17th, 2016, 9:21 pm

Nice images! Now I would really like to see a set of those 3D printed.

I'm personally not that worried about the pattern holding together. Most tilings don't (e.g. Penrose). With solid enough material on a surface with a little friction, you wouldn't be worried about the pieces moving themselves around.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 17th, 2016, 10:17 pm

But you couldn't pick them up from the surface. People do invest effort to make tiling stick, in Penrose case as well:

For exmaple:

Image

This trick can't really work in our case - and we also need something which is 3d printing friendly, i.e limited Z gradient. Probably some more "smooth" variant of your teeth design would work fine. I'll try to come up with something later...

BTW took me time to realize the octagon for the living cells is smaller...

User avatar
pcallahan
Posts: 845
Joined: April 26th, 2013, 1:04 pm

Re: Realizing still life constraints as a planar tiling

Post by pcallahan » May 18th, 2016, 1:03 am

simsim314 wrote:BTW took me time to realize the octagon for the living cells is smaller...
You probably noticed this by now, but the blue teeth are constrained to meet at the boundary of the octagon the tiling is based on. There is more flexibility with the white teeth, because you can shrink the inner part of the blue tile somewhat (eventually if they're too long, you get self-intersecting corner tiles).

I originally had a much more complicated scheme in mind, but I realized that as long as the area around the blue teeth was removed, it could be filled by a connected corner tile. The remaining part of the problem was how to reduce the number of tiles that represent neighborhoods of varying cardinality.

I wonder if the number of tiles is the right thing to optimize. The addition of a single, small, joining tile would allow all of the self-matching boundaries to be constructed in unambiguous ways (which I realize is actually the same as your idea if I understand your swiss cheese holes as grooves that extend to the tile boundary, but I don't think we need nearly as many). E.g., a symmetric shape like this:
Screen Shot 2016-05-17 at 10.00.04 PM.png
Screen Shot 2016-05-17 at 10.00.04 PM.png (5.56 KiB) Viewed 18828 times
could fill the corresponding grooves on adjacent tiles and would lock them when placed two different ways (i.e. rotated 90 degrees). These could at least hook up the half neighborhoods, which would not need to be as complex as calcyman's. They could even be used to eliminate the blue teeth by matching grooves instead. However, the connecting piece would need to be pretty small and there would be extra work in assembly.
Last edited by pcallahan on May 18th, 2016, 1:43 am, edited 2 times in total.

User avatar
pcallahan
Posts: 845
Joined: April 26th, 2013, 1:04 pm

Re: Realizing still life constraints as a planar tiling

Post by pcallahan » May 18th, 2016, 1:36 am

I found one problem in this partial tiling around the block. You might not have realized that the cross piece does double duty both connecting live neighborhoods and forming the corner between 4 live cells (which is where it originally came from). The trick is getting it small enough to fit inside the live neighborhood, and that is probably why calcyman's teeth are so blunt. It's highly constrained.
Screen Shot 2016-05-17 at 10.32.19 PM.png
Screen Shot 2016-05-17 at 10.32.19 PM.png (57.24 KiB) Viewed 18825 times

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 18th, 2016, 3:24 am

pcallahan wrote:I found one problem in this partial tiling around the block.
Woops I didn't realize the depth of the design. I've now fixed the issue you pointed, and calculated all the tiles, out of three parameter:

emptyToothWidth - the tooth width of the empty tile / radius of octagon inscribing circle.
emptyToothLength - the tooth length of the empty tile / radius of octagon inscribing circle.
filledToothWidth - the tooth width of the live tile / radius of octagon inscribing circle.

You can play with parameter inside OpenSCAD and my script (you just need to copy/paste the code into OpenSCAD and press f5, OpenSCAD - is free, open source and multi platform).

I placed those parameters in the beginning of the script.

Code: Select all

emptyToothWidth = 0.25;
emptyToothLength = 0.11;
filledToothWidth = 0.5;

module Slice(r) 
{
polygon([[0,0],[r, 0],[r * cos(360/8), r* sin(360/8)]] )  ;
};

module SliceWithTooth(r) 
{
    d = r * cos(45.0 / 2);
    union()
    {
        Slice(r);
        rotate(360/16) translate([d,  -r * emptyToothWidth / 2]) square([r * emptyToothLength,r * emptyToothWidth]);
    }
};

module EmptyTile4_2teeth_90(r)
{
    union()
    {
        SliceWithTooth(r);
        rotate(360/8) SliceWithTooth(r);
    }
};

module EmptyTile4_1teeth_90(r)
{
    union()
    {
        Slice(r);
        rotate(360/8) SliceWithTooth(r);
    }
};

module EmptyTile4_2teeth_180(r)
{
    union()
    {
        Slice(r);
        rotate(45) Slice(r);
        rotate(90) EmptyTile4_2teeth_90(r);
    }
};


module SquareTile(r)
{
    translate([-r * sin(45.0 / 2), -r * sin(45.0 / 2)]) square([2 * r * sin(45.0 / 2), 2 * r * sin(45.0 / 2)]);
};

module EmptyTile0_NoTeeth(r)
{
    difference()
    {
        union()
        {
            Slice(r);
            rotate(45) Slice(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module EmptyTile0_EdgeTeeth(r)
{
    difference()
    {
        union()
        {
            SliceWithTooth(r);
            rotate(45) Slice(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module EmptyTile0_InternalTeeth(r)
{
    difference()
    {
        union()
        {
            Slice(r);
            rotate(45) SliceWithTooth(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module LiveSlice(r)
{
    r1 = (r * cos(45/2) - emptyToothLength * r) / cos(45/2);
    color([0, 0.4, 0.9]) Slice(r1);
};

module LiveSliceWithTooth(r) 
{
    size = r/3;
    dtooth = r * filledToothWidth;
    
    color([0, 0.4, 0.9]) union()
    {
        LiveSlice(r);
        rotate(360/16) translate([r * cos(45/2) - size,  -dtooth /2])square([size,dtooth ]);
    }
};

module HelperLife_InternalTooth(r)
{
   
    rotate(45/2) LiveSliceWithTooth(100);
    rotate(45 + 45/2) LiveSliceWithTooth(100);
    rotate(-45 + 45/2) LiveSliceWithTooth(100);
};

module TileLife_InternalTooth(r)
{
    d = r * cos(45.0 / 2);
     color([0, 0.4, 0.9]) 
    rotate(45) difference()
    {
        polygon([[-d, d], [-d, -d], [d, -d], [d, d]]); 
        translate([-d, -d]) HelperLife_InternalTooth(r);
        translate([d, -d]) rotate(90) HelperLife_InternalTooth(r);
        translate([-d, d]) rotate(-90) HelperLife_InternalTooth(r);
        translate([d, d]) rotate(180) HelperLife_InternalTooth(r);

    }
};

module TileLife_EmptyInternalEdgeTooth(r)
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSlice(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module TileLife_EmptyInternalTooth(r)
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            rotate(45) LiveSliceWithTooth(r);
            LiveSlice(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module TileLife_FilledInternalEdgeTooth(r)
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSlice(r);
            rotate(90) LiveSliceWithTooth(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module TileLife_FilledInternalTooth(r)
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSliceWithTooth(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module Helper1_Tile(r)
{
    side = r * cos(45.0 / 2);

    translate([side, -side]) rotate([0, 180, 0])rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
    translate([side, -side]) rotate(45 + 90 - 45/2) EmptyTile0_EdgeTeeth(100);

};

module Helper2_Tile(r, a = 0)
{
    side = r * cos(45.0 / 2);

   
translate([-side, side]) rotate(a + 90 +45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, side])rotate([0, 180, 0])rotate(a + 45 / 2 + 45) TileLife_EmptyInternalEdgeTooth(100);
        translate([-side, side]) rotate(a + -45/2) TileLife_InternalTooth(100);

};


module TileSpaceFiller_Type1(r)
{
    side = r * cos(45.0 / 2);
    difference()
    {
        polygon([[-side, side], [-side, 0], [0, -side], [side, 0], [0, side]]);  
        translate([side, -side]) rotate(45 + 45/2) EmptyTile0_InternalTeeth(100);
        translate([-side, -side]) rotate([0, 180, 0]) rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
        translate([-side, side]) rotate(90 +45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, side])rotate([0, 180, 0])rotate(45 / 2 + 45) TileLife_EmptyInternalEdgeTooth(100);
        translate([-side, side]) rotate(-45/2) TileLife_InternalTooth(100);
        translate([side, side])  rotate(45/2 + 90 + 45) EmptyTile0_EdgeTeeth(100);
    }
};


module TileSpaceFiller_Type2(r)
{
    side = r * cos(45.0 / 2);
    
    difference()
    {
        polygon([[-side, side], [-side, 0], [0, -side], [side, 0], [side, side]]); 

        Helper1_Tile(100); 
        rotate(-90)Helper1_Tile(100);
        translate([-side, -side])  rotate([0, 180, 0]) rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
        
        translate([-side, side]) rotate(-90 - 45/2) rotate([0, 180, 0]) TileLife_FilledInternalTooth(100);
        translate([side, side]) rotate(90 + 45 - 45/2) TileLife_FilledInternalTooth(100);   
    }
    
};

module TileSpaceFiller_Type3(r)
{
    side = r * cos(45.0 / 2);
    
    difference()
    {
        polygon([[-side, side], [-side, -side], [side, -side], [side, side]]); 

        translate([-side, side])rotate([0, 180, 0])rotate(45/2)EmptyTile4_2teeth_180(100);
        translate([-side, side])rotate(135  + 45/2)EmptyTile4_2teeth_180(100);
        translate([side, side])rotate(180 +45/2) TileLife_FilledInternalTooth(100);
        translate([side, side])rotate(45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, -side])rotate(-45 + 45/2) TileLife_FilledInternalTooth(100);
        translate([side, -side])rotate(45 + 45/2) TileLife_FilledInternalEdgeTooth(100);

    }
    
};

module TileSpaceFiller_Type4(r)
{
    side = r * cos(45.0 / 2);
     color([0, 0.4, 0.9]) 
    difference()
    {
        polygon([[-side, side], [-side, -side], [side, -side], [side, side]]); 

        translate([side, -side])rotate([0, 180, 0])rotate(45/2)EmptyTile4_1teeth_90(100);
        translate([side, -side])rotate([0, 180, 0])rotate(-90 + 45/2)EmptyTile4_1teeth_90(100);
        translate([-side, side])rotate([0, 180, 0])rotate(180 + 45/2)EmptyTile4_1teeth_90(100);
        translate([-side, side])rotate([0, 180, 0])rotate(90 + 45/2)EmptyTile4_1teeth_90(100);
        translate([side, side])rotate(90 + 45 / 2) TileLife_FilledInternalEdgeTooth(100);
        translate([-side, -side])rotate(-90 + 45 / 2) TileLife_FilledInternalEdgeTooth(100);

    }
    
};



//linear_extrude(height = 10) TileSpaceFiller_Type1(100);

r = 100;
side = r * cos(45.0 / 2);
d = 2.5 * r;
color([0, 0.4, 0.9]) linear_extrude(15)translate([-2 * d, 2 * d]) TileSpaceFiller_Type4(100);
linear_extrude(15)translate([- d, 2 * d])TileSpaceFiller_Type3(100);
linear_extrude(15)translate([0, 2 * d])TileSpaceFiller_Type1(100);
linear_extrude(15)translate([d, 2 * d])TileSpaceFiller_Type2(100);

color([0, 0.4, 0.9]) linear_extrude(15)translate([- 2 * d,  d])TileLife_InternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([-  d,  d])TileLife_EmptyInternalEdgeTooth(100);
color([0, 0.4, 0.9])linear_extrude(15)translate([0,  d])TileLife_EmptyInternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([d,  d])TileLife_FilledInternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([- 2 * d,  0])TileLife_FilledInternalEdgeTooth(100);

linear_extrude(15)translate([- d,  0])EmptyTile4_2teeth_180(100);
linear_extrude(15)translate([0,  0])EmptyTile4_1teeth_90(100);
linear_extrude(15)translate([d,  0])EmptyTile4_2teeth_90(100);

linear_extrude(15)translate([- 2 * d,  -d])SquareTile(100);
linear_extrude(15)translate([- d,  -d])EmptyTile0_NoTeeth(100);
linear_extrude(15)translate([0,  -d])EmptyTile0_InternalTeeth(100);
linear_extrude(15)translate([d,  -d])EmptyTile0_EdgeTeeth(100);
Here is my best attempt (I was trying to optimize breakability of the tiles):
Tile params optimized
Tile params optimized
1.png (22.39 KiB) Viewed 18817 times
EDIT There is online OpenSCAD online but they don't support basic functions like "module" which is the same as function in any other language. So it's simpler just to download OpenSCAD.

EDIT2
Maybe these somewhat better:
emptyToothWidth = 0.3;
emptyToothLength = 0.12;
filledToothWidth = 0.45;

EDIT3 I think those are optimized in all domains in the best way:
emptyToothWidth = 0.28;
emptyToothLength = 0.13;
filledToothWidth = 0.48;
Optimized2
Optimized2
1.png (24.54 KiB) Viewed 18817 times
EDIT4 I think for the 4 and more teeth we can use this holder:
Holder
Holder
3.png (1.3 KiB) Viewed 18815 times

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 18th, 2016, 5:20 am

OK here is the updated tile set, the 4 teeth should now stick together well:
tiles.png
tiles.png (5.48 KiB) Viewed 18813 times

Code: Select all

//main parameters
emptyToothWidth = 0.3;
emptyToothLength = 0.13;
filledToothWidth = 0.49;

//4 teeth case parameters
Twidth = 1.1;
THeight = 0.5;

module Slice(r) 
{
polygon([[0,0],[r, 0],[r * cos(360/8), r* sin(360/8)]] )  ;
};

module SliceWithTooth(r) 
{
    d = r * cos(45.0 / 2);
    union()
    {
        Slice(r);
        rotate(360/16) translate([d,  -r * emptyToothWidth / 2]) square([r * emptyToothLength,r * emptyToothWidth]);
    }
};

module EmptyTile4_2teeth_90(r)
{
    x = Twidth * r;
    y = THeight * r;

    difference()
    {
        union()
        {
            SliceWithTooth(r);
            rotate(360/8) SliceWithTooth(r);
        }
        rotate(45) translate([-x/2, -y/2]) square([x, y]);
        rotate(-45) translate([-x/2, -y/2]) square([x, y]);
    }
};

module EmptyTile4_1teeth_90(r)
{
    x = Twidth * r;
    y = THeight * r;

    difference()
    {
        union()
        {
            Slice(r);
            rotate(360/8) SliceWithTooth(r);
        }
        rotate(45) translate([-x/2, -y/2]) square([x, y]);
        rotate(-45) translate([-x/2, -y/2]) square([x, y]);
    }
};

module EmptyTile4_2teeth_180(r)
{
    x = Twidth * r;
    y = THeight * r;

    union()
    {
        Slice(r);
        rotate(45) Slice(r);
        rotate(90) EmptyTile4_2teeth_90(r);
        rotate(45) translate([-x/2, -y/2]) square([x, y]);
        rotate(-45) translate([-x/2, -y/2]) square([x, y]);
    }
};


module SquareTile(r)
{
    translate([-r * sin(45.0 / 2), -r * sin(45.0 / 2)]) square([2 * r * sin(45.0 / 2), 2 * r * sin(45.0 / 2)]);
};

module EmptyTile0_NoTeeth(r)
{
    difference()
    {
        union()
        {
            Slice(r);
            rotate(45) Slice(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module EmptyTile0_EdgeTeeth(r)
{
    difference()
    {
        union()
        {
            SliceWithTooth(r);
            rotate(45) Slice(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module EmptyTile0_InternalTeeth(r)
{
    difference()
    {
        union()
        {
            Slice(r);
            rotate(45) SliceWithTooth(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module LiveSlice(r)
{
    r1 = (r * cos(45/2) - emptyToothLength * r) / cos(45/2);
    color([0, 0.4, 0.9]) Slice(r1);
};

module LiveSliceWithTooth(r) 
{
    size = r/3;
    dtooth = r * filledToothWidth;
    
    color([0, 0.4, 0.9]) union()
    {
        LiveSlice(r);
        rotate(360/16) translate([r * cos(45/2) - size,  -dtooth /2])square([size,dtooth ]);
    }
};

module HelperLife_InternalTooth(r)
{
   
    rotate(45/2) LiveSliceWithTooth(100);
    rotate(45 + 45/2) LiveSliceWithTooth(100);
    rotate(-45 + 45/2) LiveSliceWithTooth(100);
};

module TileLife_InternalTooth(r)
{
    d = r * cos(45.0 / 2);
     color([0, 0.4, 0.9]) 
    rotate(45) difference()
    {
        polygon([[-d, d], [-d, -d], [d, -d], [d, d]]); 
        translate([-d, -d]) HelperLife_InternalTooth(r);
        translate([d, -d]) rotate(90) HelperLife_InternalTooth(r);
        translate([-d, d]) rotate(-90) HelperLife_InternalTooth(r);
        translate([d, d]) rotate(180) HelperLife_InternalTooth(r);

    }
};

module TileLife_EmptyInternalEdgeTooth(r)
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSlice(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }

        rotate(45) TileLife_InternalTooth(r);
    }
};

module TileLife_EmptyInternalTooth(r)
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            rotate(45) LiveSliceWithTooth(r);
            LiveSlice(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module TileLife_FilledInternalEdgeTooth(r)
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSlice(r);
            rotate(90) LiveSliceWithTooth(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module TileLife_FilledInternalTooth(r)
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSliceWithTooth(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module Helper1_Tile(r)
{
    side = r * cos(45.0 / 2);

    translate([side, -side]) rotate([0, 180, 0])rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
    translate([side, -side]) rotate(45 + 90 - 45/2) EmptyTile0_EdgeTeeth(100);

};

module Helper2_Tile(r, a = 0)
{
    side = r * cos(45.0 / 2);

   
translate([-side, side]) rotate(a + 90 +45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, side])rotate([0, 180, 0])rotate(a + 45 / 2 + 45) TileLife_EmptyInternalEdgeTooth(100);
        translate([-side, side]) rotate(a + -45/2) TileLife_InternalTooth(100);

};


module TileSpaceFiller_Type1(r)
{
    side = r * cos(45.0 / 2);
    difference()
    {
        polygon([[-side, side], [-side, 0], [0, -side], [side, 0], [0, side]]);  
        translate([side, -side]) rotate(45 + 45/2) EmptyTile0_InternalTeeth(100);
        translate([-side, -side]) rotate([0, 180, 0]) rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
        translate([-side, side]) rotate(90 +45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, side])rotate([0, 180, 0])rotate(45 / 2 + 45) TileLife_EmptyInternalEdgeTooth(100);
        translate([-side, side]) rotate(-45/2) TileLife_InternalTooth(100);
        translate([side, side])  rotate(45/2 + 90 + 45) EmptyTile0_EdgeTeeth(100);
    }
};


module TileSpaceFiller_Type2(r)
{
    side = r * cos(45.0 / 2);
    
    difference()
    {
        polygon([[-side, side], [-side, 0], [0, -side], [side, 0], [side, side]]); 

        Helper1_Tile(100); 
        rotate(-90)Helper1_Tile(100);
        translate([-side, -side])  rotate([0, 180, 0]) rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
        
        translate([-side, side]) rotate(-90 - 45/2) rotate([0, 180, 0]) TileLife_FilledInternalTooth(100);
        translate([side, side]) rotate(90 + 45 - 45/2) TileLife_FilledInternalTooth(100);   
    }
    
};

module TileSpaceFiller_Type3(r)
{
    side = r * cos(45.0 / 2);
    
    difference()
    {
        polygon([[-side, side], [-side, -side], [side, -side], [side, side]]); 

        translate([-side, side])rotate([0, 180, 0])rotate(45/2)EmptyTile4_2teeth_180(100);
        translate([-side, side])rotate(135  + 45/2)EmptyTile4_2teeth_180(100);
        translate([side, side])rotate(180 +45/2) TileLife_FilledInternalTooth(100);
        translate([side, side])rotate(45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, -side])rotate(-45 + 45/2) TileLife_FilledInternalTooth(100);
        translate([side, -side])rotate(45 + 45/2) TileLife_FilledInternalEdgeTooth(100);

    }
    
};

module TileSpaceFiller_Type4(r)
{
    side = r * cos(45.0 / 2);
     color([0, 0.4, 0.9]) 
    difference()
    {
        polygon([[-side, side], [-side, -side], [side, -side], [side, side]]); 


    //}
    translate([side, -side]) rotate(135 + 45/2) SliceWithTooth(r)  ;
    translate([side, -side]) rotate(90 + 45/2) Slice(r)  ;
  translate([side, -side]) rotate(45 + 45/2) SliceWithTooth(r)  ;
  
    translate([-side, side]) rotate(-45 + 45/2) SliceWithTooth(r)  ;
    translate([-side, side]) rotate(-90 + 45/2) Slice(r)  ;
  translate([-side, side]) rotate(-135 + 45/2) SliceWithTooth(r);  
  translate([side, side]) rotate(180 + 45/2)LiveSliceWithTooth(r) ;
  translate([side, side]) rotate(135 + 45/2)LiveSlice(r) ;
  translate([side, side]) rotate(-135 + 45/2)LiveSlice(r) ;
  
    translate([-side, -side]) rotate(45/2)LiveSliceWithTooth(r) ;
  translate([-side,-side]) rotate(45 + 45/2)LiveSlice(r) ;
  translate([-side, -side]) rotate(-45 + 45/2)LiveSlice(r) ;
    }
};


r = 100;
side = r * cos(45.0 / 2);
d = 2.5 * r;
color([0, 0.4, 0.9]) linear_extrude(15)translate([-2 * d, 2 * d]) TileSpaceFiller_Type4(100);
linear_extrude(15)translate([- d, 2 * d])TileSpaceFiller_Type3(100);
linear_extrude(15)translate([0, 2 * d])TileSpaceFiller_Type1(100);
linear_extrude(15)translate([d, 2 * d])TileSpaceFiller_Type2(100);

color([0, 0.4, 0.9]) linear_extrude(15)translate([- 2 * d,  d])TileLife_InternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([-  d,  d])TileLife_EmptyInternalEdgeTooth(100);
color([0, 0.4, 0.9])linear_extrude(15)translate([0,  d])TileLife_EmptyInternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([d,  d])TileLife_FilledInternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([- 2 * d,  0])TileLife_FilledInternalEdgeTooth(100);

linear_extrude(15)translate([- d,  0])EmptyTile4_2teeth_180(100);
linear_extrude(15)translate([0,  0])EmptyTile4_1teeth_90(100);
linear_extrude(15)translate([d,  0])EmptyTile4_2teeth_90(100);

linear_extrude(15)translate([- 2 * d,  -d])SquareTile(100);
linear_extrude(15)translate([- d,  -d])EmptyTile0_NoTeeth(100);
linear_extrude(15)translate([0,  -d])EmptyTile0_InternalTeeth(100);
linear_extrude(15)translate([d,  -d])EmptyTile0_EdgeTeeth(100);

User avatar
pcallahan
Posts: 845
Joined: April 26th, 2013, 1:04 pm

Re: Realizing still life constraints as a planar tiling

Post by pcallahan » May 18th, 2016, 9:26 am

simsim314 wrote:EDIT4 I think for the 4 and more teeth we can use this holder:
The quarter tiles also need to fit together by themselves, e.g. for this neighborhood:
Screen Shot 2016-05-18 at 6.21.07 AM.png
Screen Shot 2016-05-18 at 6.21.07 AM.png (9.62 KiB) Viewed 18803 times
I'm not sure what can be done with that tile set without adding a new connector tile. If the white teeth are made trapezoidal and extended, these could at least be linked to neighboring blue tiles.

EDIT 1:
This is roughly how I was thinking of making the white (yellow) teeth locking. You would also need a corresponding groove where they meet blue tiles and a corresponding bump where the flat parts of blue tiles meet corner tiles.
Screen Shot 2016-05-18 at 7.03.53 AM.png
Screen Shot 2016-05-18 at 7.03.53 AM.png (22.98 KiB) Viewed 18800 times
Last edited by pcallahan on May 18th, 2016, 10:07 am, edited 1 time in total.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 18th, 2016, 10:07 am

Woops...we can go back to the "grey tiles", and use the internal 8 folded symmetrical tile that calcyman used for this purpose as well.

And to avoid attachment of two different colors - we can use the other blue space filling tile, as the living tiles set require flipXY symmetry only. Personally I kinda liked the grey set, it was emphasizing the state itself. On the other hand I do admit it somewhat less aesthetic.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 18th, 2016, 1:12 pm

Here is still valid collection with better interlocking, but with color issues:
Tiles with better interlocking
Tiles with better interlocking
tiles.png (5.2 KiB) Viewed 18783 times
Also I understood your solution, it's nice. I'm working on something more 3d printing friendly, here is an example. This will work on the square, and all the straight edges.
tile_teeth.png
tile_teeth.png (5.3 KiB) Viewed 18783 times
The collection SCAD:

Code: Select all

emptyToothWidth = 0.4;
emptyToothLength = 0.12;
filledToothWidth = 0.4;

module Slice(r) 
{
polygon([[0,0],[r, 0],[r * cos(360/8), r* sin(360/8)]] )  ;
};

module SliceWithTooth(r) 
{
    d = r * cos(45.0 / 2);
    union()
    {
        Slice(r);
        rotate(360/16) translate([d,  -r * emptyToothWidth / 2]) square([r * emptyToothLength,r * emptyToothWidth]);
    }
};

module EmptyTile4_2teeth_90(r)
{
     difference()
    {
    union()
    {
        SliceWithTooth(r);
        rotate(360/8) SliceWithTooth(r);
    }
       TileLife_InternalTooth(r);
    }
};

module EmptyTile4_1teeth_90(r)
{
    
    difference()
    {
    union()
    {
        Slice(r);
        rotate(360/8) SliceWithTooth(r);
    }
       TileLife_InternalTooth(r);
    }
};

module EmptyTile4_2teeth_180(r)
{

    union()
    {
        Slice(r);
        rotate(45) Slice(r);
        rotate(90) EmptyTile4_2teeth_90(r);
        TileLife_InternalTooth(r);
    }
    
};


module SquareTile(r)
{
    translate([-r * sin(45.0 / 2), -r * sin(45.0 / 2)]) square([2 * r * sin(45.0 / 2), 2 * r * sin(45.0 / 2)]);
};

module EmptyTile0_NoTeeth(r)
{
    difference()
    {
        union()
        {
            Slice(r);
            rotate(45) Slice(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module EmptyTile0_EdgeTeeth(r)
{
    difference()
    {
        union()
        {
            SliceWithTooth(r);
            rotate(45) Slice(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module EmptyTile0_InternalTeeth(r)
{
    difference()
    {
        union()
        {
            Slice(r);
            rotate(45) SliceWithTooth(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module LiveSlice(r)
{
    r1 = (r * cos(45/2) - emptyToothLength * r) / cos(45/2);
    color([0, 0.4, 0.9]) Slice(r1);
};

module LiveSliceWithTooth(r) 
{
    size = r/3;
    dtooth = r * filledToothWidth;
    
    color([0, 0.4, 0.9]) union()
    {
        LiveSlice(r);
        rotate(360/16) translate([r * cos(45/2) - size,  -dtooth /2])square([size,dtooth ]);
    }
};

module HelperLife_InternalTooth(r)
{
   
    rotate(45/2) LiveSliceWithTooth(100);
    rotate(45 + 45/2) LiveSliceWithTooth(100);
    rotate(-45 + 45/2) LiveSliceWithTooth(100);
};

module TileLife_InternalTooth(r)
{
    d = r * cos(45.0 / 2);
     color([0, 0.4, 0.9]) 
    rotate(45) difference()
    {
        polygon([[-d, d], [-d, -d], [d, -d], [d, d]]); 
        translate([-d, -d]) HelperLife_InternalTooth(r);
        translate([d, -d]) rotate(90) HelperLife_InternalTooth(r);
        translate([-d, d]) rotate(-90) HelperLife_InternalTooth(r);
        translate([d, d]) rotate(180) HelperLife_InternalTooth(r);

    }
};

module TileLife_EmptyInternalEdgeTooth(r)
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSlice(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileSpaceFiller_Type4(r);
    }
};

module TileLife_EmptyInternalTooth(r)
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            rotate(45) LiveSliceWithTooth(r);
            LiveSlice(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileSpaceFiller_Type4(r);
    }
};

module TileLife_FilledInternalEdgeTooth(r)
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSlice(r);
            rotate(90) LiveSliceWithTooth(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileSpaceFiller_Type4(r);
    }
};

module TileLife_FilledInternalTooth(r)
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSliceWithTooth(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileSpaceFiller_Type4(r);
    }
};

module Helper1_Tile(r)
{
    side = r * cos(45.0 / 2);

    translate([side, -side]) rotate([0, 180, 0])rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
    translate([side, -side]) rotate(45 + 90 - 45/2) EmptyTile0_EdgeTeeth(100);

};

module Helper2_Tile(r, a = 0)
{
    side = r * cos(45.0 / 2);

   
translate([-side, side]) rotate(a + 90 +45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, side])rotate([0, 180, 0])rotate(a + 45 / 2 + 45) TileLife_EmptyInternalEdgeTooth(100);
        translate([-side, side]) rotate(a + -45/2) TileLife_InternalTooth(100);

};


module TileSpaceFiller_Type1(r)
{
    side = r * cos(45.0 / 2);
    difference()
    {
        polygon([[-side, side], [-side, 0], [0, -side], [side, 0], [0, side]]);  
        translate([side, -side]) rotate(45 + 45/2) EmptyTile0_InternalTeeth(100);
        translate([-side, -side]) rotate([0, 180, 0]) rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
        translate([-side, side]) rotate(90 +45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, side])rotate([0, 180, 0])rotate(45 / 2 + 45) TileLife_EmptyInternalEdgeTooth(100);
        translate([-side, side]) rotate(-45/2) TileLife_InternalTooth(100);
        translate([side, side])  rotate(45/2 + 90 + 45) EmptyTile0_EdgeTeeth(100);
    }
};


module TileSpaceFiller_Type2(r)
{
    side = r * cos(45.0 / 2);
    
    difference()
    {
        polygon([[-side, side], [-side, 0], [0, -side], [side, 0], [side, side]]); 

        Helper1_Tile(100); 
        rotate(-90)Helper1_Tile(100);
        translate([-side, -side])  rotate([0, 180, 0]) rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
        
        translate([-side, side]) rotate(-90 - 45/2) rotate([0, 180, 0]) TileLife_FilledInternalTooth(100);
        translate([side, side]) rotate(90 + 45 - 45/2) TileLife_FilledInternalTooth(100);   
    }
    
};

module TileSpaceFiller_Type3(r)
{
    side = r * cos(45.0 / 2);
    
    difference()
    {
        polygon([[-side, side], [-side, -side], [side, -side], [side, side]]); 

        translate([-side, side])rotate([0, 180, 0])rotate(45/2)EmptyTile4_2teeth_180(100);
        translate([-side, side])rotate(135  + 45/2)EmptyTile4_2teeth_180(100);
        translate([side, side])rotate(180 +45/2) TileLife_FilledInternalTooth(100);
        translate([side, side])rotate(45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, -side])rotate(-45 + 45/2) TileLife_FilledInternalTooth(100);
        translate([side, -side])rotate(45 + 45/2) TileLife_FilledInternalEdgeTooth(100);

    }
    
};


module TileSpaceFiller_Type4(r)
{
    side = r * cos(45.0 / 2);
     color([0, 0.4, 0.9]) 
    difference()
    {
        polygon([[-side, side], [-side, -side], [side, -side], [side, side]]); 


    //}
    translate([side, -side]) rotate(135 + 45/2) SliceWithTooth(r)  ;
    translate([side, -side]) rotate(90 + 45/2) Slice(r)  ;
  translate([side, -side]) rotate(45 + 45/2) SliceWithTooth(r)  ;
  
    translate([-side, side]) rotate(-45 + 45/2) SliceWithTooth(r)  ;
    translate([-side, side]) rotate(-90 + 45/2) Slice(r)  ;
  translate([-side, side]) rotate(-135 + 45/2) SliceWithTooth(r);  
  translate([side, side]) rotate(180 + 45/2)LiveSliceWithTooth(r) ;
  translate([side, side]) rotate(135 + 45/2)LiveSlice(r) ;
  translate([side, side]) rotate(-135 + 45/2)LiveSlice(r) ;
  
    translate([-side, -side]) rotate(45/2)LiveSliceWithTooth(r) ;
  translate([-side,-side]) rotate(45 + 45/2)LiveSlice(r) ;
  translate([-side, -side]) rotate(-45 + 45/2)LiveSlice(r) ;
    }
};


//linear_extrude(height = 10) TileSpaceFiller_Type1(100);

r = 100;
side = r * cos(45.0 / 2);
d = 2.5 * r;
color([0, 0.4, 0.9]) linear_extrude(15)translate([-2 * d, 2 * d]) TileSpaceFiller_Type4(100);
linear_extrude(15)translate([- d, 2 * d])TileSpaceFiller_Type3(100);
linear_extrude(15)translate([0, 2 * d])TileSpaceFiller_Type1(100);
linear_extrude(15)translate([d, 2 * d])TileSpaceFiller_Type2(100);

color([0.5, 0.5, 0.5]) linear_extrude(15)translate([- 2 * d,  d])TileLife_InternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([-  d,  d])TileLife_EmptyInternalEdgeTooth(100);
color([0, 0.4, 0.9])linear_extrude(15)translate([0,  d])TileLife_EmptyInternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([d,  d])TileLife_FilledInternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([- 2 * d,  0])TileLife_FilledInternalEdgeTooth(100);

linear_extrude(15)translate([- d,  0])EmptyTile4_2teeth_180(100);
linear_extrude(15)translate([0,  0])EmptyTile4_1teeth_90(100);
linear_extrude(15)translate([d,  0])EmptyTile4_2teeth_90(100);

linear_extrude(15)translate([- 2 * d,  -d])SquareTile(100);
linear_extrude(15)translate([- d,  -d])EmptyTile0_NoTeeth(100);
linear_extrude(15)translate([0,  -d])EmptyTile0_InternalTeeth(100);
linear_extrude(15)translate([d,  -d])EmptyTile0_EdgeTeeth(100);
The tooth SCAD.

Code: Select all

//main parameters
emptyToothWidth = 0.3;
emptyToothLength = 0.13;
filledToothWidth = 0.49;

//4teeth_empty, internal tooth parameters
Twidth = 1.1;
THeight = 0.5;

//Height 
H = 20;

//3d structural teeth 
cubeSizeH = 0.04;
cubeSizeW = 0.08;
cubeAngle = 15;

//Adding 3d tooths flag
add3dStructure = true; 

module Conn_Cube(r, a, alpha)
{
    x = r * alpha + r * cos(45) * (1 - alpha);
    y = r * sin(45) * (1 - alpha);
    translate([x, y]) 
    
    intersection()
{
     translate([0, 0, H/4]) 
rotate([0,0,45/2])rotate([0,cubeAngle * a,0])cube([cubeSizeH * r, cubeSizeW*r , H], center = true) ;
    
    translate([-r/4, -r/4, 0])cube([r / 2, r / 2, H/2]);
}
   
};
module SliceHelper(r) 
{
    linear_extrude(H)
    polygon([[0,0],[r, 0],[r * cos(360/8), r* sin(360/8)]] )  ;
}

module Slice(r, add3dTeeth = true) 
{
    
 if(add3dTeeth && add3dStructure)
 {
     difference()
     {
         union()
         {
            SliceHelper(r);
     
            translate([0, 0, H/2]) Conn_Cube(r, 1, 1/5);
            Conn_Cube(r, -1, 2/5);
             translate([0, 0, H/2]) Conn_Cube(r, 1, 3/5);
            Conn_Cube(r, -1, 4/5);
         }
        Conn_Cube(r, 1, 1/5);
        translate([0, 0, H/2]) Conn_Cube(r, -1, 2/5);
        Conn_Cube(r, 1, 3/5);
        translate([0, 0, H/2]) Conn_Cube(r, -1, 4/5);
        
     }
    
 }
 else 
     SliceHelper(r);
};

module SliceWithTooth(r) 
{
    d = r * cos(45.0 / 2);
    union()
    {
        Slice(r, false);
         linear_extrude(H)rotate(360/16) translate([d,  -r * emptyToothWidth / 2]) square([r * emptyToothLength,r * emptyToothWidth]);
    }
};

module EmptyTile4_2teeth_90(r)
{
    x = Twidth * r;
    y = THeight * r;

   
        union()
        {
            SliceWithTooth(r);
            rotate(360/8) SliceWithTooth(r);
        }
       
    
};

module EmptyTile4_1teeth_90(r)
{
    x = Twidth * r;
    y = THeight * r;

   
        union()
        {
            Slice(r);
            rotate(360/8) SliceWithTooth(r);
        }
       
};

module EmptyTile4_2teeth_180(r)
{
    x = Twidth * r;
    y = THeight * r;

    union()
    {
        Slice(r);
        rotate(45) Slice(r);
        rotate(90) EmptyTile4_2teeth_90(r);
        
    }
};


module SquareTile(r)
{
    translate([-r * sin(45.0 / 2), -r * sin(45.0 / 2)]) square([2 * r * sin(45.0 / 2), 2 * r * sin(45.0 / 2)]);
};

module EmptyTile0_NoTeeth(r)
{
    difference()
    {
        union()
        {
            Slice(r);
            rotate(45) Slice(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module EmptyTile0_EdgeTeeth(r)
{
    difference()
    {
        union()
        {
            SliceWithTooth(r);
            rotate(45) Slice(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module EmptyTile0_InternalTeeth(r)
{
    difference()
    {
        union()
        {
            Slice(r);
            rotate(45) SliceWithTooth(r);
            rotate(90) Slice(r);
            rotate(135) Slice(r);
        }
        
        rotate(45) SquareTile(r);
    }
};

module LiveSlice(r)
{
    r1 = (r * cos(45/2) - emptyToothLength * r) / cos(45/2);
    color([0, 0.4, 0.9]) Slice(r1);
};

module LiveSliceWithTooth(r) 
{
    size = r/3;
    dtooth = r * filledToothWidth;
    
    color([0, 0.4, 0.9]) union()
    {
        LiveSlice(r);
        rotate(360/16) translate([r * cos(45/2) - size,  -dtooth /2])square([size,dtooth ]);
    }
};

module HelperLife_InternalTooth(r)
{
   
    rotate(45/2) LiveSliceWithTooth(100);
    rotate(45 + 45/2) LiveSliceWithTooth(100);
    rotate(-45 + 45/2) LiveSliceWithTooth(100);
};

module TileLife_InternalTooth(r)
{
    d = r * cos(45.0 / 2);
     color([0, 0.4, 0.9]) 
    rotate(45) difference()
    {
        polygon([[-d, d], [-d, -d], [d, -d], [d, d]]); 
        translate([-d, -d]) HelperLife_InternalTooth(r);
        translate([d, -d]) rotate(90) HelperLife_InternalTooth(r);
        translate([-d, d]) rotate(-90) HelperLife_InternalTooth(r);
        translate([d, d]) rotate(180) HelperLife_InternalTooth(r);

    }
};

module TileLife_EmptyInternalEdgeTooth(r)
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSlice(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }

        rotate(45) TileLife_InternalTooth(r);
    }
};

module TileLife_EmptyInternalTooth(r)
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            rotate(45) LiveSliceWithTooth(r);
            LiveSlice(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module TileLife_FilledInternalEdgeTooth(r)
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSlice(r);
            rotate(90) LiveSliceWithTooth(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module TileLife_FilledInternalTooth(r)
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth(r);
            rotate(45) LiveSliceWithTooth(r);
            rotate(90) LiveSlice(r);
            rotate(135) LiveSlice(r);
        }
        rotate(45) TileLife_InternalTooth(r);
    }
};

module Helper1_Tile(r)
{
    side = r * cos(45.0 / 2);

    translate([side, -side]) rotate([0, 180, 0])rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
    translate([side, -side]) rotate(45 + 90 - 45/2) EmptyTile0_EdgeTeeth(100);

};

module Helper2_Tile(r, a = 0)
{
    side = r * cos(45.0 / 2);

   
translate([-side, side]) rotate(a + 90 +45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, side])rotate([0, 180, 0])rotate(a + 45 / 2 + 45) TileLife_EmptyInternalEdgeTooth(100);
        translate([-side, side]) rotate(a + -45/2) TileLife_InternalTooth(100);

};


module TileSpaceFiller_Type1(r)
{
    side = r * cos(45.0 / 2);
    difference()
    {
        polygon([[-side, side], [-side, 0], [0, -side], [side, 0], [0, side]]);  
        translate([side, -side]) rotate(45 + 45/2) EmptyTile0_InternalTeeth(100);
        translate([-side, -side]) rotate([0, 180, 0]) rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
        translate([-side, side]) rotate(90 +45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, side])rotate([0, 180, 0])rotate(45 / 2 + 45) TileLife_EmptyInternalEdgeTooth(100);
        translate([-side, side]) rotate(-45/2) TileLife_InternalTooth(100);
        translate([side, side])  rotate(45/2 + 90 + 45) EmptyTile0_EdgeTeeth(100);
    }
};


module TileSpaceFiller_Type2(r)
{
    side = r * cos(45.0 / 2);
    
    difference()
    {
        polygon([[-side, side], [-side, 0], [0, -side], [side, 0], [side, side]]); 

        Helper1_Tile(100); 
        rotate(-90)Helper1_Tile(100);
        translate([-side, -side])  rotate([0, 180, 0]) rotate(45 + 45/2) EmptyTile0_EdgeTeeth(100);
        
        translate([-side, side]) rotate(-90 - 45/2) rotate([0, 180, 0]) TileLife_FilledInternalTooth(100);
        translate([side, side]) rotate(90 + 45 - 45/2) TileLife_FilledInternalTooth(100);   
    }
    
};

module TileSpaceFiller_Type3(r)
{
    side = r * cos(45.0 / 2);
    
    difference()
    {
        polygon([[-side, side], [-side, -side], [side, -side], [side, side]]); 

        translate([-side, side])rotate([0, 180, 0])rotate(45/2)EmptyTile4_2teeth_180(100);
        translate([-side, side])rotate(135  + 45/2)EmptyTile4_2teeth_180(100);
        translate([side, side])rotate(180 +45/2) TileLife_FilledInternalTooth(100);
        translate([side, side])rotate(45/2) TileLife_EmptyInternalTooth(100);
        translate([-side, -side])rotate(-45 + 45/2) TileLife_FilledInternalTooth(100);
        translate([side, -side])rotate(45 + 45/2) TileLife_FilledInternalEdgeTooth(100);

    }
    
};

module TileSpaceFiller_Type4(r)
{
    side = r * cos(45.0 / 2);
     color([0, 0.4, 0.9]) 
    difference()
    {
        polygon([[-side, side], [-side, -side], [side, -side], [side, side]]); 


    //}
    translate([side, -side]) rotate(135 + 45/2) SliceWithTooth(r)  ;
    translate([side, -side]) rotate(90 + 45/2) Slice(r)  ;
  translate([side, -side]) rotate(45 + 45/2) SliceWithTooth(r)  ;
  
    translate([-side, side]) rotate(-45 + 45/2) SliceWithTooth(r)  ;
    translate([-side, side]) rotate(-90 + 45/2) Slice(r)  ;
  translate([-side, side]) rotate(-135 + 45/2) SliceWithTooth(r);  
  translate([side, side]) rotate(180 + 45/2)LiveSliceWithTooth(r) ;
  translate([side, side]) rotate(135 + 45/2)LiveSlice(r) ;
  translate([side, side]) rotate(-135 + 45/2)LiveSlice(r) ;
  
    translate([-side, -side]) rotate(45/2)LiveSliceWithTooth(r) ;
  translate([-side,-side]) rotate(45 + 45/2)LiveSlice(r) ;
  translate([-side, -side]) rotate(-45 + 45/2)LiveSlice(r) ;
    }
};



r = 100;
side = r * cos(45.0 / 2);
d = 2.5 * r;

translate([- d,  0])EmptyTile4_2teeth_180(r);
translate([0,  0])EmptyTile4_1teeth_90(r);
translate([d,  0])EmptyTile4_2teeth_90(r);

/*

color([0, 0.4, 0.9]) linear_extrude(15)
linear_extrude(15)translate([- d, 2 * d])TileSpaceFiller_Type3(100);
linear_extrude(15)translate([0, 2 * d])TileSpaceFiller_Type1(100);
linear_extrude(15)translate([d, 2 * d])TileSpaceFiller_Type2(100);

color([0, 0.4, 0.9]) linear_extrude(15)translate([- 2 * d,  d])TileLife_InternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([-  d,  d])TileLife_EmptyInternalEdgeTooth(100);
color([0, 0.4, 0.9])linear_extrude(15)translate([0,  d])TileLife_EmptyInternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([d,  d])TileLife_FilledInternalTooth(100);
color([0, 0.4, 0.9]) linear_extrude(15)translate([- 2 * d,  0])TileLife_FilledInternalEdgeTooth(100);

linear_extrude(15)translate([- 2 * d,  -d])SquareTile(100);
linear_extrude(15)translate([- d,  -d])EmptyTile0_NoTeeth(100);
linear_extrude(15)translate([0,  -d])EmptyTile0_InternalTeeth(100);
linear_extrude(15)translate([d,  -d])EmptyTile0_EdgeTeeth(100);
*/

User avatar
pcallahan
Posts: 845
Joined: April 26th, 2013, 1:04 pm

Re: Realizing still life constraints as a planar tiling

Post by pcallahan » May 18th, 2016, 7:05 pm

Taking this in the other direction, I thought it would be useful to see calcyman's tile set with most of the magic taken away. I think this is accurate
Screen Shot 2016-05-18 at 3.47.50 PM.png
Screen Shot 2016-05-18 at 3.47.50 PM.png (80.77 KiB) Viewed 18768 times
Assigning letters arbitrarily based on mnemonics: L is "lonely", C is "crowded", s/S is "survives." (but S is the half of a 3-cell neighborhood with 2 cells).

0 is the boundary between two 0 (dead) cells and 1 is the boundary between two 1 (live) cells. n/N is "neighbor", the boundary between a 0 and 1. n is the 0 side and N is the 1 side.

The only thing that does not correspond to a simple 2D geometric constraint is having s-s and s-S but not S-S matching. That works by adding an inner tile, but there is a simpler "cheat" for 3D boundaries. Just have a central bump on the interior of the boundary for S and a matching hole for s (in addition to any other complementary surfaces). Then two S tiles conflict and two s tiles hide the inner space (or you could pretend there is a another small tile that fills that space).

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 18th, 2016, 8:35 pm

I was thinking about this idea as well. Because we're kinda overusing 3d connections, why do we even bother with the 2d complexity? we could simply have different 3d "key" for each valid connection.

I think my main problem with this is that 3d tiling is less interesting at least in our case - we like to see the objects in 2d and how they attach, especially where we use 2d tiling to correspond to CGOL. I was thinking of 3d tiling just as a way to make the tiles stick together.

I'm also thinking about the experience of attaching the tiles into a puzzle - 3d tiling is very tedious, figuring out which key is proper to what attachment.

Anyway I've finished to model the 3d construction with the teeth, and my tiling design that uses a gray tile, and improves the interlocking of high teeth count tiles.

The code is written in OpenJScad which has online interface (OpenSCAD has failed me with 3d modeling, it's too buggy). Just paste the code and press Shift+Enter.

Code: Select all

//!OpenSCAD

//main parameters
emptyToothWidth = 0.4;
emptyToothLength = 0.12;
filledToothWidth = 0.4;

//3d structural teeth 
cubeSizeW = 0.17;
cubeAngle = 30;
toothLocation = 0.07;
//Adding 3d tooths flag
add3dStructure = true; 

//Height 
H = 1;
r = 10;
d = r * cos(45 / 2);
side = 2 * r * sin(45 / 2);
eps = 0.001; 

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 = true, r1 = r) 
{
   if(use3D && add3dStructure)
   {
       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 
   {
    linear_extrude(height = H) rotate([0,0,-45/2])polygon([[0,0],[r, 0],[r * cos(360/8), r* sin(360/8)]] );
   }
};


module SliceWithTooth() 
{
    
    union()
    {
        Slice(false);
         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]]);
               if(add3dStructure)
               {
                translate([side / 2, - side * toothLocation, H / 2]) Tooth();
                translate([side / 2, side * toothLocation, H / 2]) rotate([180, 0, 0]) Tooth();
               }
           }   
           
           if(add3dStructure)
           {
            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 EmptyTile0_NoTeeth()
{
    difference()
    {
        union()
        {
            Slice();
            rotate([0,0,45]) Slice();
            rotate([0,0,90]) Slice();
            rotate([0,0,135]) Slice();
        }
        
        SquareTile();
    }
};

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


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

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

module LiveSliceWithTooth() 
{
    size = r/3;
    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 HelperLife_InternalTooth()
{
   rotate([0,0,45])
   union()
    {
        LiveSliceWithTooth();
        rotate([0,0,45]) LiveSliceWithTooth();
        rotate([0,0,-45]) LiveSliceWithTooth();
    }
};


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 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() ;
    
    }
  
};
//Tiles with complex negatives
module TileLife_EmptyInternalEdgeTooth()
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            LiveSliceWithTooth();
            rotate([0,0,45]) LiveSlice();
            rotate([0,0,90]) LiveSlice();
            rotate([0,0,135]) LiveSlice();
        }

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


module TileLife_EmptyInternalTooth()
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            rotate([0,0,45]) LiveSliceWithTooth();
            LiveSlice();
            rotate([0,0,90]) LiveSlice();
            rotate([0,0,135]) LiveSlice();
        }
        //rotate([0,0,45]) TileLife_InternalTooth();
        rotate([0,0,45]) TileSpaceFiller_Type4();
    }
};

module TileLife_FilledInternalEdgeTooth()
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth();
            rotate([0,0,45]) LiveSlice();
            rotate([0,0,90]) LiveSliceWithTooth();
            rotate([0,0,135]) LiveSlice();
        }
        //rotate([0,0,45]) TileSpaceFiller_Type4();
        rotate([0,0,45]) TileSpaceFiller_Type4();
    }
};

module TileLife_FilledInternalTooth()
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth();
            rotate([0,0,45]) LiveSliceWithTooth();
            rotate([0,0,90]) LiveSlice();
            rotate([0,0,135]) LiveSlice();
        }
        //rotate([0,0,45]) TileLife_InternalTooth();
        rotate([0,0,45]) TileSpaceFiller_Type4();
    }
};


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()
{
    union()
    {
        Slice();
        rotate([0,0,45]) Slice();
        rotate([0,0,90]) EmptyTile4_2teeth_90();
        TileLife_InternalTooth();
    }

};



//Tests
//Slice();
//SliceWithTooth();
//LiveSlice();
//LiveSliceWithTooth();
//HelperLife_InternalTooth();


//Tiles
//TileSpaceFiller_Type1();
//TileSpaceFiller_Type2();
//TileSpaceFiller_Type3();
//TileLife_FilledInternalEdgeTooth();
//TileLife_FilledInternalTooth();
 //TileLife_EmptyInternalEdgeTooth();
//TileLife_EmptyInternalEdgeTooth();
//TileSpaceFiller_Type4();
//TileLife_InternalTooth();
//EmptyTile4_2teeth_90();
//EmptyTile4_1teeth_90();
//EmptyTile4_2teeth_180();
//SquareTile();
//EmptyTile0_NoTeeth();
//EmptyTile0_EdgeTeeth();
//EmptyTile0_InternalTeeth();

//Slice();

d1 = 2.2 * 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.7, 0.7, 0.7])translate([- 2 * d1,  d1])TileLife_InternalTooth();
translate([-  d1,  d1])TileLife_EmptyInternalEdgeTooth();
translate([0,  d1])TileLife_EmptyInternalTooth();


translate([d1,  d1])TileLife_FilledInternalEdgeTooth();
translate([- 2 * d1,  0])TileLife_FilledInternalTooth();

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])SquareTile();
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();
Here is an example of a square:
2.png
2.png (35.13 KiB) Viewed 18761 times

User avatar
pcallahan
Posts: 845
Joined: April 26th, 2013, 1:04 pm

Re: Realizing still life constraints as a planar tiling

Post by pcallahan » May 18th, 2016, 11:24 pm

By the way, I noticed that in your most recent tile set, the blue and whitish teeth are the same width. There is a problem with that, which I mentioned before. You need to make sure that the blue/blue boundary is different from the blue/white boundary or else you can place tilings that aren't still lifes. In this case, for example, you should not be able to put a 2-tooth blue tile where the red 1-tooth tile is placed. But if the teeth have identical outlines, then this cannot be enforced by corner tiles.
Screen Shot 2016-05-18 at 8.17.41 PM.png
Screen Shot 2016-05-18 at 8.17.41 PM.png (21.84 KiB) Viewed 18752 times
As for the 3D boundaries, I think they are preferable to the internal tiles, which just look like a hack to me. So I would consider them for fitting together half and quarter neighborhoods. But I like the live/live, live/dead teeth, because they provide a visual representation of Conway's rules and enforce still life constraints using straightforward 2D geometry.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 19th, 2016, 5:16 am

pcallahan wrote:As for the 3D boundaries, I think they are preferable to the internal tiles, which just look like a hack to me. So I would consider them for fitting together half and quarter neighborhoods.
Well I do agree it's a hack. On the other hand if you think of it as a puzzle, having 16 pieces is already a lot. So adding more tiles is problematic. Also looking at each edge of pretty similar tiles, and figuring out its key, is also a disaster.

If you want to add some more complex "sticking out" shapes which would be more visible, 3d printing becomes an issue. Because it's limited by gradient on Z, and we need 180 degree symmetry i.e. what you have on the bottom you'll need to have on the top.

Another problem with 3d tiling is adding the last piece. because they all interlocked in 3d, it might be tricky to add the last piece. Keeping them small as a support only, makes sure this would be secondary issue, but if you want them to be the main locking mechanism it's pretty material dependent, and very not trivial in the small details.

All in all I would prefer somewhat more elegant internal 2d sticking mechanism with cost of extra tiles, over 3d attachment.
pcallahan wrote:I noticed that in your most recent tile set, the blue and whitish teeth are the same width. There is a problem with that, which I mentioned before.
Yes good point, I missed that.

I was trying to make the tooth difference larger, and here are my params:

Code: Select all

emptyToothWidth = 0.4;
emptyToothLength = 0.1;
filledToothWidth = 0.58;

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 19th, 2016, 6:41 am

Anyway I have published on thingverse the tiles in single file:

http://www.thingiverse.com/thing:1574333

@pcallahan & calcyman - I've published with creative license, tell me if you want more restrictive license. Eventually there's very slight chance this puzzle will enter the mainstream game for kids.

The puzzle should be easily 3d printable. I now want to build a BOM (build of material) report script for SL to know how many of each piece is needed for some particular SL (I find the 4 living neighbors particularly complex case).

User avatar
pcallahan
Posts: 845
Joined: April 26th, 2013, 1:04 pm

Re: Realizing still life constraints as a planar tiling

Post by pcallahan » May 19th, 2016, 9:49 am

simsim314 wrote:Also looking at each edge of pretty similar tiles, and figuring out its key, is also a disaster.
If I were doing the tiling, I would consider the visual cue to be the fact that you can't have 3 teeth around an empty cell or 4 teeth around a live cell. The edge constraints would just be there to stop me from making a mistake. However, I agree that this might not be obvious to someone who is fitting the tiles together without thinking of Life rules.

If the only 3D rules were internal to neighborhood tiles, you would also not have a major problem adding the last piece (at least I don't think so). You just put the neighborhood together first and then place it (not as easy when fitting together quarter neighborhoods for overcrowding though).

But I agree that 3D boundaries are not the greatest solution. I wonder if there is some much better tile set lurking in here (I don't see it though). Calcyman's split of the 3-neighborhoods into 1.5 tooth halves was much more elegant than the interior tile splits (of course, you get non-locking boundaries).
simsim314 wrote:Anyway I have published on thingverse the tiles in single file
Some thoughts:
  • I don't have a problem with it, but I think people who read the one line description are unlikely to understand what the tiles do and probably won't read this thread. Would it help to add "enforce constraints such that any tiling corresponds to a still life in Conway's Game of Life" or something like that?
  • An example would help (such as the eater, which is small, recognizable, and asymmetric, but unfortunately does not include the block neighborhood, so maybe two examples would be better, including a block).
  • Is there any way to illustrate the tiles in two colors (not sure about the details of printing them)?
  • Are you absolutely sure these fit together? I have a feeling that unless the grooves are slightly larger than the complex protrusions that they won't really fit (maybe less of a problem with plastic than metal)
EDIT 1: I saw your updates to thingiverse. Thanks! It would be cool to see someone print this and piece together a simple example. Of course, it would be even better if someone would mail a set to me.

Shameless plug: A Java applet I wrote about 20 years ago is still my favorite way to construct still lifes. http://www.radicaleye.com/lifepage/stilledit.html
Browsers (Chrome anyway) have mostly made it hard to run applets in recent years, but it can still be run using:
appletviewer http://www.radicaleye.com/lifepage/stilledit.html

It provides a way to draw a still life by hand, indicating constraint violations. It also has a way to find still lifes using something like a hill-climbing method with randomization (not true simulated annealing but similar). You can fix the value of some cells to fill in unknown cells. Of course, you can do all that with lifesrc and other tools, but this has a simple UI.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 19th, 2016, 1:11 pm

pcallahan wrote:Calcyman's split of the 3-neighborhoods into 1.5 tooth halves was much more elegant than the interior tile splits (of course, you get non-locking boundaries).
I can add calcyman's tiling as well if they're of interest (the modification is quiet minor). There is also a point of making STL for 3d printing per piece. But I'm a bit lazy to add 16 stl files.

I do agree calcyman tiles are more aesthetic - we can add 3d groove to the internal sides of the empty with 4+ tooth.

There is also an option to cancel the 3d structure completely.
pcallahan wrote: Would it help to add "enforce constraints such that any tiling corresponds to a still life in Conway's Game of Life" or something like that?
Added.
pcallahan wrote:An example would help
Added pictures from this thread. Doesn't exactly correspond the posted tiles - but I think people will get it. They could come here and ask as well.
pcallahan wrote:Is there any way to illustrate the tiles in two colors (not sure about the details of printing them)?
Not in the main window. But I have colors in the jscad file. People who work in jscad will see the colors pretty quickly. The jscad code is also written in a readable way.
pcallahan wrote:Are you absolutely sure these fit together? I have a feeling that unless the grooves are slightly larger than the complex protrusions that they won't really fit (maybe less of a problem with plastic than metal)
There is a known 3d printing trick to resize internal tiles by 0.99 to make them fit. I'll need to play with 3d printer to see how they all fit.

I don't think anyone soon will 3d print those tiles in metal :)

-----

If someone has experience with 3d printing and want to try those tiles, please share your experience and photos.

EDIT Couldn't make the java code run in any online compiler... and you linked twice to the same page with your app, so I'm not sure where is this appletviewer located, or how to run this java code. Looks interesting though.
Last edited by simsim314 on May 19th, 2016, 1:36 pm, edited 1 time in total.

User avatar
pcallahan
Posts: 845
Joined: April 26th, 2013, 1:04 pm

Re: Realizing still life constraints as a planar tiling

Post by pcallahan » May 19th, 2016, 1:17 pm

When I try to open it in OpenJSCAD I get this error:
"Error in line 28: Uncaught SyntaxError: Unexpected identifier"

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 19th, 2016, 2:01 pm

pcallahan wrote:When I try to open it in OpenJSCAD I get this error:
You should not drug and drop the jscad to the dropbox, instead you need to copy/paste the code into the code window on the top right - (it has transparent interface, a bit confusing). Then just press shift+enter.

User avatar
pcallahan
Posts: 845
Joined: April 26th, 2013, 1:04 pm

Re: Realizing still life constraints as a planar tiling

Post by pcallahan » May 19th, 2016, 2:14 pm

Thanks! I got OpenJSCAD to work, but I would not have thought of copy-pasting the code like that.

Hmm... appletviewer is an executable that should come with any Java JDK, but it might not be in your path. It's a shame that applets never caught on and are now effectively deprecated for security reasons. Stilledit and my Life generator applet used to run just fine in any browser (till less than 10 years ago). I am not sure of the easiest way to get around this. Chrome is pretty dead set against running any applets and Safari tells me it violates security settings.

UPDATE: I can get it to run in Firefox and Safari, but only after adding http://www.radicaleye.com as an exception on the Java security tab of the Java control panel (under settings on a Mac). radicaleye is Tom Rokicki's site and should be safe, but it's up to you if you want to go through the effort. Some day I should rewrite the still life editor in a form supported by modern browsers. I don't think there is anything that does the same thing (but there are many Life generators out there).

Screenshot of security change:
Screen Shot 2016-05-19 at 11.32.31 AM.png
Screen Shot 2016-05-19 at 11.32.31 AM.png (98.53 KiB) Viewed 18698 times
Screenshot of the applet. The cyan region is around a fixed neighborhood of 4 around an empty cell. Most of the still life was randomly generated by pressing "stabilize" and the colorful area is a section that needs some changes to be stable. E.g. green square indicates a cell that will improve stability if toggled.
Screen Shot 2016-05-19 at 11.35.34 AM.png
Screen Shot 2016-05-19 at 11.35.34 AM.png (103.11 KiB) Viewed 18698 times

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 19th, 2016, 3:03 pm

Woops I had a problem with my new sets. The [1,1,0,0] empty tile should be possible to add to itself. So it should use negative operation like the rest of the empty >= 4 tiles.

I've updated the Thignverse already, and here is the jscad code:

Code: Select all

//!OpenSCAD

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

//3d structural teeth 
cubeSizeW = 0.17;
cubeAngle = 30;
toothLocation = 0.07;
//Adding 3d tooths flag
add3dStructure = true; 

//Height 
H = 1;
r = 10;
d = r * cos(45 / 2);
side = 2 * r * sin(45 / 2);
eps = 0.001; 

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 = true, r1 = r) 
{
   if(use3D && add3dStructure)
   {
       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 
   {
    linear_extrude(height = H) rotate([0,0,-45/2])polygon([[0,0],[r, 0],[r * cos(360/8), r* sin(360/8)]] );
   }
};


module SliceWithTooth() 
{
    
    union()
    {
        Slice(false);
         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]]);
               if(add3dStructure)
               {
                translate([side / 2, - side * toothLocation, H / 2]) Tooth();
                translate([side / 2, side * toothLocation, H / 2]) rotate([180, 0, 0]) Tooth();
               }
           }   
           
           if(add3dStructure)
           {
            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 EmptyTile0_NoTeeth()
{
    difference()
    {
        union()
        {
            Slice();
            rotate([0,0,45]) Slice();
            rotate([0,0,90]) Slice();
            rotate([0,0,135]) Slice();
        }
        
        SquareTile();
    }
};

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


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

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

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 HelperLife_InternalTooth()
{
   rotate([0,0,45])
   union()
    {
        LiveSliceWithTooth();
        rotate([0,0,45]) LiveSliceWithTooth();
        rotate([0,0,-45]) LiveSliceWithTooth();
    }
};


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 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() ;
    
    }
  
};
//Tiles with complex negatives
module TileLife_EmptyInternalEdgeTooth()
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            LiveSliceWithTooth();
            rotate([0,0,45]) LiveSlice();
            rotate([0,0,90]) LiveSlice();
            rotate([0,0,135]) LiveSlice();
        }

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


module TileLife_EmptyInternalTooth()
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            rotate([0,0,45]) LiveSliceWithTooth();
            LiveSlice();
            rotate([0,0,90]) LiveSlice();
            rotate([0,0,135]) LiveSlice();
        }
        //rotate([0,0,45]) TileLife_InternalTooth();
        rotate([0,0,45]) TileSpaceFiller_Type4();
    }
};

module TileLife_FilledInternalEdgeTooth()
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth();
            rotate([0,0,45]) LiveSlice();
            rotate([0,0,90]) LiveSliceWithTooth();
            rotate([0,0,135]) LiveSlice();
        }
        //rotate([0,0,45]) TileSpaceFiller_Type4();
        rotate([0,0,45]) TileSpaceFiller_Type4();
    }
};

module TileLife_FilledInternalTooth()
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth();
            rotate([0,0,45]) LiveSliceWithTooth();
            rotate([0,0,90]) LiveSlice();
            rotate([0,0,135]) LiveSlice();
        }
        //rotate([0,0,45]) TileLife_InternalTooth();
        rotate([0,0,45]) TileSpaceFiller_Type4();
    }
};


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)
{
    if(type == 0)
        translate([2 * d * x, 2 * d * y]) scale(0.97) rotate([0,0,-45 - 45 * rot])TileLife_EmptyInternalEdgeTooth();

};

//PlaceFilled(0,0,0,0);



//Tests
//Slice();
//SliceWithTooth();
//LiveSlice();
//LiveSliceWithTooth();
//HelperLife_InternalTooth();


//Tiles
//TileSpaceFiller_Type1();
//TileSpaceFiller_Type2();
//TileSpaceFiller_Type3();
//TileLife_FilledInternalEdgeTooth();
//TileLife_FilledInternalTooth();
//TileLife_EmptyInternalEdgeTooth();
//TileLife_EmptyInternalEdgeTooth();
//TileSpaceFiller_Type4();
//TileLife_InternalTooth();
//EmptyTile4_2teeth_90();
//EmptyTile4_1teeth_90();
//EmptyTile4_2teeth_180();
//SquareTile();
//EmptyTile0_NoTeeth();
//EmptyTile0_EdgeTeeth();
//EmptyTile0_InternalTeeth();

//Slice();





d1 = 2.2 * 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.7, 0.7, 0.7])translate([- 2 * d1,  d1])TileLife_InternalTooth();
translate([-  d1,  d1])TileLife_EmptyInternalEdgeTooth();
translate([0,  d1])TileLife_EmptyInternalTooth();
translate([d1,  d1])TileLife_FilledInternalEdgeTooth();

translate([- 2 * d1,  0])TileLife_FilledInternalTooth();
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])SquareTile();
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();
EDIT I've built a script that calculates BOM (it actually calculates a valid tiling).

To read the report you need to look at the images I posted with tiles, and the index order is as follows:

0, 1, 2, 3
4, 5, 6, 7
8, 9, 10, 11
12, 13, 14, 15

Each index represent tile, and each tile has a count. The script only shows the amount of each type in 16 number array. Here is the script:

Code: Select all

import golly as g 

def FindTiles(list, validTiles, tilesUseLimit, result):
	
	if len(list) == 0:
		return True
		
	for j in xrange(len(validTiles)):
		v = validTiles[j]
		
		if tilesUseLimit[j] < 1 or len(v) > len(list):
			continue 
			
			
		fail = False
		
		for i in xrange(len(v)):
			if v[i] != list[i]:
				fail = True
				break 
				
		if not fail:
		
			newlist = []
			
			for i in xrange(len(v), len(list)):
				newlist.append(list[i])
			
			result.append(j)
			tilesUseLimit[j] -= 1
			
			if j > 0:
				tilesUseLimit[j - 1] -= 1
			if j + 1 < len(tilesUseLimit):
				tilesUseLimit[j + 1] -= 1
			
			if not FindTiles(newlist, validTiles, tilesUseLimit, result):
				result.pop()
				tilesUseLimit[j] += 1
				
				if j > 0:
					tilesUseLimit[j - 1] += 1
				if j + 1 < len(tilesUseLimit):
					tilesUseLimit[j + 1] += 1
			
			else:
				return True
				
	return False
	
def FindTilesCircular(list, tiles, tilesUseLimit):
	
	for i in xrange(8):
		result = [] 
		newlist = []
		
		for j in xrange(len(list)):
			newlist.append(list[(i + j) % len(list)])
		
		
		if FindTiles(newlist, tiles, tilesUseLimit, result):
			return result
			
			
def UpdateBom(x, y, bom, addCentral):
	neighbohrs = [(0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1)]
	list = []
	total = 0 
	
	for i, j in neighbohrs:
		list.append(g.getcell(x + i, y + j))
		if g.getcell(x + i, y + j) == 1:
			total += 1
	
	if g.getcell(x, y) == 1:
		
		if total == 2:
			bom[0] += 1
		
		tiles = [[0,0,0,1], [1,0,0,0], [0,0,1,0], [0,1,0,0], [0,1,0,1], [1,0,1,0], [1,1,0,0], [0,0,1,1]]
		tilesUseLimit = [100, 100, 100, 100, 100, 100, 100, 100]
		tilesIdx = [5, 5, 6, 6, 7, 7, 8, 8]
		
		res = FindTilesCircular(list, tiles, tilesUseLimit)
			
		for r in res:
			bom[tilesIdx[r]] += 1
			
	#Dead cell
	else:
		
		if total <= 2:
			bom[12] += 1
			
			tiles = [[0,0,0,0], [1,0,0,0], [0,0,0,1], [0,0,1,0], [0,1,0,0]]
			tilesUseLimit = [100, 100, 100, 100, 100]
			tilesIdx = [13, 14, 14, 15, 15]
			
		# total >= 4
		else:
			bom[4] += 1
			
			tiles = [[0,0,1,1], [1,1,0,0], [0,1], [1,0], [1, 1]]
			tilesUseLimit = [100, 100, 100, 100, 100]
			tilesIdx = [9, 9, 10, 10, 11]
		
		res = FindTilesCircular(list, tiles, tilesUseLimit)
			
		for r in res:
			bom[tilesIdx[r]] += 1
	
	if addCentral:
		totalConnectors = 0 
		
		for i in xrange(4):
			if list[i] > 0:
				totalConnectors+=1
				
		if totalConnectors == 0:
			bom[12] += 1
		if totalConnectors == 1:
			bom[1] += 1
		if totalConnectors == 2:
			
			if list[0] == list[2]:
				bom[0] += 1
			else:
				bom[3] += 1
				
		if totalConnectors == 3:
			bom[2] += 1
		if totalConnectors == 4:
			bom[4] += 1
		
bom = [0 for i in xrange(16)]

rect = g.getrect()

for i in xrange(rect[0], rect[0] + rect[2] + 1):
	for j in xrange(rect[1], rect[1] + rect[3] + 1):
		UpdateBom(i,j,bom, i != rect[0] and j != rect[1])

g.show(str(bom))

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Realizing still life constraints as a planar tiling

Post by simsim314 » May 19th, 2016, 9:32 pm

I've finished a script that converts SL in golly to tile code in OpenJSCAD, and also reports the BOM of the tiles.

To use the TileGenerator.py script:

1. Copy it to some directory under golly python.
2. Make sure you have SL only in golly.
3. Run the script. You get the report of each tile quantity, total, and script to generate the tiles.
4. In the same folder of the script, find tiles.txt file, this is jscad script.
5. Add it content to the end of posted below jscad script.
6. Run jscad (shift+enter)
7. Have your SL in tiles.

Download is available from Thingverse as well:

TileGenerator.py

Code: Select all

import golly as g 

def FindTiles(list, validTiles, tilesUseLimit, result):
   
   if len(list) == 0:
      return True
      
   for j in xrange(len(validTiles)):
      v = validTiles[j]
      
      if tilesUseLimit[j] < 1 or len(v) > len(list):
         continue 
         
         
      fail = False
      
      for i in xrange(len(v)):
         if v[i] != list[i]:
            fail = True
            break 
            
      if not fail:
      
         newlist = []
         
         for i in xrange(len(v), len(list)):
            newlist.append(list[i])
         
         result.append(j)
         tilesUseLimit[j] -= 1
         
         if j > 0:
            tilesUseLimit[j - 1] -= 1
         if j + 1 < len(tilesUseLimit):
            tilesUseLimit[j + 1] -= 1
         
         if not FindTiles(newlist, validTiles, tilesUseLimit, result):
            result.pop()
            tilesUseLimit[j] += 1
            
            if j > 0:
               tilesUseLimit[j - 1] += 1
            if j + 1 < len(tilesUseLimit):
               tilesUseLimit[j + 1] += 1
         
         else:
            return True
            
   return False
   
def FindTilesCircular(list, tiles, tilesUseLimit):
   
   for i in xrange(8):
      result = [] 
      newlist = []
      
      for j in xrange(len(list)):
         newlist.append(list[(i + j) % len(list)])
      
      
      if FindTiles(newlist, tiles, tilesUseLimit, result):
         return (i, result)
         
         
def UpdateBom(x, y, bom, addCentral, filled, con, empty0, empty4, excon):
	neighbohrs = [(0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1)]
	list = []
	total = 0 

	for i, j in neighbohrs:
		list.append(g.getcell(x + i, y + j))
		if g.getcell(x + i, y + j) == 1:
			total += 1

	if g.getcell(x, y) == 1:

		if total == 2:
			bom[0] += 1

		tiles = [[0,0,0,1], [1,0,0,0], [0,0,1,0], [0,1,0,0], [0,1,0,1], [1,0,1,0], [0,0,1,1], [1,1,0,0]]
		tilesUseLimit = [100, 100, 100, 100, 100, 100, 100, 100]
		tilesIdx = [5, 5, 6, 6, 7, 7, 8, 8]

		i, res = FindTilesCircular(list, tiles, tilesUseLimit)

		if total == 2:
			con.append([x, y, 0, i])

		di = i 	  
		for r in res:
			bom[tilesIdx[r]] += 1
			filled.append([x, y, r, di])
			di += len(tiles[r])

	#Dead cell
	else:
	  
		if total <= 2:
			bom[12] += 1

			tiles = [[0,0,0,0], [0,0,1,0],  [0,1,0,0], [0,0,0,1], [1,0,0,0]]
			tilesUseLimit = [100, 100, 100, 100, 100]
			tilesIdx = [13, 14, 14, 15, 15]

			i, res = FindTilesCircular(list, tiles, tilesUseLimit)
			di = i 	  

			for r in res:
				bom[tilesIdx[r]] += 1
				empty0.append([x, y, r, di])
				di += len(tiles[r])

			con.append([x, y, 12, i])

		# total >= 4
		else:
			bom[4] += 1

			tiles = [[1,1,0,0], [0,0,1,1], [1,0], [0,1], [1, 1]]
			tilesUseLimit = [100, 100, 100, 100, 100]
			tilesIdx = [9, 9, 10, 10, 11]

			i, res = FindTilesCircular(list, tiles, tilesUseLimit)
			di = i 	  

			for r in res:
				bom[tilesIdx[r]] += 1
				empty4.append([x, y, r, di])
				di += len(tiles[r])
			
			con.append([x, y, 4, i])
			
			
	if addCentral:
	
		totalConnectors = 0 
		firstIdx = -1 
		first0Idx = -1 
		firstCon = -1 
		neighbohrs = [(0, 0), (0, -1), (1, -1), (1, 0)]
		cnt = 0 
		
		for i, j in neighbohrs:
			if g.getcell(x + i, y + j) > 0:
				totalConnectors+=1
				
				if firstIdx == -1:
					firstIdx = cnt
					
				i1, j1 = neighbohrs[(cnt + 1) % 4]
				if g.getcell(x + i1, y + j1) > 0:
					firstCon = cnt
					
			else:
				if first0Idx == -1 :
					first0Idx = cnt 
		
			cnt += 1
			

		if totalConnectors == 0:
			bom[12] += 1
			excon.append([x, y, 12, 0])
			
		if totalConnectors == 1:
			bom[1] += 1
			excon.append([x, y, 1, firstIdx])
			
		if totalConnectors == 2:

			if list[0] == list[2]:
				bom[0] += 1
				excon.append([x, y, 0, firstIdx])
			else:
				bom[3] += 1
				excon.append([x, y, 3, firstCon])
				
		if totalConnectors == 3:
			bom[2] += 1
			excon.append([x, y, 2, first0Idx])
			
		if totalConnectors == 4:
			bom[4] += 1
			excon.append([x, y, 4, firstIdx])
			
bom = [0 for i in xrange(16)]

rect = g.getrect()
filled = [] 
con = []
empty0 = []
empty4 = []
excon = []
for i in xrange(rect[0] - 1, rect[0] + rect[2] + 1):
   for j in xrange(rect[1] - 1, rect[1] + rect[3] + 1):
      UpdateBom(i,j,bom, i != rect[0] + rect[2] and j !=  rect[1] - 1, filled, con, empty0, empty4, excon)
	  
f = open("tiles.txt",'w')

for fil in filled:
	f.write("PlaceFilled(%d,%d,%d,%d);\n" % (fil[0], -fil[1], fil[2], fil[3]))

for fil in con:
	f.write("PlaceInternalConnector(%d,%d,%d,%d);\n" % (fil[0], -fil[1], fil[2], fil[3]))

for fil in excon:
	f.write("PlaceExternalConnector(%d,%d,%d,%d);\n" % (fil[0], -fil[1], fil[2], fil[3]))

for fil in empty0:
	f.write("PlaceEmpty0(%d,%d,%d,%d);\n" % (fil[0], -fil[1], fil[2], fil[3]))

for fil in empty4:
	f.write("PlaceEmpty4(%d,%d,%d,%d);\n" % (fil[0], -fil[1], fil[2], fil[3]))

f.close() 

total = 0
for i in bom:
	total += i
	
g.show("Total = " + str(total) + " , BOM = " + str(bom))

TileGenerator.jsad

Code: Select all

//!OpenSCAD

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

//3d structural teeth 
cubeSizeW = 0.17;
cubeAngle = 30;
toothLocation = 0.07;
//Adding 3d tooths flag
add3dStructure = false; 

//Height 
H = 1;
r = 10;
d = r * cos(45 / 2);
side = 2 * r * sin(45 / 2);
eps = 0.001; 

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 = true, r1 = r) 
{
   if(use3D && add3dStructure)
   {
       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 
   {
    linear_extrude(height = H) rotate([0,0,-45/2])polygon([[0,0],[r, 0],[r * cos(360/8), r* sin(360/8)]] );
   }
};


module SliceWithTooth() 
{
    
    union()
    {
        Slice(false);
         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]]);
               if(add3dStructure)
               {
                translate([side / 2, - side * toothLocation, H / 2]) Tooth();
                translate([side / 2, side * toothLocation, H / 2]) rotate([180, 0, 0]) Tooth();
               }
           }   
           
           if(add3dStructure)
           {
            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 EmptyTile0_NoTeeth()
{
    difference()
    {
        union()
        {
            Slice();
            rotate([0,0,45]) Slice();
            rotate([0,0,90]) Slice();
            rotate([0,0,135]) Slice();
        }
        
        SquareTile();
    }
};

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


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

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

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 HelperLife_InternalTooth()
{
   rotate([0,0,45])
   union()
    {
        LiveSliceWithTooth();
        rotate([0,0,45]) LiveSliceWithTooth();
        rotate([0,0,-45]) LiveSliceWithTooth();
    }
};


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 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() ;
    
    }
  
};
//Tiles with complex negatives
module TileLife_EmptyInternalEdgeTooth()
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            LiveSliceWithTooth();
            rotate([0,0,45]) LiveSlice();
            rotate([0,0,90]) LiveSlice();
            rotate([0,0,135]) LiveSlice();
        }

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


module TileLife_EmptyInternalTooth()
{
   color([0, 0.4, 0.9]) 
   difference()
   {
        union()
        {
            rotate([0,0,45]) LiveSliceWithTooth();
            LiveSlice();
            rotate([0,0,90]) LiveSlice();
            rotate([0,0,135]) LiveSlice();
        }
        //rotate([0,0,45]) TileLife_InternalTooth();
        rotate([0,0,45]) TileSpaceFiller_Type4();
    }
};

module TileLife_FilledInternalEdgeTooth()
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth();
            rotate([0,0,45]) LiveSlice();
            rotate([0,0,90]) LiveSliceWithTooth();
            rotate([0,0,135]) LiveSlice();
        }
        //rotate([0,0,45]) TileSpaceFiller_Type4();
        rotate([0,0,45]) TileSpaceFiller_Type4();
    }
};

module TileLife_FilledInternalTooth()
{
   color([0, 0.4, 0.9]) 
   union()
   {
        union()
        {
            LiveSliceWithTooth();
            rotate([0,0,45]) LiveSliceWithTooth();
            rotate([0,0,90]) LiveSlice();
            rotate([0,0,135]) LiveSlice();
        }
        //rotate([0,0,45]) TileLife_InternalTooth();
        rotate([0,0,45]) TileSpaceFiller_Type4();
    }
};


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 =- 0.1  * sin(-45 - 45 * rot);
    dy =- 0.1  * cos(-45 - 45 * rot);
    scalefactor1 = 0.99;
    scalefactor2 = 0.98;
    
    if(type == 0)
        translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,-45 - 45 * rot])TileLife_EmptyInternalEdgeTooth();
    if(type == 1)
        translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(scalefactor1) rotate([0,180, -90 - 45 * rot])TileLife_EmptyInternalEdgeTooth();
    if(type == 2)
        translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor1) rotate([0,0,-45 - 45 * rot])TileLife_EmptyInternalTooth();
    if(type == 3)
        translate([2 * d * x + dx, 2 * d * y + dy, H]) scale(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.1  * sin(-45 - 45 * rot);
    dy =- 0.1  * cos(-45 - 45 * rot);
    scalefactor1 = 0.97;
    scalefactor2 = 0.94;
    
    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(scalefactor2) rotate([0,0,45 - 45 * rot])SquareTile();
    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])TileLife_InternalTooth();
    
}

module PlaceExternalConnector(x, y, type, rot)
{
    dx =- 0.1  * sin(-45 - 45 * rot);
    dy =- 0.1  * cos(-45 - 45 * rot);
    scalefactor1 = 0.97;
    scalefactor2 = 0.94;
    
    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(scalefactor2) 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(scalefactor2) rotate([0,0,-45/2 ])TileLife_InternalTooth();
    
}

module PlaceEmpty0(x, y, type, rot)
{
    dx =- 0.1  * sin(-45 - 45 * rot);
    dy =- 0.1  * cos(-45 - 45 * rot);
    scalefactor1 = 0.98;
    scalefactor2 = 0.98;
    
    if(type == 0)
        color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor1) 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(scalefactor1) 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(scalefactor1) 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 =- 0.1  * sin(-45 - 45 * rot);
    dy =- 0.1  * cos(-45 - 45 * rot);
    scalefactor1 = 0.98;
    scalefactor2 = 0.98;
    
    if(type == 0)
        color([0.8, 0.8, 0.2]) translate([2 * d * x + dx, 2 * d * y + dy]) scale(scalefactor1) 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(scalefactor1) 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(scalefactor1) rotate([0,0,45 - 45 * rot])EmptyTile4_2teeth_90();
}
Example of result:
Sample1.png
Sample1.png (179.85 KiB) Viewed 18673 times
EDIT I cheated a bit, I used two different colors for the "grey" symmetrical piece. The grey was somewhat ugly, but having the same piece in two colors I think is totally valid, and should not be considered two tiles.

EDIT2 I managed to see the Still life applet (your instructions helped to enable it in IE10). I think the most missing feature is copy rle or any format that can be used outside of it. As the applet itself I enjoyed it very much, especially the stabilize option.
Last edited by simsim314 on May 19th, 2016, 9:51 pm, edited 1 time in total.

Post Reply