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.