Feature Request: 3D Honeycomb infill #1646

Closed
gringer opened this Issue Dec 28, 2013 · 35 comments

Projects

None yet
@gringer
gringer commented Dec 28, 2013

The current infill patterns (apart from concentric) seem to be 2D in nature, creating vertical walls. It would be nice to have different patterns where the fill pattern for each layer is dependent on the Z direction / height, allowing for stronger support structures with less infill.

I have an example of one such pattern here, which uses a tesselation of a truncated octahedron:

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

There are other examples of space-filling tesselations:

http://en.wikipedia.org/wiki/Honeycomb_%28geometry%29#Space-filling_polyhedra.5B2.5D

@gringer
gringer commented Dec 30, 2013

I've added an octagon/tetrahedron tesselation to the thingiverse page as well. Here's my rough description of the tesselations, sort of as a 3D printer would view them going from bottom to top (as discovered by investigating my models in Repetier Host with the 'Cut Objects' function):

Tetrahedron/Octahedron: Start from a regular square grid, form square on one diagonal and expand it from the corner, pushing the "old" square smaller to eventually return to a regular square grid.

Truncated tetrahedron: Start from a regular square grid and expand from centre (truncating diagonals to form diamonds) until a regular octahedron is formed, then reduce from centre to return to the square grid and continue reducing to form an octahedron in the other direction, then expand back to the square grid.

Although the truncated tetrahedron is a more geometrically complex structure, it would probably work better on a 3D printer because it can be done with relative filament travel angles that are no steeper than 45 degrees (excluding edges). The octahedron structure requires sharper 90 degree turns for printing.

In order to form the pattern for the truncated tetrahedron you would travel vertically doing zigzags as necessary, then turn 180 degrees at the end to do the next square over in the horizontal direction. Once reaching the end of the pattern in the horizontal direction, you rotate 90 degrees and proceed in a similar way in the vertical direction. This will create walls that are only one filament width thick, so it might be better to go forward and back along a path before shifting over to create a 2-filament thick wall.

Edit: an image to demonstrate this is attached. The grids are twice magnified in the non-square grid views because I got tired of making mistakes. The rectangles should be squares, but I had trouble getting that working with the grid I was using for laying out the points.
troct_pattern

@gringer
gringer commented Jan 9, 2014

I've now written an R script to generate path points at a given Z height, assuming a grid size of 1x1 unit. For the purpose of making the lines easier to see, I've coloured the horizontal-ish lines blue and vertical-ish lines red. Most of the script complexity is involved in trying to deal with the edges of the pattern. An animated GIF generated from this is attached:

#!/usr/bin/Rscript

