/* Recreating Sugihara's impossible objects Original work: http://www.isc.meiji.ac.jp/~kokichis/Welcomee.html PDF Paper with the maths: https://www.mdpi.com/2073-8994/8/4/21 Based on Sugihara's work, I've approximated his mathematical model with OpenSCAD. */ /* let's build it in steps: given a 2D object, extrude a `thin` "cylinder" with its shape as section; this approximates the set of points that would appear indistinguishable from the given shape to an observer infinitely distant on the Z axis (`height` determines how tall the cylinder is, `shift` determines how low it is on Z axis) */ module view_cylinder(shift=100/3,height=200,thin=0.1) { translate([0,0,-shift]) { difference() { linear_extrude(height=height,center=false) { offset(r=thin/2) children(0); } linear_extrude(height=height,center=false) { offset(r=-thin/2) children(0); } } } } /* given two objects: * build they view cylinders * rotate them so the two "observers" are opposite each other on the Y axis, lookin toward the origin with a downward `angle` * intersect them the result approximates the set of points that would look like the first object to the first observers, and like the second object to the second observer (`size` should be higher than the diameter of the largest child, otherwise the shapes may get cut) */ module intersect_view_cylinders(angle=45,size=100,thin=0.1) { shift=size/3; height=size*2; intersection() { rotate([0,angle,0]) view_cylinder(shift,height,thin) children(0); rotate([0,-angle,0]) view_cylinder(shift,height,thin) children(1); } }; /* `intersect_view_cylinders` produces a slightly ugly shape, with two half-curves for each half-object; to see the problem, render this: intersect_view_cylinders() { circle(d=14); rotate([0,0,45]) square(size=10,center=true); } therefore, we render it twice, cutting the "wrong half" out each time. This gives us the curve we want at the top of our ambiguous cylinder */ module clip_intersect_view(angle=45,size=100,thin=0.1) { bound=size*2; skip=0; intersection() { intersect_view_cylinders(angle,size,thin) { children(0); children(1); } translate([0,-size/2,0]) rotate([0,-90-angle,0]) cube([size,size,size]); } intersection() { intersect_view_cylinders(angle,size,thin) { children(0); children(1); } translate([0,-size/2,0]) rotate([0,90-angle,0]) cube([size,size,size]); } }; /* finally, we "extrude" the curve; OpenSCAD can't extrude a 3d shape, but we can "paint" it with a vertical cylinder, and that's close enough (`thick` determines the thickness of the wall, `height` its height) */ module ambiguous_cylinder(angle=45,size=100,thin=0.1,thick=1,height=10) { minkowski() { clip_intersect_view(angle,size,thin) { children(0); children(1); } cylinder(r=thick/2,h=height*2,center=true); } } /* Some examples: */ /* Arrow that always points right */ module arrow(length=10) { scale([length/10,length/10,length/10]) polygon([ [-5, 0], [-4.5, 1], [ 2, 1], [ 2.5, 3], [ 5, 0], [ 2.5,-3], [ 2,-1], [-4.5,-1] ]); } ambiguous_cylinder() { rotate([0,0,90]) arrow(30); rotate([0,0,-90]) arrow(30); } /* circle / diamond */ translate([0,50,0]) ambiguous_cylinder() { circle(d=14); rotate([0,0,45]) square(size=10,center=true); }