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 20139 days, 6 hours, 57 minutes ago.