diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8837fe9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+.DS_Store
+
+
+docgen
+
+*.swp
diff --git a/.openscad_docsgen_rc b/.openscad_docsgen_rc
new file mode 100644
index 0000000..bb17041
--- /dev/null
+++ b/.openscad_docsgen_rc
@@ -0,0 +1,4 @@
+DocsDirectory: doc/
+TargetProfile: githubwiki
+ProjectName: Battery Holder
+GenerateDocs: Files, TOC, Index, Topics, CheatSheet, Sidebar
diff --git a/Readme.md b/Readme.md
index 3de1bed..ffca2d5 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,5 +1,18 @@
# Battery Holder
+Here's the documentation:
+
+
+
+
+```include doc/_Sidebar.md```
+
+
+- [Battery Holder](doc/battery-holder.scad.md)
+- [Table of Contents](doc/TOC.md)
+- [Alphabetical Index](doc/AlphaIndex.md)
+- [Topics](doc/Topics.md)
+
## Connection
Use 4mm nickel strip available at Aliexpress [Nickel Plated Steel Strip Connector](https://www.aliexpress.com/item/1005005127569726.html) R$9.12 (0.1x4mm (50pcs))
@@ -12,4 +25,11 @@ Use 4mm nickel strip available at Aliexpress [Nickel Plated Steel Strip Connecto
+## Generate Doc
+
+```bash
+openscad-docsgen -m *.scad
+```
+
+
diff --git a/battery-holder.json b/battery-holder.json
new file mode 100644
index 0000000..d30e6b8
--- /dev/null
+++ b/battery-holder.json
@@ -0,0 +1,6 @@
+{
+ "fileFormatVersion": "1",
+ "parameterSets": {
+ "New set 1": ""
+ }
+}
diff --git a/battery-holder.scad b/battery-holder.scad
new file mode 100644
index 0000000..6164115
--- /dev/null
+++ b/battery-holder.scad
@@ -0,0 +1,648 @@
+
+//////////////////////////////////////////////////////////////////////
+// LibFile: battery-holder.scad
+// Functions and modules for creating battery holders
+// Includes:
+// include
+// FileGroup: Batteries
+// FileSummary: This file generates battery holders for arbitrary cylindrical sizes..
+//////////////////////////////////////////////////////////////////////
+
+
+/* [Battery Holder specs] */
+
+// Battery Type
+battery_type="AAA"; // [None,AA,AAA,18650*,18650P,C,D,CR123A]
+
+// Number of battery slots to generate
+battery_slots = 1;
+
+
+
+strip_holes = true;
+wire_channels = true;
+
+battery_symbol = true;
+poles_symbol = true;
+
+contact_bulges = true;
+
+
+// Section: Batteries
+
+
+
+//////////////////////////////////////////////////////////////////////////////////////
+///
+/// FLEXBATTER: Flexing battery holder with built-in spring
+///
+/// This file generates battery holders for arbitrary cylindrical sizes.
+/// The number of batteries and their size is fully parametrized.
+///
+/// The usual metallic spring at the minus pole is replaced by a
+/// flexible printed spring, which is pressing the contacts firmly to
+/// the battery.
+///
+/// The contacts for the plus and minus pole can easily be made by
+/// a few windings of the uninsulated ends of the connecting wires.
+/// Each battery compartment contains are several holes to the outside
+/// as well ad to the neighboring compartments, through which connecting
+/// wires can be passed for easy configuring of parallel, serial or
+/// balanced-serial battery packs.
+///
+/// The preconfigured battery sizes are:
+/// AA, AAA, C, D, 18650(Li-Ion), 18650P(protected Li-Ion), CR123A(16340)
+///
+/// Given that the printed plastic spring needs to be flexible, ABS is the material
+/// of choice here.
+///
+//////////////////////////////////////////////////////////////////////////////////////
+///
+/// 2014-09-09 Heinz Spiess, Switzerland
+///
+/// released under Creative Commons - Attribution - Share Alike licence (CC BY-SA)
+//////////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+/**
+ * Generates a flexible battery holder for 'n' cylindrical batteries with complex
+ * features like wire channels, screw holes, and contact points.
+ *
+ * @param {number} [n=1] - Number of battery slots to generate.
+ * @param {number} [l=65] - Length of each battery slot.
+ * @param {number} [d=18] - Diameter of the battery.
+ * @param {number} [hf=0.75] - Height factor for the battery holder relative to the battery diameter.
+ * @param {number} [r=4] - Radius for the plastic spring (calculated inside).
+ * @param {number} [shd=3] - Screw hole diameter.
+ * @param {number} [eps=0.28] - Epsilon for additional cavity space around the battery.
+ * @param {number} [el=0] - Extra length for the spring.
+ * @param {array} [xchan=[1/4,3/4]] - Relative positions for traversal wire channels.
+ * @param {number} [$fn=24] - Number of fragments for circles/cylinders for smoother curves.
+ *
+ * @description
+ * This module constructs a battery holder with:
+ * - A main body with a cavity for each battery.
+ * - Plastic springs for negative pole contact using the `sline` module.
+ * - Contact points (+ and -) for electrical connectivity.
+ * - Wire channels for routing electrical connections.
+ * - Screw holes for mounting.
+ * - Engraving for battery orientation symbols.
+ *
+ * Key Features:
+ * - **Chamfering** of edges for a more finished look (`chamfered_cube`).
+ * - **Deepening** for better grip on batteries (optional).
+ * - **Wire Channels**: Both longitudinal and transversal for wire routing.
+ * - **Spring**: Utilizes `sline` to create a complex spring shape for battery contact.
+ * - **Contact Points**: Bulges for ensuring good electrical contact.
+ * - **Engraving**: Symbols to indicate battery polarity.
+ *
+ * Note:
+ * - The module uses recursion implicitly through the `for` loop to create multiple slots.
+ * - Adjustments for middle separators are commented out but could be uncommented for use.
+ * - Some parameters like `r` are calculated within the module to adjust based on input.
+ */
+
+// Module: battery_case()
+// Usage:
+// battery_case(n);
+// Description:
+// Generates a flexible battery holder for 'n' cylindrical batteries with complex
+// features like wire channels, screw holes, and contact points.
+//
+// DefineHeader(BulletList): Parts
+// Parts: This module constructs a battery holder with
+// A main body with a cavity for each battery.
+// Plastic springs for negative pole contact using the `sline` module.
+// Contact points (+ and -) for electrical connectivity.
+// Wire channels for routing electrical connections.
+// Screw holes for mounting.
+// Engraving for battery orientation symbols.
+//
+// DefineHeader(BulletList): Key Features
+// Key Features:
+// **Chamfering** of edges for a more finished look (`chamfered_cube`).
+// **Deepening** for better grip on batteries (optional).
+// **Wire Channels**: Both longitudinal and transversal for wire routing.
+// **Spring**: Utilizes `sline` to create a complex spring shape for battery contact.
+// **Contact Points**: Bulges for ensuring good electrical contact.
+// **Engraving**: Symbols to indicate battery polarity.
+//
+// Arguments:
+// n = Number of battery slots to generate. (default: 1)
+// l = Length of each battery slot. (default: 65)
+// d = Diameter of the battery. (default: 18)
+// hf = Height factor for the battery holder relative to the battery diameter. (default: 0.75)
+// r = Radius for the plastic spring (calculated inside). (default: 4)
+// shd = Screw hole diameter. (default: 3)
+// eps = Epsilon for additional cavity space around the battery. (default: 0.28)
+// el = Extra length for the spring. (default: 0)
+// xchan = Relative positions for traversal wire channels. (default: [1/4, 3/4])
+// $fn = Number of fragments for circles/cylinders for smoother curves. (default: 24)
+//
+// Example: Simple Case
+// battery_case(n=1,l=46.1,d=10.45,hf=0.84,shd=2,el=1,xchan=[0.5],eps=0);
+
+module battery_case(n=1,l=65,d=18,hf=0.75,r=4,shd=3,eps=0.28,el=0,xchan=[1/4,3/4],$fn=24){
+ ew=0.56; // extrusion width
+ eh=0.25; // extrusion height
+ w = 4*ew; // case wall thickness
+ ws = 2*ew; // spring wall thickness
+ ch = w-ws; // edge chamfering
+ deepen=0; //deepening for side grip of batteries
+ //el = 0; // extra length in spring
+ //eps = 0.28;
+ //shd = 3; // screw hole diameter
+ //hf=0.75;
+ //xchan=[1/4,3/4]; // relative position of traversal wire channels
+
+ r = d/5+2*ws; // linear spring length (depends on sline() call!)
+
+ for(i=[0:n-1])translate([0,i*(d+w+ws),0]){ // generate n battery cases
+ difference(){
+
+ union(){
+ difference(){
+ // main body
+ translate([0,-w-d/2,0])
+ chamfered_cube([l+w,d+2*w+(i0)translate([-1,0,d/2+w])rotate([0,90,0])cylinder(r=d/2+eps,h=l+1);
+ }
+
+ // plastic spring for minus pole
+ for(sy=[-1,1])scale([1,sy,1]) //assign(D=d+2*w-2*ws-0.7)
+ {
+ D=d+2*w-2*ws-0.7;
+ translate([ch,d/2+w-ws/2,0])rotate(-90)
+ //sline([90,0,120,90,120,90,0],[d/8+2,d/6,d/8-1,d/8,-d/8,d/8,d/2],0,ws,hf*d+w);
+ sline([0,180,0,180,0,-180,0,90,0],[r+ch+el,D/4,el,D/12,el/2,D/12,1+el/2,D/5,D/3],0,ws,hf*d+w);
+
+
+ }
+ }
+
+ // lower and upper holes for contacts
+ if (strip_holes)
+ stripHoles(case_wall_thickness = w,spring_wall_thickness=ws,battery_diam = d,slot_length=l);
+ /*
+ for(z=[-2*ws,2*ws])
+ translate([-2*ws,-w,w-ws/2+d/2+z])
+ cube([l+2*w+2,2*w,ws]);
+ */
+
+
+ // Wire channels
+ if (wire_channels)
+ wireChannels(
+ case_wall_thickness = w,
+ spring_wall_thickness = ws,
+ slot_length = l,
+ battery_diam = d,
+ spring_radius = r,
+ channel_pos = xchan,
+ extrusion_height = eh
+ );
+
+
+ // grip deepening
+ if (deepen>0)
+ translate([w+l/2-0.5,-d/2-w-0.01,w+d+l])
+ rotate([-90,0,0]){
+ cylinder(r=l+deepen*d,h=d+2*w+2*ws+2,$fn=72);
+ if(i==0)cylinder(r1=l+deepen*d+ch,r2=l+deepen*d,h=ch+0.02,$fn=72);
+ if(i==n-1)translate([0,0,d+2*w-ch])cylinder(r2=l+deepen*d+ch,r1=l+deepen*d,h=ch+0.02,$fn=72);
+
+ }
+ // conical screw holes in corners
+ for(x=[7+shd,l-2*shd])for(y=[-d/2+shd,d/2-shd])
+ translate([x,y,-1]){
+ cylinder(r=shd/2,h=w+2);
+ translate([0,0,w-shd/2+1])cylinder(r1=shd/2,r2=shd,h=shd/2+0.01);
+ }
+
+ // holes for wires passing inside
+ for(sy=[-1,1])scale([1,sy,1]){
+ translate([l-1,-d/2,w])cube([w+2,2,2]);
+ for(x=[3,l-7])translate([x,-d/2-w-ws-1,w])cube([3,w+ws+3,2]);
+ translate([3,-d/2+w/2-0.75,-1])cube([3,1.5,w+2]);
+ translate([-0.5,-d/2+w/2,0])rotate([0,90,0])cylinder(r=w/2,h=6.5,$fn=5);
+ }
+
+
+ if (battery_symbol)
+ batterySymbol(
+ case_wall_thickness = w,
+ battery_diam = d,
+ slot_length = l,
+ extrusion_height = eh,
+ );
+
+ // engrave plus symbol
+ //assign(sy=(l>12*shd)?1:-1)
+ if (poles_symbol)
+ polesSymbol(
+ case_wall_thickness = w,
+ battery_diam = d,
+ slot_length = l,
+ screw_hole_diameter = shd,
+ extrusion_height = eh,
+
+ );
+
+
+
+
+ if (contact_bulges)
+ contactBulges(
+ case_wall_thickness = w,
+ spring_wall_thickness = ws,
+ battery_diam = d,
+ slot_length = l,
+ spring_extra_length = el,
+ vertical_bulge = true
+
+
+ );
+
+ }
+}
+
+module contactBulges( case_wall_thickness, spring_wall_thickness, battery_diam,slot_length, spring_extra_length, vertical_bulge ) {
+ // horizontal contact bulges (+ and - pole)
+ for( x = [ -0.3,slot_length ] )
+ hull()
+ for(y=[
+ -3+spring_extra_length,
+ +3-spring_extra_length
+ ])
+ translate([
+ x,
+ y,
+ case_wall_thickness + battery_diam / 2
+ ])
+ sphere(r=spring_wall_thickness);
+
+ // vertical contact bulge (+ pole only)
+
+ if (vertical_bulge) hull()
+ for(z=[-3+spring_extra_length,3-spring_extra_length])
+ for(x=[0,case_wall_thickness-spring_wall_thickness])
+ translate([ slot_length + x,0,case_wall_thickness + battery_diam / 2 + z])
+ sphere(r=spring_wall_thickness);
+}
+
+
+/**
+ * Engraves a simple battery symbol on the side of a battery case.
+ *
+ * @param {number} case_wall_thickness - Thickness of the case walls.
+ * @param {number} slot_length - Length of the battery slot.
+ * @param {number} battery_diam - Diameter of the battery, used for symbol size and positioning.
+ * @param {number} extrusion_height - Height of the extrusion for the symbol.
+ *
+ * @description
+ * This module creates a visual representation of a battery by engraving:
+ * - A rectangle representing the battery body.
+ * - A smaller rectangle at one end to symbolize the positive terminal or battery cap.
+ *
+ * The positioning of the symbol is:
+ * - Centered along the length of the slot with an offset for the case wall thickness.
+ * - Placed slightly above the bottom of the battery diameter for visibility.
+ *
+ * Notes:
+ * - Both rectangles are centered (`true` in the `cube` calls) for symmetrical engraving.
+ * - The dimensions of the symbol parts are proportional to `slot_length` and `battery_diam`, ensuring the symbol scales appropriately with different battery sizes.
+ */
+module batterySymbol( case_wall_thickness, slot_length, battery_diam,extrusion_height ) {
+ translate(
+ [
+ case_wall_thickness+slot_length/2,
+ battery_diam/4+1,
+ case_wall_thickness
+ ])
+ cube(
+ [
+ slot_length/5,
+ battery_diam/4.5,
+ 4*extrusion_height
+ ],true);
+ translate(
+ [
+ case_wall_thickness+slot_length/2+slot_length/10,
+ battery_diam/4+1,
+ case_wall_thickness
+ ])
+ cube([battery_diam/7,battery_diam/10,4*extrusion_height],true);
+};
+
+
+/**
+ * Creates strip-like holes on both ends of the battery slot for contact purposes.
+ *
+ * @param {number} case_wall_thickness - Thickness of the case walls.
+ * @param {number} spring_wall_thickness - Thickness of the spring or contact wall.
+ * @param {number} battery_diam - Diameter of the battery, used for positioning.
+ * @param {number} slot_length - Length of the battery slot.
+ *
+ * @description
+ * This module generates two rectangular holes at each end of the battery compartment:
+ * - These holes are designed to facilitate contact between the battery terminals and external connectors or springs.
+ * - They are positioned at the top and bottom of the battery diameter area, relative to the case's interior.
+ *
+ * Key features:
+ * - **Positioning**: Holes are placed at the ends of the battery slot, with one at `z = -2*spring_wall_thickness` and another at `z = 2*spring_wall_thickness` above and below the battery's center line.
+ * - **Dimensions**:
+ * - Width spans the length of the slot plus additional space for case walls.
+ * - Height is twice the case wall thickness, ensuring enough room for contact.
+ * - Depth (or height in OpenSCAD's coordinate system) is equal to the spring wall thickness.
+ *
+ * Note:
+ * - The module uses a loop to create both holes symmetrically around the battery's center.
+ * - The exact positioning ensures these holes do not interfere with the battery's body but are accessible for contacts.
+ */
+module stripHoles( case_wall_thickness,spring_wall_thickness,battery_diam,slot_length ) {
+ for( z = [-2*spring_wall_thickness,2*spring_wall_thickness] )
+ translate([
+ -2*spring_wall_thickness,
+ -case_wall_thickness,
+ case_wall_thickness-spring_wall_thickness/2+battery_diam/2+z
+ ])
+ cube([
+ slot_length+2*case_wall_thickness+2,
+ 2*case_wall_thickness,
+ spring_wall_thickness
+ ]);
+}
+
+
+/**
+ * Creates wire channels in the battery case to route electrical connections.
+ *
+ * @param {number} case_wall_thickness - Thickness of the case walls.
+ * @param {number} spring_wall_thickness - Thickness of the spring or contact wall.
+ * @param {number} slot_length - Length of the battery slot.
+ * @param {number} battery_diam - Diameter of the battery.
+ * @param {number} spring_radius - Radius of the spring or additional space for wiring.
+ * @param {array} channel_pos - Array of relative positions for transversal channels along the slot's length.
+ * @param {number} extrusion_height - Height of the extrusion for positioning the wire channel.
+ *
+ * @description
+ * This module constructs wire channels for guiding wires:
+ * - **Longitudinal Channel**: Runs along the bottom of the battery case for wires connecting multiple batteries or exiting the case.
+ * - **Transversal Channels**: Provides paths for wires to cross the width of the case, positioned according to `channel_pos`.
+ *
+ * Key Features:
+ * - **Longitudinal Channel**:
+ * - Positioned at the base of the case, extending the length of the slot plus additional space.
+ * - Rotated to lie horizontally under the battery for ease of wire routing.
+ * - **Transversal Channels**:
+ * - Created at specified positions along the slot's length (`channel_pos`).
+ * - Run perpendicular to the longitudinal channel, allowing for cross-connection of wires or external connections.
+ *
+ * Notes:
+ * - Cylinders are used to form these channels, with $fn determining the number of facets for a smoother or more angular appearance.
+ * - The longitudinal channel's height includes the slot length plus extra space for routing flexibility.
+ * - Transversal channels are placed just below the battery's diameter to avoid interfering with battery placement.
+ */
+module wireChannels( case_wall_thickness, spring_wall_thickness,slot_length, battery_diam, spring_radius, channel_pos,extrusion_height ) {
+ // longitudinal bottom wire channel
+ translate([-2*spring_wall_thickness,0,0])
+ rotate([0,90,0])
+ cylinder(
+ r=case_wall_thickness/2,
+ h=slot_length + case_wall_thickness + 2 +spring_radius,
+ $fn=5
+ );
+
+ // traversal bottom wire channels
+ for ( x = slot_length * channel_pos )
+ translate([x,-battery_diam/2-case_wall_thickness-1,extrusion_height])
+ rotate([-90,0,0])
+ cylinder(
+ r = case_wall_thickness/2,
+ h = battery_diam +2 * case_wall_thickness + spring_wall_thickness+2,
+ $fn=6
+ );
+
+}
+
+
+/**
+ * Engraves symbols for battery polarity (+ and -) on the side of the battery case.
+ *
+ * @param {number} case_wall_thickness - Thickness of the case walls.
+ * @param {number} slot_length - Length of the battery slot.
+ * @param {number} screw_hole_diameter - Diameter of the screw holes, used to determine symbol orientation.
+ * @param {number} battery_diam - Diameter of the battery, used for symbol size and positioning.
+ * @param {number} extrusion_height - Height of the extrusion for the symbols.
+ *
+ * @description
+ * This module adds visual indicators for battery polarity:
+ * - For longer batteries, symbols are placed horizontally.
+ * - For shorter batteries, symbols are adjusted vertically to fit within the available space.
+ *
+ * The symbols are:
+ * - **Plus (+)**: Created by intersecting two cubes to form a cross.
+ * - **Minus (-)**: A single cube representing a line.
+ *
+ * The placement and orientation of these symbols depend on:
+ * - `slot_length` compared to `screw_hole_diameter` to determine if the battery is considered 'short' or 'long'.
+ * - `sy` (scale y) determines whether symbols are placed on the top or side based on the comparison.
+ *
+ * Note:
+ * - The `echo` statement is included for debugging or informational purposes, to verify the battery diameter.
+ * - The commented-out code for middle separators suggests this module might have been part of a larger context where such adjustments were necessary but have since been removed or commented out.
+ */
+module polesSymbol( case_wall_thickness, slot_length,screw_hole_diameter, battery_diam, extrusion_height ) {
+ sy=( slot_length > 12 * screw_hole_diameter)?1:-1;
+ echo ("battery_diam",battery_diam);
+ { // for short batteries +- on the side
+ translate([
+ case_wall_thickness+slot_length/2+slot_length/(sy>0?5:10),
+ sy*(battery_diam/4+1),
+ case_wall_thickness
+ ]){
+ cube([1,battery_diam/4,4*extrusion_height],true);
+ cube([battery_diam/4,1,4*extrusion_height],true);
+ }
+ // engrave minus symbol
+ translate([
+ case_wall_thickness+slot_length/2-slot_length/(sy>0?5:10),
+ sy*(battery_diam/4+1),
+ case_wall_thickness
+ ])
+ cube([1,battery_diam/4,4*extrusion_height],true);
+ }
+
+ //correction for middle separators
+ //if(i0) translate([-d,-d/2-w-0.1,-1])cube([d,ws/2+0.1,d+2]);
+ //else translate([1,-d/2-w,-0.01])cylinder(r1=ch,r2=0,h=ch);
+ }
+}
+
+
+
+
+//caseCR123A(n=2);
+if (battery_type == "AAA") {
+ battery_case(n=battery_slots,l=46.1,d=10.45,hf=0.84,shd=2,el=1,xchan=[0.5],eps=0);
+} else if ( battery_type == "AA" ) {
+ battery_case(n=battery_slots,l=51.6,d=14.4,hf=0.80,shd=2.5,el=0.5,eps=0.28);
+} else if ( battery_type == "C" ) {
+ battery_case(n=battery_slots,l=51.6,d=26.4,hf=0.75,shd=3,eps=0.28);
+} else if ( battery_type == "D" ) {
+ battery_case(n=battery_slots,l=61.5,d=34.0,hf=0.75,shd=3,eps=0.28);
+} else if ( battery_type == "18650*" ) {
+ battery_case(n=battery_slots,l=65.5,d=18.4,hf=0.75,shd=3,eps=0.28);
+} else if ( battery_type == "18650P") {
+ battery_case(n=battery_slots,l=67.5,d=18.4,hf=0.75,shd=3,eps=0.28);
+} else if ( battery_type == "CR123A") {
+ battery_case(n=battery_slots,l=35.1,d=16.7,hf=0.75,shd=3,xchan=[0.5],eps=0.28);
+} else {
+ echo (str("ERROR for battery type :'",battery_type,"'"));
+}
+
+
+
+
+
+/**
+ * Creates an arc or a segment of a cylinder with inner and outer radii.
+ *
+ * @param {number} r1 - Inner radius of the arc.
+ * @param {number} r2 - Outer radius of the arc.
+ * @param {number} height - The height of the arc segment.
+ * @param {number} [a1=0] - Starting angle of the arc in degrees.
+ * 0 degrees is along the positive X-axis.
+ * @param {number} [a2=0] - Ending angle of the arc in degrees.
+ * Positive values are counter-clockwise from a1.
+ *
+ * @description
+ * This module constructs a 3D arc segment between two radii (`r1` and `r2`)
+ * from `a1` to `a2` degrees. The arc's height is determined by `height`.
+ * If `a2 - a1` is less than or equal to 180 degrees, it uses a direct
+ * difference operation to carve out the arc, otherwise, it uses intersection
+ * for arcs greater than 180 degrees to ensure proper geometry.
+ *
+ * The function uses cylinders and cubes for creating and subtracting shapes:
+ * - A larger cylinder (`r2`) forms the outer arc.
+ * - A smaller cylinder (`r1`) is subtracted to make the inner void.
+ * - Cubes are positioned and rotated to cut away the unwanted parts of the
+ * cylinders, forming the arc or segment shape.
+ *
+ * Note:
+ * - All rotations are in the XY plane (around the Z-axis).
+ * - The height parameter includes an extra 2 units for the difference and
+ * intersection operations to ensure clean cuts through the cylinders.
+ */
+module arc( r1, r2, height, a1=0, a2=0 ) {
+ if (a2 - a1 <= 180) {
+ // For arcs less than or equal to 180 degrees, we can directly cut the shape
+ difference() {
+ // Create the outer cylinder
+ cylinder(r=r2, h=height);
+ // Remove the inner part to make it hollow
+ translate([0,0,-1]) cylinder(r=r1, h=height+2);
+ // Cut off one side of the arc
+ rotate(a2) translate([-r1-r2,0,-1]) cube([2*(r1+r2),2*(r1+r2),height+2]);
+ // Cut off the other side of the arc
+ rotate(a1+180) translate([-r1-r2,0,-1]) cube([2*(r1+r2),2*(r1+r2),height+2]);
+ }
+ } else {
+ // For arcs greater than 180 degrees, we use intersection to ensure we keep the correct shape
+ difference() {
+ cylinder(r=r2, h=height); // Outer shape
+ translate([0,0,-1]) cylinder(r=r1, h=height+2); // Inner void
+ intersection() {
+ // First cut to carve the arc shape
+ rotate(a2) translate([-r1-r2,0,-1]) cube([2*(r1+r2),2*(r1+r2),height+2]);
+ // Second cut for arcs greater than 180 degrees to ensure we keep only the arc
+ rotate(a1+180) translate([-r1-r2,0,-1]) cube([2*(r1+r2),2*(r1+r2),height+2]);
+ }
+ }
+ }
+}
+
+
+/**
+ * Generates a "snake line" or a path with segments that can turn or go straight.
+ *
+ * @param {array} angle - An array of angles for each segment.
+ * Positive values indicate a left/counter-clockwise turn,
+ * negative for right/clockwise, and zero for straight segments.
+ * @param {array} radius - An array of radii for each segment.
+ * The absolute value of radius[i] is used for turning segments,
+ * and directly for straight segments.
+ * @param {number} i - Current index in the angle and radius arrays.
+ * @param {number} w - Width of the snake line.
+ * @param {number} h1 - Height of the snake line.
+ *
+ * @description
+ * This module constructs a continuous line that can bend at specified angles
+ * and lengths. It uses recursion to build the path segment by segment:
+ *
+ * - For each segment:
+ * - If the angle is non-zero (`a != 0`), it creates an arc using the `arc` module,
+ * where the radii of the arc are adjusted by `w/2` to account for the line's width.
+ * - For zero angle (`a == 0`), it simply extends straight using a cube.
+ * - The `scale` operation ensures the correct orientation of the arc for negative angles.
+ * - The `translate` and `rotate` operations move and orient each new segment relative
+ * to the last one.
+ *
+ * Notes:
+ * - The module uses a tiny overlap (`-r-0.01`) when placing cubes to ensure a seamless join.
+ * - Recursive calls to `sline` build the whole path until all angles in the array are processed.
+ */
+module sline(angle, radius, i, w, h1) {
+ scale([angle[i] >= 0 ? 1 : -1, 1, 1]) { // Flip for negative angles
+ // Declare variables for current segment
+ r = abs(radius[i]); // Radius for current segment
+ a = angle[i]; // Angle for current segment
+
+ translate([a ? r : 0, 0, 0]) { // Move to start of segment
+ translate([-w/2, -r - 0.01, 0]) // Position for overlap
+ cube([w, 0.02, h1]); // Small cube for overlap
+
+ if (a) {
+ // Create an arc if there's an angle (turn)
+ arc(
+ r1 = r - w/2, // Inner radius of arc
+ r2 = r + w/2, // Outer radius of arc
+ a1 = a, // Start angle, adjusted to be from 0 to a
+ height = h1 // Height of the arc
+ );
+ } else if (r > 0) {
+ // Create straight segment if no turn and radius is positive
+ translate([-w/2, -r, 0]) cube([w, r, h1]);
+ }
+
+ if (i + 1 < len(angle)) { // Check if there's another segment to process
+ rotate(angle[i]) // Rotate for the next segment
+ translate([a ? -r : 0, a ? 0 : -r, 0]) // Translate to the end of current segment
+ sline(angle, radius, i + 1, w, h1); // Recursively call for next segment
+ }
+ }
+ }
+}
+// build a cube with chamfered edges
+module chamfered_cube(size,d=1){
+ hull(){
+ translate([d,d,0])cube(size-2*[d,d,0]);
+ translate([0,d,d])cube(size-2*[0,d,d]);
+ translate([d,0,d])cube(size-2*[d,0,d]);
+ }
+}
+
+
+
+
diff --git a/doc/.source_hashes b/doc/.source_hashes
new file mode 100644
index 0000000..50c8c74
--- /dev/null
+++ b/doc/.source_hashes
@@ -0,0 +1 @@
+battery-holder.scad|6ed4929e3433db05a34a3565e39c611c992f9c4d586969cd2e0d3cd5bb49db3e
diff --git a/doc/AlphaIndex.md b/doc/AlphaIndex.md
new file mode 100644
index 0000000..4a75674
--- /dev/null
+++ b/doc/AlphaIndex.md
@@ -0,0 +1,10 @@
+# Alphabetical Index
+
+An index of Functions, Modules, and Constants by name.
+
+[B](#b)
+
+## B
+
+- [`battery_case()`](battery-holder.scad#module-battery_case) Mod
+
diff --git a/doc/TOC.md b/doc/TOC.md
new file mode 100644
index 0000000..600050d
--- /dev/null
+++ b/doc/TOC.md
@@ -0,0 +1,16 @@
+# Table of Contents
+
+## List of Files
+
+**Batteries:**
+
+- [battery-holder.scad](#1-battery-holderscad) ([docs](battery-holder.scad))
+This file generates battery holders for arbitrary cylindrical sizes..
+
+## 1. [battery-holder.scad](battery-holder.scad)
+
+This file generates battery holders for arbitrary cylindrical sizes..
+- [Batteries](battery-holder.scad#section-batteries)
+ - [`battery_case()`](battery-holder.scad#module-battery_case) Mod
+
+
diff --git a/doc/Topics.md b/doc/Topics.md
new file mode 100644
index 0000000..3afd7c7
--- /dev/null
+++ b/doc/Topics.md
@@ -0,0 +1,4 @@
+# Topic Index
+
+An index of topics, with related functions, modules, and constants.
+
diff --git a/doc/_Sidebar.md b/doc/_Sidebar.md
new file mode 100644
index 0000000..7af479d
--- /dev/null
+++ b/doc/_Sidebar.md
@@ -0,0 +1,10 @@
+[Table of Contents](TOC)
+[Function Index](AlphaIndex)
+[Topics Index](Topics)
+
+## List of Files:
+
+**Batteries:**
+
+- [battery-holder.scad](battery-holder.scad)
+
diff --git a/doc/battery-holder.scad.md b/doc/battery-holder.scad.md
new file mode 100644
index 0000000..47ecf64
--- /dev/null
+++ b/doc/battery-holder.scad.md
@@ -0,0 +1,72 @@
+# LibFile: battery-holder.scad
+
+Functions and modules for creating battery holders
+
+To use, add the following lines to the beginning of your file:
+
+ include
+
+## File Contents
+
+1. [Section: Batteries](#section-batteries)
+ - [`battery_case()`](#module-battery_case)
+
+
+## Section: Batteries
+
+
+### Module: battery\_case()
+
+**Usage:**
+
+- battery_case(n);
+
+**Description:**
+
+Generates a flexible battery holder for 'n' cylindrical batteries with complex
+ features like wire channels, screw holes, and contact points.
+
+**Parts:** This module constructs a battery holder with
+
+- A main body with a cavity for each battery.
+- Plastic springs for negative pole contact using the `sline` module.
+- Contact points (+ and -) for electrical connectivity.
+- Wire channels for routing electrical connections.
+- Screw holes for mounting.
+- Engraving for battery orientation symbols.
+
+**Key Features:**
+
+- **Chamfering** of edges for a more finished look (`chamfered_cube`).
+- **Deepening** for better grip on batteries (optional).
+- **Wire Channels**: Both longitudinal and transversal for wire routing.
+- **Spring**: Utilizes `sline` to create a complex spring shape for battery contact.
+- **Contact Points**: Bulges for ensuring good electrical contact.
+- **Engraving**: Symbols to indicate battery polarity.
+
+**Arguments:**
+
+By Position | What it does
+-------------------- | ------------
+`n` | Number of battery slots to generate. (default: 1)
+`l` | Length of each battery slot. (default: 65)
+`d` | Diameter of the battery. (default: 18)
+`hf` | Height factor for the battery holder relative to the battery diameter. (default: 0.75)
+`r` | Radius for the plastic spring (calculated inside). (default: 4)
+`shd` | Screw hole diameter. (default: 3)
+`eps` | Epsilon for additional cavity space around the battery. (default: 0.28)
+`el` | Extra length for the spring. (default: 0)
+`xchan` | Relative positions for traversal wire channels. (default: [1/4, 3/4])
+`$fn` | Number of fragments for circles/cylinders for smoother curves. (default: 24)
+
+**Example 1:** Simple Case
+
+
+
+
+
+ include
+ battery_case(n=1,l=46.1,d=10.45,hf=0.84,shd=2,el=1,xchan=[0.5],eps=0);
+
+---
+
diff --git a/doc/images/battery-holder/battery_case.png b/doc/images/battery-holder/battery_case.png
new file mode 100644
index 0000000..775a926
Binary files /dev/null and b/doc/images/battery-holder/battery_case.png differ
diff --git a/doc/images/battery-holder/case.png b/doc/images/battery-holder/case.png
new file mode 100644
index 0000000..775a926
Binary files /dev/null and b/doc/images/battery-holder/case.png differ
diff --git a/run.sh b/run.sh
new file mode 100755
index 0000000..76590a8
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# Function to install docgen
+install_docgen() {
+ echo "Creating virtual environment..."
+ python3 -m venv docgen
+ source docgen/bin/activate
+ echo "Installing openscad_docsgen..."
+ pip install openscad_docsgen
+ echo "Installation completed."
+}
+
+# Function to run openscad-docsgen with your specified options
+run_docsgen() {
+ # Check if we are in the virtual environment
+ if [ -z "$VIRTUAL_ENV" ] || [ ! "$(basename "$VIRTUAL_ENV")" == "docgen" ]; then
+ echo "Activating docgen virtual environment..."
+ source docgen/bin/activate || { echo "Failed to activate virtual environment. Make sure 'docgen' exists."; return 1; }
+ fi
+ echo "Running openscad-docsgen..."
+ openscad-docsgen -m -I -P "Metalib" -f -p wiki -i -t -m -s
+ echo "Documentation generation completed."
+}
+
+# Function to uninstall (deactivate the virtual environment)
+uninstall_docgen() {
+ echo "Deactivating virtual environment..."
+ deactivate
+ echo "Virtual environment deactivated."
+}
+
+# Main script logic
+echo "OpenSCAD DocsGen Script"
+echo "----------------------"
+echo "1. Doc Generation"
+echo "2. Install docgen"
+echo "3. Uninstall docgen"
+echo "q. Exit"
+
+while true; do
+ read -p "Choose an option (1/2/3/q): " choice
+
+ case $choice in
+ 1)
+ run_docsgen
+ ;;
+ 2)
+ install_docgen
+ ;;
+ 3)
+ uninstall_docgen
+ ;;
+ q)
+ echo "Exiting script. Goodbye!"
+ exit 0
+ ;;
+ *)
+ echo "Invalid option. Please try again."
+ ;;
+ esac
+done