for(zHeight in (0:39 / (10*sqrt(2)))){
    png(sprintf("Rplot_z%0.3f.png",zHeight));
    octagramGap = 1 / (1 + sqrt(2)); # offset required to create a regular octagram
     # sawtooth wave function, assuming edge length 1, height 4/sqrt(2)
    gap = (abs(((zHeight * sqrt(2)) %% 4) - 2) - 1) * octagramGap;
    gridHeight = 9; # number of cells in Y direction
    gridWidth = 5; # number of cells in X direction
    plot(-2.5:7.5, -0.5:9.5, type = "n", xlab = paste("Grid width:",gridWidth),
         ylab = paste("Grid height:",gridHeight),
         main = sprintf("Truncated Octagram Space Filling Tesselation\n[Height = %0.3f]",zHeight));
    gapSepLine = c(-abs(gap)/2, abs(gap)/2); # offset along the direction of travel
    gapSepOffset = c(-gap/2, gap/2); # offset perpend to the direction of travel
    columnPoints = cbind(rep(0,gridHeight+1),0:gridHeight);
    for (xOffset in 1:(gridWidth-1)){
        yPointBase = c(rep(1:gridHeight, each = 2));
        yPoints = c(0,abs(gap)/2,head(yPointBase + gapSepLine,-1),gridHeight);
        xPoints = c(0,head(rep(gapSepOffset, gridHeight, each = 2),
            gridHeight*2),0) * (-1)^xOffset + xOffset;
        pointsToAdd = cbind(xPoints, yPoints);
        if(xOffset %% 2 == 1){
            pointsToAdd = cbind(rev(xPoints),rev(yPoints));
        }
        columnPoints = rbind(columnPoints, pointsToAdd);
    }
    if(gridWidth %% 2 == 0){
        columnPoints = rbind(columnPoints,
            cbind(rep(gridWidth,gridHeight+1),0:gridHeight));
    } else {
        columnPoints = rbind(columnPoints,
            cbind(rep(gridWidth,gridHeight+1),rev(0:gridHeight)));
    }
    points(columnPoints, type = "l", col = "red", lwd = 4);
    rowPoints = cbind(0:gridWidth, rep(0,gridWidth+1));
    for(yOffset in 1:(gridHeight-1)){
        xPointBase = c(rep(1:gridWidth, each = 2));
        xPoints = c(0,abs(gap)/2,head(xPointBase + gapSepLine,-1),gridWidth);
        yPoints = c(0,head(rep(gapSepOffset, gridWidth, each = 2),
            gridWidth*2),0) * (-1)^(yOffset) + yOffset;
        pointsToAdd = cbind(xPoints, yPoints);
        if(yOffset %% 2 == 1){
            pointsToAdd = cbind(rev(xPoints),rev(yPoints));
        }
        rowPoints = rbind(rowPoints, pointsToAdd);
    }
    if(gridWidth %% 2 == 0){
        rowPoints = rbind(rowPoints,
            cbind(0:gridWidth,rep(gridHeight,gridWidth+1)));
    } else {
        rowPoints = rbind(rowPoints,
            cbind(rev(0:gridWidth),rep(gridHeight,gridWidth+1)));
    }
    points(rowPoints, type = "l", col = "blue", lwd = 2);
    ##Sys.sleep(0.2);
    graphics.off();
}

truncoct

@alexrj
Owner
alexrj commented Jan 9, 2014

Hello!

Is there any chance you can provide that implemented in C or C++ or Perl? It should just be a function that takes a Z height and returns a set of polylines (or closed polygons, as you like) for that height.

Thank you for contributing :)

@alexrj
Owner
alexrj commented Jan 9, 2014

BTW, your animations show that two lines overlap at every diagonal edge. That's not ideal for 3D printing because it means that several jumps are needed, and extrusion must stop and start lots of times. If you look closely at how honeycomb infill is implemented, you'll notice that those lines are duplicated and slightly moved so that two adjacent diagonal traces can be extruded continuously. I recommend you take the same approach.

@hroncok
Contributor
hroncok commented Jan 9, 2014

This looks great 👍

@gringer
gringer commented Jan 9, 2014

Yes, I can convert this to Perl. I'll have a go today.

For the overlap I'd probably just do something like shift the vertical lines half a width down, and the horizontal lines half a width up. There will still be dots of overlap at the grid intersections -- hopefully that won't be a problem.

@gringer
gringer commented Jan 10, 2014

Okay, here it is, as a Perl script, with some example SVG output that I used for debugging. I've shifted the vertical line points down, but not modified the horizontal points. I also made it so that the lines are not connected, because it sounded from your request that unconnected lines might be better for you:

#!/usr/bin/perl

=head1 DESCRIPTION

Creates a contiguous sequence of points at a specified height that make
up a horizontal slice of the edges of a space filling truncated
octahedron tesselation. The octahedrons are oriented so that the
square faces are in the horizontal plane with edges parallel to the X
and Y axes.

=cut

use warnings;
use strict;

use POSIX "fmod";

=head1 FUNCTIONS
=cut

=head2 colinearPoints(offset, gridLength)

Generate an array of points that are in the same direction as the
basic printing line (i.e. Y points for columns, X points for rows)

Note: a negative offset only causes a change in the perpendicular
direction

=cut

sub colinearPoints{
    my ($offset, $gridLength) = @_;
    my @points = ();
    for(my $i = 0; $i < $gridLength; $i++){
        push(@points, $i);
        push(@points, $i + abs($offset/2));
        push(@points, ($i+1) - abs($offset/2));
    }
    push(@points, $gridLength);
    return(@points);
}

=head2 colinearPoints(offset, baseLocation, gridLength)

Generate an array of points for the dimension that is perpendicular to
the basic printing line (i.e. X points for columns, Y points for rows)

=cut

