1include <BOSL2/std.scad>
2include <BOSL2/constants.scad>
3use <BOSL2/shapes3d.scad>
4use <JetBrainsMono-Italic-VariableFont_wght.ttf>
5use <NotoSansJP-Regular.otf>
6use <PTMono-Regular.ttf>
7use <NotoSansMono-Bold.ttf>
8
9// Parameters
10top_part = false;
11
12lens_focal_length = 50; //mm 75
13lens_diameter = 52; //mm added 2mm of tolerance
14lens_holder_edge_thickness = 2; //mm
15lens_thickness = 4.5; //mm
16lens_holder_thickness = 2; //mm
17lens_holder_outwards_radius = 3; //mm
18
19camera_inner_dimensions = [100,100,50]; //mm
20
21external_width = 3; //mm
22
23image_plane_width = 0.8; // mm
24
25// This equation can come in handy OA*f/(OA+f)=OA'
26object_distances = [100,102.632,105.556,108.824,112.5,116.667,121.429,126.923,133.333,140.909,150,161.111,175,192.857,216.667,235.185,258.333,288.095,327.778,383.333,466.667,605.556,883.333,1716.67,"∞"]; // mm [300,325,350,375,400,450,500,600,750,1000,1500,3000,"∞"]
27
28text_top_padding = 1; // mm
29text_offset_coeff = -3.7; // -1.9
30index_offset = true; // false
31text_size = 3.3; // mm 2.6
32text_angle = 180; // deg
33text_font = "JetBrainsMono:style=Bold Italic"; //"JetBrainsMono:style=Bold";
34
35jp_font = "NotoSansJP:style=Bold";
36camera_name = "立方体";
37spec_text_size = 5;
38spec_text_padding = 5; // 15
39
40part_connection_depth = 2;
41part_connection_thickness = 0.5;
42part_connection_thickness_tolerance = 0.05;
43
44cyl_resolution = 200;
45txt_resolution = 80;
46crn_resolution = 60;
47
48corner_rounding = 2;
49
50// System
51// Camera box
52camera_box_dimensions = [camera_inner_dimensions.x+external_width*2,camera_inner_dimensions.y+external_width*2,camera_inner_dimensions.z+external_width];
53
54// Lens holder
55lens_holder_cyl_height = lens_thickness+lens_holder_thickness*2;
56lens_holder_external_cyl_height = (lens_holder_cyl_height-external_width)/2;
57
58// Image plane text
59side_image_plane_text_count = ceil(len(object_distances)/2);
60
61// Connection
62part_connection_thickness_post_tolerance_adjustment = part_connection_thickness+(top_part?1:-1)*part_connection_thickness_tolerance;
63
64// Modules
65
66function image_plane_calc(object_distance,focal_length)= (object_distance == "∞")? focal_length : -object_distance*focal_length/(focal_length-object_distance);
67
68
69module image_plane_transform(object_distance,focal_length) { // mm
70 if(object_distance == "∞"){
71 translate([focal_length,0,0]) children();
72 } else{
73 OA_prime = -object_distance*focal_length/(focal_length-object_distance);
74 translate([OA_prime,0,0]) children();
75 }
76}
77
78function image_plane_text_dist_from_top (object_distance,focal_length,index) = -text_top_padding+(index_offset ? text_offset_coeff*(side_image_plane_text_count-index-1) : text_offset_coeff*image_plane_calc(object_distance,focal_length)-text_offset_coeff*focal_length);
79
80
81module image_plane_text_transform(object_distance,focal_length,index){
82 fixed_index = index%side_image_plane_text_count;
83 has_looped = fixed_index<index;
84 dist_from_top = image_plane_text_dist_from_top(object_distance,focal_length,fixed_index);
85 translate([0, has_looped ? camera_inner_dimensions.y+external_width*2: 0, camera_inner_dimensions.z+external_width/2-(has_looped ? text_size : 0)])
86 translate([external_width/2, (has_looped ? 0 : 1) * external_width/4, external_width/2])
87 translate([0,0,dist_from_top])
88 image_plane_transform(object_distance,focal_length) children();
89}
90
91module image_plane_text(object_distance,focal_length,index){
92 fixed_index = index%side_image_plane_text_count;
93 has_looped = fixed_index<index;
94 image_plane_text_transform(object_distance,focal_length,index) rotate([90,text_angle+(has_looped?180:0),(has_looped?180:0)])
95 translate([0,0,(has_looped ? -1 : 0) * external_width/4])
96 linear_extrude(external_width/4)
97 if(object_distance != "∞"){
98 text(format("{:-4d} mm; Υ: {:0.3f}", [object_distance, image_plane_calc(object_distance,focal_length)/-object_distance]),text_size, font = text_font, $fn=txt_resolution);
99 } else {
100 text(format("{:-4s} mm", [object_distance]),text_size, font = text_font, $fn=txt_resolution);
101 }
102 }
103
104module image_plane(object_distance,focal_length,index) { // mm
105 fixed_index = index%side_image_plane_text_count;
106 has_looped = fixed_index<index;
107 translate([external_width/2, external_width/2+external_width/4, external_width/2+external_width/4])
108 image_plane_transform(object_distance,focal_length)
109 cube([image_plane_width,camera_inner_dimensions.y+external_width/2,camera_inner_dimensions.z+external_width/2]);
110 // Legend text
111 image_plane_text(object_distance,focal_length,index);
112 translate([image_plane_width/4, -external_width/4,0])
113 image_plane_text_transform(object_distance,focal_length,index)
114 translate([0,0,(has_looped ? text_size : 0)]) cube([image_plane_width/2,external_width/4,camera_inner_dimensions.z]);
115}
116
117module rounded_cuboid(dimensions,edges,rounding){
118 cuboid(dimensions,anchor=[-1,-1,-1],rounding=rounding,edges=edges,$fn=crn_resolution);
119}
120
121module lens_transform(){
122translate([0, external_width+camera_inner_dimensions.y/2, external_width+camera_inner_dimensions.z]) rotate([-90,0,-90])
123children();}
124
125module lens_cut_out(){
126translate([-lens_holder_external_cyl_height, 0,0])
127lens_transform()
128 cylinder(external_width/2-lens_thickness/2+lens_holder_external_cyl_height,lens_diameter/2-lens_holder_edge_thickness,lens_diameter/2, $fn=cyl_resolution);
129 translate([external_width/2+lens_thickness/2, 0,0]) lens_transform()
130 cylinder(external_width/2-lens_thickness/2+lens_holder_external_cyl_height,lens_diameter/2,lens_diameter/2-lens_holder_edge_thickness, $fn=cyl_resolution);
131 translate([external_width/2-lens_thickness/2, 0,0]) lens_transform()
132 cylinder(lens_thickness,lens_diameter/2,lens_diameter/2, $fn=cyl_resolution);
133}
134
135module half_cylinder(h,r1,r2){
136difference(){cylinder(h,r1,r2, $fn=cyl_resolution);
137translate([-r1-r2,(-r1-r2)*2,-0.5])
138cube([(r1+r2)*2,(r1+r2)*2,h+1]);
139}
140}
141
142module part_connection(){
143ext_dist = (external_width-part_connection_thickness_post_tolerance_adjustment)/2;
144difference(){
145translate([ext_dist,ext_dist, camera_box_dimensions.z])
146rounded_cuboid([camera_box_dimensions.x-external_width+part_connection_thickness_post_tolerance_adjustment,camera_box_dimensions.y-external_width+part_connection_thickness_post_tolerance_adjustment,part_connection_depth],[FRONT+LEFT,FRONT+RIGHT,BACK+LEFT,BACK+RIGHT],corner_rounding);
147translate([ext_dist+part_connection_thickness_post_tolerance_adjustment, ext_dist+part_connection_thickness_post_tolerance_adjustment, camera_box_dimensions.z])
148rounded_cuboid([camera_box_dimensions.x-external_width-part_connection_thickness_post_tolerance_adjustment,camera_box_dimensions.y-external_width-part_connection_thickness_post_tolerance_adjustment,part_connection_depth],[FRONT+LEFT,FRONT+RIGHT,BACK+LEFT,BACK+RIGHT],corner_rounding);
149translate([ext_dist,lens_diameter/2,lens_diameter/2]) lens_transform()
150cube([lens_diameter,lens_diameter,part_connection_thickness_post_tolerance_adjustment]);
151}}
152
153
154
155difference(){
156 union(){
157 rounded_cuboid(camera_box_dimensions,[BOTTOM+FRONT,BOTTOM+RIGHT,BOTTOM+LEFT,BOTTOM+BACK,FRONT+LEFT,FRONT+RIGHT,BACK+LEFT,BACK+RIGHT],corner_rounding);
158 // Connection
159 if (!top_part){
160 part_connection();
161 }
162 }
163 if (top_part){
164 translate([0,0,-part_connection_depth])
165 part_connection();
166 }
167 // Internal cavity
168translate([external_width, external_width, external_width]) cube(camera_inner_dimensions);
169 // Lens
170 lens_cut_out();
171 // Image planes
172for(i = [0:len(object_distances)-1]){
173 distance = object_distances[i];
174 image_plane(distance,lens_focal_length,i);
175 }
176 // Camera specs
177 // f-stop
178 translate([spec_text_padding,external_width/4,spec_text_padding]) rotate([90,0,0])
179 linear_extrude(external_width/4)
180 text(format("f/ {:.2f}", [lens_focal_length/(lens_diameter-2*lens_holder_edge_thickness)]),spec_text_size, font = text_font, $fn=txt_resolution);
181 // focal length
182 translate([spec_text_padding,external_width/4,spec_text_padding+spec_text_size*1.5]) rotate([90,0,0])
183 linear_extrude(external_width/4)
184 text(format("f: {:3d} mm", [lens_focal_length]),spec_text_size, font = text_font, $fn=txt_resolution);
185 // Camera name
186 translate([spec_text_padding,external_width/4,spec_text_padding+spec_text_size*1.5*2]) rotate([90,0,0])
187 linear_extrude(external_width/4)
188 text(camera_name, spec_text_size, font = jp_font, $fn=txt_resolution);
189}
190
191// Lens holder
192difference(){
193union(){
194 translate([-lens_holder_external_cyl_height,0,0]) lens_transform() half_cylinder(lens_holder_external_cyl_height,lens_diameter/2,lens_diameter/2+lens_holder_outwards_radius);
195 translate([external_width,0,0]) lens_transform() half_cylinder(lens_holder_external_cyl_height,lens_diameter/2+lens_holder_outwards_radius,lens_diameter/2);
196 }
197 lens_cut_out();
198}