16PesciBox.frink

Download or view 16PesciBox.frink in plain text format


/** This is an attempt to make a completely programmatical box for an object
    extruded from an image.  It calculates a convex hull for the puzzle and
    makes a tight-fitting box.  It also embosses a thin version of the puzzle
    onto the bottom of the box as a guide.

    This is used to make a box for this puzzle:
     16Pesci.frink

    Original image used for extracting:
     https://frinklang.org/frinksamp/16PesciOrig.jpg

    Cleaned-up image:  (you will need this to run this program)
     https://frinklang.org/frinksamp/16PesciBW.png
*/

use ConvexHull.frink

wallThickness = 1.2 mm
origWidth = 150 mm  // Width I printed the original puzzle at (after cropping)
spacing = 0.15 mm    // The spacing between the puzzle and walls and lid
wallHeight = 7 mm
floorOverhang = 2 mm
floorThickness = 0.8 mm
emboss = 0.7 mm
lidWallHeight = 12 mm + emboss + spacing
lidOverhang = 2 mm
lidThickness = 0.8 mm

// The image file to extract can be loaded from:
// https://frinklang.org/frinksamp/16PesciBW.png
img = new image["file:16PesciBW.png"]
img.show[]

//img = img.resize[200, undef]
[width, height] = img.getSize[]
println["width is $width, height is $height"]

print["Loading points..."]
points = new set

// We calculate the convex hull (which is O(n log n)) faster by first finding
// only the possible outside points.
LTOR:
for y = 0 to height-1
   for x = 0 to width-1
      if img.getPixelGrayInt[x,y] < 128
      {
         points.put[[x,y]]
         next LTOR
      }

RTOL:
for y = 0 to height-1
   for x = width-1 to 0 step -1
      if img.getPixelGrayInt[x,y] < 128
      {
         points.put[[x,y]]
         next RTOL
      }

DOWN:      
for x = 0 to width-1      
   for y = 0 to height-1
      if img.getPixelGrayInt[x,y] < 128
      {
         points.put[[x,y]]
         next DOWN
      }
      
UP:      
for x = 0 to width-1      
   for y = height-1 to 0 step -1
      if img.getPixelGrayInt[x,y] < 128
      {
         points.put[[x,y]]
         next UP
      }

println["done."]
println["Object has " + length[points] + " points."]

print["Calculating convex hull..."]
hull = ConvexHull.hull[points]
println["done."]
println["Hull has " + length[hull] + " points."]

// Make a Point2DFloatList (polygon) out of the points.
// TODO:  Find a way for ConvexHull to act on a Point2DFloatList?
hullFloat = newJava["frink.graphics.Point2DFloatList"]
for [x,y] = hull
   hullFloat.addPoint[x, -y]

// Calculate the voxel resolution that we printed the original at
bb = hullFloat.getBoundingBox[]
r = (bb.getMaxX[] - bb.getMinX[]) / (origWidth)
println["res is " + format[r, 1/in, 3] + "/in"]

// This is a Point2DFloat
[cx, cy] = hullFloat.getCentroid[].toExpression[]

offset = (spacing + wallThickness/2) r
floorOffset = (spacing + wallThickness + floorOverhang) r
lidWallOffset = (spacing + wallThickness + spacing + wallThickness/2) r
lidOffset = (spacing + wallThickness + spacing + wallThickness/2 + lidOverhang) r

wallPoly =  newJava["frink.graphics.Point2DFloatList"]
floorPoly = newJava["frink.graphics.Point2DFloatList"]
lidWall =  newJava["frink.graphics.Point2DFloatList"]
lid = newJava["frink.graphics.Point2DFloatList"]

points = hullFloat.getPointCount[]
for i=0 to points-1
{
   [px, py] = hullFloat.getPoint[i].toExpression[]
   wallPoly.addPoint[px < cx ? px-offset : px+offset,
                     py < cy ? py-offset : py+offset]
   floorPoly.addPoint[px < cx ? px-floorOffset : px+floorOffset,
                      py < cy ? py-floorOffset : py+floorOffset]
   lidWall.addPoint[px < cx ? px-lidWallOffset : px+lidWallOffset,
                    py < cy ? py-lidWallOffset : py+lidWallOffset]
   lid.addPoint[px < cx ? px-lidOffset : px+lidOffset,
                py < cy ? py-lidOffset : py+lidOffset]
}

// Make wall
v = callJava["frink.graphics.VoxelArray", "strokeZ", [wallPoly, floorThickness r , (wallHeight+floorThickness) r, wallThickness/2 r, true]]

// Make floor
f = callJava["frink.graphics.VoxelArray", "extrudeZ", [floorPoly, 0, floorThickness r]]
v = v.union[f]

// Emboss puzzle on floor
f = callJava["frink.graphics.VoxelArray", "extrudeZ", [img, round[floorThickness r], round[(floorThickness + emboss) r]]]
f.translate[round[cx], round[cy+1], 0]
v = v.union[f]

// Make lid wall
lw = callJava["frink.graphics.VoxelArray", "strokeZ", [lidWall, 0, lidWallHeight r, wallThickness/2 r, true]]

// Make lid surface
top = callJava["frink.graphics.VoxelArray", "extrudeZ", [lid, round[lidWallHeight r], round[(lidWallHeight + lidThickness) r]]]
lw = lw.union[top]


v.projectX[undef].show["X"]
v.projectY[undef].show["Y"]
v.projectZ[undef].show["Z"]

lw.projectX[undef].show["Lid X"]
lw.projectY[undef].show["Lid Y"]
lw.projectZ[undef].show["Lid Z"]

filename = "16PesciBox.obj"
print["Writing $filename..."]
w = new Writer[filename]
w.println[v.toObjFormat["16PesciBox", 1 / (r mm)]]
w.close[]
println["done."]

filename = "16PesciLid.obj"
print["Writing $filename..."]
w = new Writer[filename]
w.println[lw.toObjFormat["16PesciLid", 1 / (r mm)]]
w.close[]
println["done."]


Download or view 16PesciBox.frink in plain text format


This is a program written in the programming language Frink.
For more information, view the Frink Documentation or see More Sample Frink Programs.

Alan Eliasen was born 19966 days, 20 hours, 51 minutes ago.