misc
This commit is contained in:
@@ -0,0 +1,298 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
Flock-style Camera Prop — non-functional film/political-commentary prop
|
||||||
|
Approximate exterior geometry derived from publicly visible product photos.
|
||||||
|
Not dimensionally exact and not suitable for surveillance, mounting in traffic,
|
||||||
|
impersonating an operational device, or outdoor deployment.
|
||||||
|
|
||||||
|
Open in OpenSCAD. Set PART below, press F6, then Export STL.
|
||||||
|
|
||||||
|
PART options:
|
||||||
|
"assembly"
|
||||||
|
"front_shell"
|
||||||
|
"rear_shell"
|
||||||
|
"lens_insert"
|
||||||
|
"lower_badge"
|
||||||
|
"pole_bracket"
|
||||||
|
"solar_panel"
|
||||||
|
"solar_frame"
|
||||||
|
*/
|
||||||
|
|
||||||
|
PART = "assembly";
|
||||||
|
|
||||||
|
// ---------- Main dimensions, mm ----------
|
||||||
|
body_w = 112;
|
||||||
|
body_h = 245;
|
||||||
|
body_d = 70;
|
||||||
|
corner_r = 28;
|
||||||
|
shell_wall = 2.6;
|
||||||
|
split_gap = 0.35;
|
||||||
|
|
||||||
|
hood_depth = 28;
|
||||||
|
hood_overhang = 10;
|
||||||
|
|
||||||
|
lens_outer_d = 51;
|
||||||
|
lens_recess_d = 4;
|
||||||
|
lens_center_z = 72;
|
||||||
|
|
||||||
|
badge_d = 28;
|
||||||
|
badge_center_z = -74;
|
||||||
|
|
||||||
|
pole_d = 60.3; // nominal 2-inch schedule-40 PVC outside diameter
|
||||||
|
bracket_wall = 5;
|
||||||
|
bracket_width = 72;
|
||||||
|
bracket_depth = 46;
|
||||||
|
|
||||||
|
panel_w = 310;
|
||||||
|
panel_h = 205;
|
||||||
|
panel_t = 10;
|
||||||
|
panel_frame = 9;
|
||||||
|
|
||||||
|
$fn = 72;
|
||||||
|
|
||||||
|
// ---------- Helpers ----------
|
||||||
|
module rounded_prism(w,h,d,r){
|
||||||
|
linear_extrude(height=d, center=true)
|
||||||
|
hull()
|
||||||
|
for (x=[-w/2+r,w/2-r], y=[-h/2+r,h/2-r])
|
||||||
|
translate([x,y]) circle(r=r);
|
||||||
|
}
|
||||||
|
|
||||||
|
module soft_body(d=body_d){
|
||||||
|
// Subtle taper: narrower near bottom.
|
||||||
|
hull(){
|
||||||
|
translate([0, 20, 0]) rounded_prism(body_w, body_h-40, d, corner_r);
|
||||||
|
translate([0,-body_h/2+29,0])
|
||||||
|
rounded_prism(body_w-16, 58, d, 24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module face_features(depth=8){
|
||||||
|
// Lens recess
|
||||||
|
translate([0, body_h/2-lens_center_z, body_d/2-depth/2+0.1])
|
||||||
|
cylinder(d=lens_outer_d+2, h=depth, center=true);
|
||||||
|
|
||||||
|
// Lower circular badge recess
|
||||||
|
translate([0, body_h/2-(body_h/2-badge_center_z), body_d/2-depth/2+0.1])
|
||||||
|
cylinder(d=badge_d+2, h=depth, center=true);
|
||||||
|
|
||||||
|
// Small status aperture under lens
|
||||||
|
translate([0, body_h/2-lens_center_z-39, body_d/2-depth/2+0.1])
|
||||||
|
cylinder(d=9, h=depth, center=true);
|
||||||
|
}
|
||||||
|
|
||||||
|
module camera_outer(){
|
||||||
|
difference(){
|
||||||
|
soft_body();
|
||||||
|
|
||||||
|
// Flatten rear slightly
|
||||||
|
translate([0,0,-body_d/2-10])
|
||||||
|
cube([body_w+30, body_h+30, 20], center=true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flock-like asymmetric hood around upper face
|
||||||
|
translate([0, body_h/2-lens_center_z+5, body_d/2+hood_depth/2-2])
|
||||||
|
difference(){
|
||||||
|
rounded_prism(body_w+2*hood_overhang, 104, hood_depth, 25);
|
||||||
|
translate([0,-5,-hood_depth/2+5])
|
||||||
|
rounded_prism(body_w-5, 92, hood_depth+4, 20);
|
||||||
|
// Open the lower hood edge
|
||||||
|
translate([0,-56,0])
|
||||||
|
cube([body_w+40,38,hood_depth+10],center=true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module interior_void(){
|
||||||
|
scale([(body_w-2*shell_wall)/body_w,
|
||||||
|
(body_h-2*shell_wall)/body_h,
|
||||||
|
(body_d-2*shell_wall)/body_d])
|
||||||
|
soft_body(body_d);
|
||||||
|
}
|
||||||
|
|
||||||
|
module alignment_pins(front=true){
|
||||||
|
z = front ? -body_d/2+9 : body_d/2-9;
|
||||||
|
for (x=[-36,36], y=[-75,75])
|
||||||
|
translate([x,y,z])
|
||||||
|
cylinder(d=5.2, h=8, center=true);
|
||||||
|
}
|
||||||
|
|
||||||
|
module alignment_holes(front=true){
|
||||||
|
z = front ? -body_d/2+9 : body_d/2-9;
|
||||||
|
for (x=[-36,36], y=[-75,75])
|
||||||
|
translate([x,y,z])
|
||||||
|
cylinder(d=5.8, h=10, center=true);
|
||||||
|
}
|
||||||
|
|
||||||
|
module front_shell(){
|
||||||
|
difference(){
|
||||||
|
intersection(){
|
||||||
|
camera_outer();
|
||||||
|
translate([0,0,body_d/4])
|
||||||
|
cube([body_w+80, body_h+80, body_d/2-split_gap], center=true);
|
||||||
|
}
|
||||||
|
interior_void();
|
||||||
|
face_features(12);
|
||||||
|
alignment_holes(false);
|
||||||
|
|
||||||
|
// Optional 3 mm screw clearance through side tabs
|
||||||
|
for (y=[-75,75])
|
||||||
|
translate([0,y,body_d/2-8])
|
||||||
|
rotate([0,90,0]) cylinder(d=3.4,h=body_w+30,center=true);
|
||||||
|
}
|
||||||
|
// Rearward registration pins
|
||||||
|
alignment_pins(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
module rear_shell(){
|
||||||
|
difference(){
|
||||||
|
intersection(){
|
||||||
|
camera_outer();
|
||||||
|
translate([0,0,-body_d/4])
|
||||||
|
cube([body_w+80, body_h+80, body_d/2-split_gap], center=true);
|
||||||
|
}
|
||||||
|
interior_void();
|
||||||
|
alignment_holes(true);
|
||||||
|
|
||||||
|
// Flat mounting slots
|
||||||
|
for (y=[-60,25])
|
||||||
|
translate([0,y,-body_d/2+3])
|
||||||
|
cube([42,7,10],center=true);
|
||||||
|
|
||||||
|
for (y=[-75,75])
|
||||||
|
translate([0,y,-body_d/2+8])
|
||||||
|
rotate([0,90,0]) cylinder(d=3.4,h=body_w+30,center=true);
|
||||||
|
}
|
||||||
|
alignment_pins(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
module lens_insert(){
|
||||||
|
difference(){
|
||||||
|
union(){
|
||||||
|
cylinder(d=lens_outer_d, h=5, center=true);
|
||||||
|
translate([0,0,2.8])
|
||||||
|
cylinder(d1=lens_outer_d-7,d2=lens_outer_d-12,h=4,center=true);
|
||||||
|
}
|
||||||
|
// Central camera opening
|
||||||
|
cylinder(d=18,h=12,center=true);
|
||||||
|
// Ring of fake IR emitters
|
||||||
|
for(a=[0:45:315])
|
||||||
|
rotate([0,0,a]) translate([15.5,0,0])
|
||||||
|
cylinder(d=6.2,h=12,center=true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module lower_badge(){
|
||||||
|
union(){
|
||||||
|
cylinder(d=badge_d,h=3.2,center=true);
|
||||||
|
translate([0,0,1.8])
|
||||||
|
linear_extrude(height=1.2,center=true)
|
||||||
|
text("F",size=15,halign="center",valign="center",
|
||||||
|
font="Liberation Sans:style=Bold");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module wordmark(){
|
||||||
|
// Separate raised lettering for gluing to the side or panel.
|
||||||
|
linear_extrude(height=1.6)
|
||||||
|
text("FLOCK",size=17,halign="center",valign="center",
|
||||||
|
font="Liberation Sans:style=Bold",spacing=0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
module pole_bracket(){
|
||||||
|
difference(){
|
||||||
|
union(){
|
||||||
|
// Camera-side mounting plate
|
||||||
|
translate([0,0,0])
|
||||||
|
rounded_prism(bracket_width, bracket_depth, bracket_wall, 7);
|
||||||
|
|
||||||
|
// Curved pole saddle
|
||||||
|
translate([0,0,-18])
|
||||||
|
rotate([90,0,0])
|
||||||
|
difference(){
|
||||||
|
cylinder(d=pole_d+2*bracket_wall,h=bracket_depth,center=true);
|
||||||
|
cylinder(d=pole_d+0.8,h=bracket_depth+3,center=true);
|
||||||
|
translate([0,-(pole_d+20)/2,0])
|
||||||
|
cube([pole_d+30,pole_d+30,bracket_depth+10],center=true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Two gussets
|
||||||
|
for(x=[-25,25])
|
||||||
|
hull(){
|
||||||
|
translate([x,0,-2]) cube([7,bracket_depth-8,4],center=true);
|
||||||
|
translate([x,0,-20]) cube([7,bracket_depth-8,4],center=true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Camera attachment holes
|
||||||
|
for (x=[-22,22], y=[-12,12])
|
||||||
|
translate([x,y,0])
|
||||||
|
cylinder(d=4.2,h=bracket_wall+8,center=true);
|
||||||
|
|
||||||
|
// Slots for stainless hose clamps
|
||||||
|
for (z=[-12,-28])
|
||||||
|
translate([0,0,z])
|
||||||
|
rotate([90,0,0])
|
||||||
|
cube([58,5,bracket_depth+20],center=true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module solar_panel(){
|
||||||
|
difference(){
|
||||||
|
rounded_prism(panel_w,panel_h,panel_t,7);
|
||||||
|
translate([0,0,panel_t/2-2])
|
||||||
|
rounded_prism(panel_w-2*panel_frame,
|
||||||
|
panel_h-2*panel_frame,5,3);
|
||||||
|
}
|
||||||
|
// Simple fake cell grid
|
||||||
|
translate([0,0,panel_t/2+0.5])
|
||||||
|
for(x=[-panel_w/2+panel_frame+18:32:panel_w/2-panel_frame-18])
|
||||||
|
cube([1.2,panel_h-2*panel_frame-8,1],center=true);
|
||||||
|
translate([0,0,panel_t/2+0.5])
|
||||||
|
for(y=[-panel_h/2+panel_frame+18:30:panel_h/2-panel_frame-18])
|
||||||
|
cube([panel_w-2*panel_frame-8,1.2,1],center=true);
|
||||||
|
}
|
||||||
|
|
||||||
|
module solar_frame(){
|
||||||
|
// Compact printable tilt bracket, intended as a visual prop only.
|
||||||
|
difference(){
|
||||||
|
union(){
|
||||||
|
cube([95,48,7],center=true);
|
||||||
|
for(x=[-34,34])
|
||||||
|
translate([x,0,-26])
|
||||||
|
cube([9,42,52],center=true);
|
||||||
|
}
|
||||||
|
for(x=[-34,34])
|
||||||
|
translate([x,0,-26])
|
||||||
|
rotate([90,0,0]) cylinder(d=6.4,h=60,center=true);
|
||||||
|
for(x=[-34,34], y=[-15,15])
|
||||||
|
translate([x,y,0]) cylinder(d=4.5,h=15,center=true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module assembly(){
|
||||||
|
// Pole is a preview only; do not export it.
|
||||||
|
color("dimgray")
|
||||||
|
translate([0,0,-160]) cylinder(d=pole_d,h=650,center=true);
|
||||||
|
|
||||||
|
color("black")
|
||||||
|
translate([0,-40,45]) rotate([90,0,0]) camera_outer();
|
||||||
|
|
||||||
|
color("gray")
|
||||||
|
translate([0,-10,35]) rotate([90,0,0]) pole_bracket();
|
||||||
|
|
||||||
|
color("midnightblue")
|
||||||
|
translate([0,5,250]) rotate([65,0,0]) solar_panel();
|
||||||
|
|
||||||
|
color("gray")
|
||||||
|
translate([0,0,190]) rotate([0,0,0]) solar_frame();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- Part selector ----------
|
||||||
|
if (PART == "assembly") assembly();
|
||||||
|
else if (PART == "front_shell") rotate([0,180,0]) front_shell();
|
||||||
|
else if (PART == "rear_shell") rear_shell();
|
||||||
|
else if (PART == "lens_insert") lens_insert();
|
||||||
|
else if (PART == "lower_badge") lower_badge();
|
||||||
|
else if (PART == "pole_bracket") pole_bracket();
|
||||||
|
else if (PART == "solar_panel") solar_panel();
|
||||||
|
else if (PART == "solar_frame") solar_frame();
|
||||||
|
else if (PART == "wordmark") wordmark();
|
||||||
Reference in New Issue
Block a user