/* 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 a 2D object: * build the view cylinder at an angle * this means rotating the object, extruding, then rotating the result the other way: / / / / /____/ * cut away half the cylinder * because otherwise, in intersect_view_cylinders, we would get 4 arcs (two close to the XZ plane, two close to the XY plane), but we only want 2 (the XY ones) */ module clipped_angled_view_cylinder(angle=60,side=1,size=100,thin=0.1) { shift=size/3; height=size*2; intersection() { rotate([angle,0,0]) view_cylinder(shift,height,thin) rotate([-angle,0,0]) children(0); rotate([angle,0,0]) translate([0,-side*size/2,0]) cube([size,size,size],center=true); } } /* given two objects: * build their view cylinders, rotated so the two "observers" are opposite each other on the Y axis, lookin toward the origin with a downward `angle` * intersect half of them at a time (see above, and Sugihara's paper, for the reason) 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=60,size=100,thin=0.1) { union() { intersection() { clipped_angled_view_cylinder(angle,1,size,thin) children(0); clipped_angled_view_cylinder(-angle,1,size,thin) children(1); } intersection() { clipped_angled_view_cylinder(angle,-1,size,thin) children(0); clipped_angled_view_cylinder(-angle,-1,size,thin) children(1); } } }; /* 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=60,size=100,thin=0.1,thick=1,height=10) { minkowski() { intersect_view_cylinders(angle,size,thin) { children(0); children(1); } cylinder(r=thick/2,h=height*2,center=true); } } /* Some examples: */ 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] ]); } module fat_arrow(length=10) { scale([length/10,length/10,length/10]) polygon([ [-5, 0], [-4, 1.5], [-1, 1.5], [ 0.5, 4], [ 5, 0], [ 0.5,-4], [-1,-1.5], [-4,-1.5] ]); } module arrow_always_right() { ambiguous_cylinder() { arrow(30); rotate([0,0,180]) arrow(30); } } module fat_arrow_always_right() { ambiguous_cylinder() { fat_arrow(30); rotate([0,0,180]) fat_arrow(30); } } module circle_diamond() { ambiguous_cylinder() { circle(d=14); rotate([0,0,45]) square(size=10,center=true); } } module topo_disturbing_cylinders() { translate([0,-4.5,0]) ambiguous_cylinder() { difference() { circle(d=10); translate([0,6,0]) circle(d=10); } circle(d=10); } // we need this one to be lower down, otherwise the views don't // align well translate([0,4.5,-2]) ambiguous_cylinder() { difference() { circle(d=10); translate([0,-6,0]) circle(d=10); } circle(d=10); } // this is just to connect the two parts, making it easier to // hold the printed model together cube([10,4,1],center=true); }