summaryrefslogtreecommitdiff
path: root/ambiguous-cylinders.scad
blob: 0c885fba85538bbdd7c6508dc215786129498a7f (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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
  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([ [-50][-4.51][ 21][ 2.53][ 50],
                    [ 2.5,-3][ 2,-1][-4.5,-1] ]);
}
 
module fat_arrow(length=10) {
     scale([length/10,length/10,length/10])
          polygon([ [-50][-41.5][-11.5][ 0.54][ 50],
                    [ 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);
}