I saw an article on the Guardian website here on the 3D-printing of shapes which project interesting patterns of light. Ignoring the strangely forced Halloween reference, I thought this would be an interesting project to attempt for an arbitrary pattern, perhaps as a personalised lampshade. Buoyed by the continuing high of leftover sweets from Friday night, let’s have a look.

Much like the guys in the article linked above, we want to take a projected image and work backwards to see what kind of shape can be projected to form that image. For simplicity, lets assume the image is formed by placing a light source at the top of a sphere of radius as depicted below. This sphere has some holes cut into it which project light into a plane a distance below. The image on the plane is then the stereographic projection of the image on the sphere. We will need to work backwards – given a projected image with coordinates , can we work out the pattern of shapes cut into the sphere?

Obviously we can only make black-and-white images using this technique, though it would be interesting in the future perhaps to consider a sphere made of transparent printed plastic (for example) which projects a colour image.

A coordinate on the image plane can be represented in polar coordinates . The ray from the light source to this point then propagates at angle

and with some elementary geometry we know that this ray intersects the sphere at an angle . With a new coordinate system centred at the centre of the sphere, the image coordinates are mapped to new coordinates

This is all the simple maths required (though of course this is an interesting topic to examine more deeply), the rest is getting an image into the correct format.

As an example, let’s use the following pleasant flowery image from Google image search:

The first step is to binarise the image, and figure out which parts we want to keep and which we want to cut out to form the holes in our lampshade. Here it’ll look best if we cut the black parts out, so the lamp projects a nice flowery pattern. To avoid any funny edge effects I’ll crop the pattern to a circle, and so our ‘mask’ is drawn below, along with the relevant snippet of Matlab.

P = imread('Pattern.png'); P = 1-im2bw(P); % Keep black parts Nmin = min(size(P)); % Crop into square, then circle P = P(1:Nmin, 1:Nmin); [xg, yg] = meshgrid(1:Nmin, 1:Nmin); P((xg - Nmin/2).^2 + (yg - Nmin/2).^2 > 0.99*0.25*Nmin^2) = 0; % Create a small border P = padarray(P, [1 1], 0);

Now, this is a bitmap image and we will eventually need something 3D-printable, i.e. in vector format. I need to go through each of these holes and trace a contour around it. First, however, I need to isolate each hole separately. There is, as always, a useful inbuilt function just for this purpose which identifies distinct white regions of a monochrome image and which pixels they correspond to. In the snippet below I loop through these regions and create a new image containing a single hole. Before I make the contour I smooth the image slightly to avoid any ‘staircasing’ of the contour line.

CC = bwconncomp(P); for n = 1:CC.NumObjects % Create new image for each hole newP = zeros(size(P)); newP(CC.PixelIdxList{1,n}(:)) = 1; % Smooth image slightly newP = filter2(fspecial('average',3),newP); % Get contour around hole C = contourc(newP,[1 1]); end

There is one more thing to consider if we want decent performance, and that is the resolution of the contour. Matlab will quite happily produce an extra contour line for each pixel, which is much more than we need. We can apply the Douglas-Peucker line simplifcation algorithm to each hole to drastically reduce the number of segments, using the File Exchange submission here. As illustrated below, we can reduce the number of points in each contour drastically without significantly worsening the image.

Inserting this into the loop above, we get the vectorised, simplified, segmented holes which are plotted below and coloured separately. In this case there is one large hole and a few smaller ones.

This is great so far but at the moment we only have the outlines of the holes, we now need to fill in the blanks. The boundary of our lampshade is a simple circle, and the holes inside are a series of complicated contours. Fortunately, this kind of problem has been solved many times before. In the field of finite element analysis a potentially complex domain needs to be discretised into a mesh for numerical computation. Here I borrow the extremely useful submission Mesh2D from the File Exchange again, which will happily crunch through the boundaries I’ve defined and generate a mesh. This function is remarkably easy to use, so there isn’t much to say about it – it just requires a list of vertices and edges.

Now it should be clear why I was worried about the number of elements in the contour lines – each line segment forms the side of a triangular mesh element. If the contours contained many times more segments, the mesh would become very dense and slow to generate.

Happily we’re almost there now, it just remains to warp this mesh into the form required. As each mesh element is a triangle, we can move the three corners wherever we like and they will still lie on a plane together, so there is no need to re-mesh. For each vertex we apply the transformations listed above, and pack the faces and vertices into a Matlab patch object:

Nice! This looks pretty, but is it correct? And could it actually be printed? To go further, I take this mesh and import into Blender. Here I can solidify the mesh to create some physical thickness, and add a light source in the right place to check the stereographic projection.

Poor-quality render aside, this looks good. The blurring is due to the finite size of the light source rather than any depth-of-field I’ve added. If we compare the image from above and the original contour lines, we see that the stereographic projection is correct, and the original image is reproduced.

This is great, and now we know everything works we can explore a little. What if we fix the size of the image but change the size of the lampshade?

In the small-image-size limit the projection is just a simple linear projection, and the lampshade is reduced to a simple circle.

What about some different patterns? Turning again to Google image search we can do a geometric pattern:

Or perhaps a lamp to replace your wallpaper (this one isn’t strictly possible to make…):

Finally, as a Bristolian at heart, how could I not include a stencil from the worlds favourite graffiti artist (edit: as pointed out on Hacker News, this isn’t actually a Banksy! The shame. It is authentic Bristolian graffiti though so I’ll let it stand). This might also be the first time ever that a panda bear has been meshed with an FEA mesher…

If you’d like to play with these meshes yourself, the .blend file containing the models above can be found here. You should be able to export to .stl files if you’d like to 3D print anything – if you do, let me know! I don’t have access to a printer here.

Update: Below in the comments, Aleksi has printed the panda lampshade with great results! Here’s one of the pictures of it in action: