From b6f5921359c8b28bcd227d671d0fe8ea28855e41 Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 20 Jul 2026 23:33:02 +0000 Subject: [PATCH] misc --- flock_camera_prop.scad | 298 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 flock_camera_prop.scad diff --git a/flock_camera_prop.scad b/flock_camera_prop.scad new file mode 100644 index 0000000..b3439c8 --- /dev/null +++ b/flock_camera_prop.scad @@ -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();