summaryrefslogtreecommitdiff
path: root/ambiguous-cylinders.scad
blob: 965942d82fe06d717a9012fffb6d2c63e30bf73f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
  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([ [-50][-4.51][ 21][ 2.53][ 50],
                    [ 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);
}