It’s been brought to my attention that our first Image Lathe example exhibited a great flaw. It seems some people think a Vase should be able to hold water! Now around the 3D printing world we talk about water-tight meshes but not usually this definition.
This code is fairly close to the original Image Lathe example. Instead of intersecting the pattern image with the profile we instead union the pattern with a slightly smaller profile shape. The innerShell is the profile shape that’s rotated to a cylinder that’s patternHeight smaller radius. If you wanted the pattern on the inside of something then you could make the innerShell larger instead.
function getDihedralSymmetry( n){ var a = PI/(n); var cosa = Math.cos(a); var sina = Math.sin(a); var symm = new ReflectionSymmetry(); var splanes = new Array(); var count = 0; splanes[count++] = new ReflectionSymmetry.getPlane(new Vector3d(cosa,0,-sina),0); splanes[count++] = new ReflectionSymmetry.getPlane(new Vector3d(-cosa,0, -sina), 0); symm.setGroup(splanes); return symm; } function makeShell(profilePath, width, height, voxelSize){ var radius = width/2; var boxDepth = 2*Math.PI*radius; var boxWidth = radius; var boxHeight = height; var image = new ImageBitmap(profilePath, boxWidth, boxHeight, boxDepth, voxelSize); image.setBaseThickness(0.0); image.setUseGrayscale(false); image.setBlurWidth(2*voxelSize); var ct = new CompositeTransform(); ct.add(new Rotation(0,1,0, -Math.PI/2)); // align side of the image box with xy lane ct.add(new Translation(0, 0, -radius/2)); ct.add(new RingWrap(radius)); image.setTransform(ct); return image; } function main(arg){ var voxelSize = 0.25*MM; var vaseWidth = 80*MM; var vaseHeight = 100*MM; var patternHeight = 4*MM; var symmetryOrder = 12; var imgPath = arg[0]; var profilePath = arg[1]; var img = loadImage(imgPath); var imgBoxHeight = vaseHeight; var imgBoxWidth = img.getWidth() * imgBoxHeight /img.getHeight(); var imgBoxThickness = vaseWidth/2; var image = new ImageBitmap(img, imgBoxWidth, imgBoxHeight, imgBoxThickness, voxelSize); image.setBaseThickness(0.0); image.setUseGrayscale(false); image.setBlurWidth(voxelSize); image.setImagePlace(ImageBitmap.IMAGE_PLACE_TOP); var padding = 2*MM; var gWidth = vaseWidth + 2*padding; var gHeight = vaseHeight + 2*padding; var ct = new CompositeTransform(); ct.add(new Translation(0,0,-imgBoxThickness/2)); ct.add(new RingWrap(vaseWidth/2)); ct.add(getDihedralSymmetry(symmetryOrder)); image.setTransform(ct); var shell = makeShell(profilePath, vaseWidth, vaseHeight, voxelSize); var innerShell = makeShell(profilePath, vaseWidth-2*patternHeight, vaseHeight, voxelSize); var intersection = new Intersection(shell, image); var union = new Union(innerShell, intersection); dest = createGrid(-gWidth/2,gWidth/2,-gHeight/2,gHeight/2,-gWidth/2,gWidth/2,voxelSize); var maker = new GridMaker(); maker.setSource(union); maker.makeGrid(dest); meshSmoothingWidth = 2; meshErrorFactor = 0.05; return dest; }