sub perpendPoints{
    my ($offset, $baseLocation, $gridLength) = @_;
    my @points = ();
    for(my $i = 0; $i < $gridLength; $i++){
        my $side = (2*(($i+$baseLocation) % 2) - 1);
        push(@points, $baseLocation);
        push(@points, $baseLocation + $offset/2 * $side);
        push(@points, $baseLocation + $offset/2 * $side);
    }
    push(@points, $baseLocation);
    return(@points);
}

=head2 trim(pointArrayRef, minX, minY, maxX, maxY)

Trims an array of points to specified rectangular limits. Point
components that are outside these limits are set to the limits.

=cut

sub trim{
  my ($pointArrayRef, $minX, $minY, $maxX, $maxY) = @_;
  foreach (@{$pointArrayRef}){
    $_->[0] = ($_->[0] < $minX) ? $minX : (($_->[0] > $maxX) ? $maxX : $_->[0]);
    $_->[1] = ($_->[1] < $minY) ? $minY : (($_->[1] > $maxY) ? $maxY : $_->[1]);
  }
}

=head2 makeNormalisedGrid(z, gridWidth, gridHeight, lineSeparation)

Generate a set of curves (array of array of 2d points) that describe a
horizontal slice of a truncated regular octahedron with edge length 1.

=cut

sub makeNormalisedGrid{
    my ($z, $gridWidth, $gridHeight, $lineSeparation) = @_;
     # offset required to create a regular octagram
    my $octagramGap = 1 / (1 + sqrt(2));
    # sawtooth wave function for range f($z) = [-$octagramGap .. $octagramGap]
    my $offset = (abs((fmod($z * sqrt(2), 4)) - 2) - 1) * $octagramGap;
    my @points = ();
    for(my $x = 0; $x <= $gridWidth; $x++){
        my @xPoints = perpendPoints($offset, $x, $gridHeight);
        my @yPoints = colinearPoints($offset, $gridHeight);
        # This is essentially @newPoints = zip(@xPoints, @yPoints)
        my @newPoints = map { [($xPoints[$_],$yPoints[$_] - $lineSeparation)] }
          (0 .. $#xPoints);
        # trim points to grid edges
        trim(\@newPoints,0,0,$gridWidth,$gridHeight);
        if(($x + $gridWidth) % 2 == 0){
          push(@points, [ @newPoints ]);
        } else {
          push(@points, [ reverse(@newPoints) ]);
        }
    }
    for(my $y = 0; $y <= $gridHeight; $y++){
        my @xPoints = colinearPoints($offset, $gridWidth);
        my @yPoints = perpendPoints($offset, $y, $gridWidth);
        my @newPoints = map { [($xPoints[$_],$yPoints[$_])] } (0 .. $#xPoints);
        # trim points to grid edges
        trim(\@newPoints,0,0,$gridWidth,$gridHeight);
        if($y % 2 == 0){
          push(@points, [ @newPoints ]);
        } else {
          push(@points, [ reverse(@newPoints) ]);
        }
    }
    return @points;
}

=head2 makeGrid(z, gridSize, gridWidth, gridHeight, lineSeparation)

Generate a set of curves (array of array of 2d points) that describe a
horizontal slice of a truncated regular octahedron with a specified
grid square size.

=cut

sub makeGrid{
  my ($z, $gridSize, $gridWidth, $gridHeight, $lineSeparation) = @_;
  my $scaleFactor = $gridSize;
  my $normalisedZ = $z / $scaleFactor;
  my $normalisedSep = $lineSeparation / $scaleFactor;
  my @points = makeNormalisedGrid($normalisedZ, $gridWidth, $gridHeight,
                                  $normalisedSep);
  foreach my $lineRef (@points){
    foreach my $pointRef (@{$lineRef}){
      $pointRef->[0] *= $scaleFactor;
      $pointRef->[1] *= $scaleFactor;
    }
  }
  return @points;
}

# example -- SVG output

my $z = 0;

print <<EOT;
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="-50 -10 150 190"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <title>Truncated octahedron slice (Z = $z)</title>
  <desc>A slice of a truncated octahedron tesselation at Z = $z</desc>
  <path fill="none" stroke="black" stroke-width="0.3"
        d="
EOT

# grid of size 10mm per cell, with gap of 0.35mm
my @points = makeGrid($z,10,5,9,0.35);

foreach my $lineRef (@points){
  print("M".join("L",map{sprintf("%s,%s", $_->[0], $_->[1])} @{$lineRef})."\n");
}

print <<EOT;
" />
</svg>
EOT
@alexrj
Owner
alexrj commented Jan 10, 2014

Wow, thank you @gringer. Nicely coded too.

Perhaps you should add an argument to makeGrid() for passing the angle (0°/90°)? It seems to me that makeGrid() currently returns twice the paths needed for a certain layer.

Or maybe I'm getting this wrong, but looking at the exported SVG there are some paths crossing other paths, which is generally bad:

schermata 2014-01-10 a 19 11 38

@gringer
gringer commented Jan 10, 2014

Or maybe I'm getting this wrong, but looking at the exported SVG there are some paths crossing other paths, which is generally bad

The makeGrid() function generates sets of two types of curves, horizontally oriented curves and vertically oriented curves, but the curves are not joined to each other. Paths do cross, but do not intersect except at single points (which is unavoidable if you want to make sure that no angles along the extrusion path, except for edges, are 90°). Note that I've clipped the lines at the edges of the grid, which may make it appear that the edge lines overlap.

The separate line segments are a bit easier to see on the SVG if you split it up into component paths and randomly colour each path:

troct

If this edge clipping method is not desirable, I could remove the clipped line segments entirely (which would leave gaps in the outer curves), or adjust the pattern location/width so that the octagram edges line up exactly with the clip region.

I would imagine that this pattern would be implemented by generating a pattern that covers a little bit more than the bounding box of the model, intersecting it with the islands of the Z-slice, and only printing pattern lines where the pattern lies within an island, so the precise implementation of the edges shouldn't be too much of an issue.

@gringer
gringer commented Jan 10, 2014

If paths crossing at grid intersections are completely undesirable, then there are two options for fixing that:

  1. Allow angles of 90° in the pattern. This will probably slow down the pattern generation by quite a lot and make the path definition a bit more complicated, so I'd rather not do this.
  2. Alternate between column and row curves on each layer. This will mean that the diagonal edges will have a single thickness, and alternating horizontal/vertical lines will be missing on the square components.
@gringer
gringer commented Jan 11, 2014

Okay, I've implemented option 2, which alternates between horizontal and vertical lines for each layer and has no offset lines. The example output of the script is now a javascript-animated SVG file to demonstrate this (view in your web browser) -- the current layer is a black line, and the next layer is a transparent grey line. The grid now extends beyond the specified limits (which I've indicated with a yellow rectangle in the example SVG output), with the assumption that lines will be clipped to the limits as necessary:

truncoct

The final value in makeGrid defines which of columns/rows to print. 1 = columns, 2 = rows, 3 = both. I think that was what you were requesting, but I'm not completely sure.

#!/usr/bin/perl

=head1 DESCRIPTION

Creates a contiguous sequence of points at a specified height that make
up a horizontal slice of the edges of a space filling truncated
octahedron tesselation. The octahedrons are oriented so that the
square faces are in the horizontal plane with edges parallel to the X
and Y axes.

=cut

use warnings;
use strict;

use POSIX "fmod";

=head1 FUNCTIONS
=cut

=head2 colinearPoints(offset, gridLength)

Generate an array of points that are in the same direction as the
basic printing line (i.e. Y points for columns, X points for rows)

Note: a negative offset only causes a change in the perpendicular
direction

=cut

sub colinearPoints{
    my ($offset, $baseLocation, $gridLength) = @_;
    my @points = ();
    push(@points, $baseLocation - abs($offset/2));
    for(my $i = 0; $i < $gridLength; $i++){
        push(@points, $baseLocation + $i + abs($offset/2));
        push(@points, $baseLocation + ($i+1) - abs($offset/2));
    }
    push(@points, $baseLocation + $gridLength + abs($offset/2));
    return(@points);
}

=head2 colinearPoints(offset, baseLocation, gridLength)

Generate an array of points for the dimension that is perpendicular to
the basic printing line (i.e. X points for columns, Y points for rows)

=cut

sub perpendPoints{
    my ($offset, $baseLocation, $gridLength) = @_;
    my @points = ();
    my $side = (2*(($baseLocation) % 2) - 1);
    push(@points, $baseLocation - $offset/2 * $side);
    for(my $i = 0; $i < $gridLength; $i++){
        $side = (2*(($i+$baseLocation) % 2) - 1);
        push(@points, $baseLocation + $offset/2 * $side);
        push(@points, $baseLocation + $offset/2 * $side);
    }
    push(@points, $baseLocation - $offset/2 * $side);
    return(@points);
}

=head2 trim(pointArrayRef, minX, minY, maxX, maxY)

Trims an array of points to specified rectangular limits. Point
components that are outside these limits are set to the limits.

=cut

sub trim{
  my ($pointArrayRef, $minX, $minY, $maxX, $maxY) = @_;
  foreach (@{$pointArrayRef}){
    $_->[0] = ($_->[0] < $minX) ? $minX : (($_->[0] > $maxX) ? $maxX : $_->[0]);
    $_->[1] = ($_->[1] < $minY) ? $minY : (($_->[1] > $maxY) ? $maxY : $_->[1]);
  }
}

=head2 makeNormalisedGrid(z, gridWidth, gridHeight, curveType)

Generate a set of curves (array of array of 2d points) that describe a
horizontal slice of a truncated regular octahedron with edge length 1.

curveType specifies which lines to print, 1 for vertical lines
(columns), 2 for horizontal lines (rows), and 3 for both.

=cut

sub makeNormalisedGrid{
    my ($z, $gridWidth, $gridHeight, $curveType) = @_;
     # offset required to create a regular octagram
    my $octagramGap = 1 / (1 + sqrt(2));
    # sawtooth wave function for range f($z) = [-$octagramGap .. $octagramGap]
    my $offset = (abs((fmod($z * sqrt(2), 4)) - 2) - 1) * $octagramGap;
    my @points = ();
    if(($curveType & 1) != 0){
        for(my $x = 0; $x <= $gridWidth; $x++){
            my @xPoints =
                perpendPoints($offset, $x, $gridHeight);
            my @yPoints = colinearPoints($offset, 0, $gridHeight);
            # This is essentially @newPoints = zip(@xPoints, @yPoints)
            my @newPoints = map {
                [($xPoints[$_], $yPoints[$_])] } (0 .. $#xPoints);
            # trim points to grid edges
            #trim(\@newPoints,0,0,$gridWidth,$gridHeight);
            if($x % 2 == 0){
                push(@points, [ @newPoints ]);
            } else {
                push(@points, [ reverse(@newPoints) ]);
            }
        }
    }
    if(($curveType & 2) != 0){
        for(my $y = 0; $y <= $gridHeight; $y++){
            my @xPoints = colinearPoints($offset, 0, $gridWidth);
            my @yPoints = perpendPoints($offset, $y, $gridWidth);
            my @newPoints = map {
                [($xPoints[$_],$yPoints[$_])] } (0 .. $#xPoints);
            # trim points to grid edges
            #trim(\@newPoints,0,0,$gridWidth,$gridHeight);
            if($y % 2 == 0){
                push(@points, [ @newPoints ]);
            } else {
                push(@points, [ reverse(@newPoints) ]);
            }
        }
    }
    return @points;
}

=head2 makeGrid(z, gridSize, gridWidth, gridHeight, curveType)

Generate a set of curves (array of array of 2d points) that describe a
horizontal slice of a truncated regular octahedron with a specified
grid square size.

=cut

sub makeGrid{
  my ($z, $gridSize, $gridWidth, $gridHeight, $curveType) = @_;
  my $scaleFactor = $gridSize;
  my $normalisedZ = $z / $scaleFactor;
  my @points = makeNormalisedGrid($normalisedZ, $gridWidth, $gridHeight,
                                  $curveType);
  foreach my $lineRef (@points){
    foreach my $pointRef (@{$lineRef}){
      $pointRef->[0] *= $scaleFactor;
      $pointRef->[1] *= $scaleFactor;
    }
  }
  return @points;
}

# example -- animated SVG output
# 40 horizontal slices, grid of size 10mm per cell, 5 cells wide by 8 high
my $numSlices = 40;
my $gridSize = 10;
my $gridWidth = 5;
my $gridHeight = 8;
my $gridMaxX = $gridSize * ($gridWidth);
my $gridMaxY = $gridSize * ($gridHeight);
my $svgMinX = -$gridSize/2;
my $svgMinY = -$gridSize/2;
my $svgWidth = $gridSize * ($gridWidth+1);
my $svgHeight = $gridSize * ($gridHeight+1);
my $lineWidth = $gridSize / 20;

print <<EOT;
<?xml version="1.0" standalone="no"?>
<svg width="100%" height="100%" viewBox="$svgMinX $svgMinY $svgWidth $svgHeight"
     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:svg="http://www.w3.org/2000/svg" version="1.1"
     onload="startup(evt)">
  <script>
<![CDATA[
var svgDocument=null;
var slice=0;
function startup(evt){
  svgDocument=evt.target.ownerDocument;
  H=svgDocument.getElementById("gHour");
  window.setInterval("changeSlice()",500);
}
function changeSlice(){
  window.status  = "Slice: " + slice;
  for(var i=0;i<$numSlices;i++){
    S=svgDocument.getElementById("layer"+i);
    S.setAttribute("stroke","none");
    S.setAttribute("opacity","0");
  }
  S=svgDocument.getElementById("layer"+slice);
  S.setAttribute("stroke","#000000");
  S.setAttribute("opacity","1");
  S=svgDocument.getElementById("layer"+((slice+1) % $numSlices));
  S.setAttribute("stroke","#A0A0A0");
  S.setAttribute("opacity","0.25");
  slice = (slice + 1) % $numSlices;
}
//]]>
</script>
   <title>Truncated octahedron slices</title>
  <desc>Slices of a truncated octahedron tesselation</desc>
EOT

for(my $i = 0; $i < $numSlices; $i++){
  my $z = (2 * sqrt(2) * $gridSize) * ($i / $numSlices);
  my @points = makeGrid($z, 10, 5, 8, ($i % 2) + 1);
  print("   <g id=\"layer${i}\" fill=\"none\" stroke=\"none\">\n");
  foreach my $lineRef (@points){
    print("    <path stroke-width=\"$lineWidth\" d=\"");
    print("M".
          join("L",map{sprintf("%s,%s", $_->[0], $_->[1])} @{$lineRef}));
    print("\"/>\n");
  }
  print("   </g>\n");
}

print <<EOT;
   <rect x="0" y="0" width="$gridMaxX" height="$gridMaxY"
    fill="yellow" stroke="none" opacity="0.25"/>
</svg>
EOT
@alexrj
Owner
alexrj commented Jan 11, 2014

I really appreciate your contribution. I will implement that as soon as possible.

@gringer
gringer commented Jan 13, 2014

FWIW, here's one version of printing all the squares (with one side doubled) in a single layer. It doubles back on its own path for every second octagonal edge, which will produce extrusion blobs at these locations due to extruder hysteresis and the extra time required for a complete reversal of printing direction:

truncoct_singlelayer

@alexrj alexrj added this to the 1.2.0 milestone May 26, 2014
@DwayneS
DwayneS commented Jun 22, 2014

Was this feature abandoned or simply put on hold?

@traveltrousers

it's in for implementation, lets hope it works out, looks great

@gringer
gringer commented Jul 5, 2014

This version has solid walls for each face, but someone had a go at printing the wireframe version using my STL file:

http://www.thingiverse.com/make:81810

@alexrj
Owner
alexrj commented Jul 9, 2014

Not abandoned at all, I will merge it soon - sorry, I've been working on other things!

@alexrj
Owner
alexrj commented Jul 26, 2014

Good news! I merged this code. Thank you very much @gringer. Here's a preview:

schermata 2014-07-26 a 16 54 30

schermata 2014-07-26 a 16 57 28

@alexrj alexrj added the Done label Jul 26, 2014
@misan
misan commented Jul 26, 2014

It looks great.

@jiripech

Oh, that's so cool! Thank you, guys!

@trbielec

Fantastic work! This looks incredible!

@gringer
gringer commented Jul 28, 2014

Excellent, thanks.

It looks like it might be a bit tall (unless you were going for that effect) -- the diamonds on the outside should be squares, and each non-diamond face should be a regular hexagon. However, a taller version shouldn't be a problem and might be better because the angle is steeper.

@alexrj
Owner
alexrj commented Jul 29, 2014

I named this "3D honeycomb". Would "Octahedrons (3D)" be better?

@whosawhatsis

It would certainly be more accurate, which I'm all for.

@gringer
gringer commented Jul 29, 2014

A honeycomb is any space-filling tesselation. A rectangular grid with a full layer every n layers would also be a called a honeycomb in the geometric sense.

https://en.wikipedia.org/wiki/Honeycomb_%28geometry%29

If you want to be specific, this is a "bitruncated cubic honeycomb", although "Octahedrons (3D)" is probably going to better describe what people see when it's printing.

https://en.wikipedia.org/wiki/Bitruncated_cubic_honeycomb

@alexrj
Owner
alexrj commented Aug 3, 2014

Then I think I'll leave "Honeycomb (3D)", since it's not wrong and octahedrons is less correct (we have truncated octahedrons). Also, the 2D honeycomb is not called hexagons...

@alexrj alexrj closed this Aug 3, 2014
@alexrj
Owner
alexrj commented Aug 8, 2014

Uhm, I think there's something wrong with Z scaling somewhere. The truncated octahedrons are stretched vertically. This is visible in my screenshot above where the squares look like rhomboids...

@alexrj alexrj reopened this Aug 8, 2014
@alexrj
Owner
alexrj commented Aug 8, 2014

By the way, when changing $octagramGap to 1 I get a much more interesting tessellation… it's not made from truncated octahedrons anymore, but it's nice looking and also it provides completely closed cells instead of vertical voids:

schermata 2014-08-08 alle 02 51 45

@alexrj alexrj modified the milestone: 1.2.0, 1.2.1 Aug 8, 2014
@alexrj alexrj added a commit that referenced this issue Aug 8, 2014
@alexrj Fix truncated octahedrons. #1646 45fc748
@alexrj
Owner
alexrj commented Aug 8, 2014

@gringer, how can we try to fix the formulas in order to get regular truncated octahedrons? This is the closest I was able to do:

my $octagramGap = 3/4;
my $wave = abs(fmod($z, 2) - 1)*2 - 1;
my $offset = $wave * $octagramGap;

This way the resulting solid has square faces but no hexagons yet, resulting in a nicer honeycomb by the way:

schermata 2014-08-08 a 13 27 33
schermata 2014-08-08 a 13 27 41

@alexrj alexrj added a commit that referenced this issue Aug 8, 2014
@alexrj Fix truncated octahedrons. #1646 43b1aab
@alexrj
Owner
alexrj commented Aug 8, 2014

Okay. Got it:

my $octagramGap = 0.5;
my $a = sqrt(2);  # period
my $wave = abs(fmod($z, $a) - $a/2)/$a*4 - 1;
my $offset = $wave * $octagramGap;

schermata 2014-08-08 a 14 15 32

@trbielec
trbielec commented Aug 8, 2014

Closed cells is exactly what I was looking for. Thank you @alexrj! Awesome work!

@gringer
gringer commented Aug 8, 2014

Great, that looks correct now. Sorry I couldn't help out with the recent debugging.

@Recmo
Recmo commented Aug 11, 2014

It's probably not worth the extra complexity, but the Weiare-Phelan structure is supposed to be slightly more efficient. (See https://en.wikipedia.org/wiki/Weaire%E2%80%93Phelan_structure and http://www.nytimes.com/imagepages/2008/08/05/science/20080805_SWIM_GRAPHIC.html).

Also, Is it possible to complete the octahedrons by filling (bridging) the square when it is at its minimal size? Or perhaps twice in orthogonal directions.

@alexrj
Owner
alexrj commented Nov 8, 2014

@Recmo, that infill pattern looks interesting. I'm afraid I have not enough time for studying how to implement it though at the moments, so I accept any help.

Regarding filling the squares, I'm afraid that would cause lots of small bridges with poor anchors and lots of retractions for moving between them. But I understand the usefulness of closed cells...

@alexrj alexrj removed this from the 1.2.1 milestone Nov 8, 2014
@lordofhyphens
Collaborator

Yeah, it's done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment