From ce19d357bcf876333c0173b23098e7dd66c15e0f Mon Sep 17 00:00:00 2001 From: Ajabep Date: Thu, 8 Jan 2026 18:33:42 +0100 Subject: [PATCH 01/23] Add an icon library --- .gitmodules | 9 ++++++--- thing-logos | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) create mode 160000 thing-logos diff --git a/.gitmodules b/.gitmodules index 0180858..95b403e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ -[submodule "write"] - path = write - url = https://github.com/rohieb/Write.scad +[submodule "write"] + path = write + url = https://github.com/rohieb/Write.scad +[submodule "thing-logos"] + path = thing-logos + url = https://github.com/rohieb/thing-logos.git diff --git a/thing-logos b/thing-logos new file mode 160000 index 0000000..2ac5bec --- /dev/null +++ b/thing-logos @@ -0,0 +1 @@ +Subproject commit 2ac5bec02e5015918dff95b6c12ba890ef1ea95a -- 2.50.1 From b342b09dab8a22ec213deb17bbadb5a7a825d983 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Fri, 9 Jan 2026 20:31:34 +0100 Subject: [PATCH 02/23] Fix indent, some typo, and all of that --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 107 ++++++++++++----------- 1 file changed, 58 insertions(+), 49 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index 19a51e7..960978a 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -33,8 +33,8 @@ $fn = 360; NAME = "c3cat"; LOGO_FILE = ""; // empty string is catear model RENDER_COLOR_ONE = true; -RENDER_COLOR_TWO = true; -RENDER_COLOR_THREE = true; +RENDER_COLOR_TEXT = true; +RENDER_COLOR_LOGO = true; USE_TINY_EARS = true; /** @@ -48,9 +48,9 @@ USE_TINY_EARS = true; * rl: the radius on the lower side of the clip * ht: the height of the clip * width: the thickness of the wall. Values near 2.5 usually result in a good - * clippiness for PLA prints. + * clippiness for PLA prints. * name: the name that is printed on your name tag. For the default ru/rt/ht - * values, this string should not exceed 18 characters to fit on the name tag. + * values, this string should not exceed 18 characters to fit on the name tag. * font: the path to a font for Write.scad. */ @@ -61,9 +61,18 @@ USE_TINY_EARS = true; */ scale([0.2, 0.2, 0.2]) { difference() { - scale([5, 5, 5]) rotate(45, [0, 0, 1]) bottle_clip(name=NAME); - translate([ 15*5, 0*5, 18*5]) rotate(80, [0, 1, 0]) catear(); - translate([-15*5, 0*5, 18*5]) rotate(-80, [0, 1, 0]) catear(); + scale([5, 5, 5]) + rotate(45, [0, 0, 1]) + bottle_clip( + name=NAME); + + translate([ 15*5, 0*5, 18*5]) + rotate(-80, [0, 1, 0]) + catear(); + + translate([-15*5, 0*5, 18*5]) + rotate(-80, [0, 1, 0]) + catear(); } if (RENDER_COLOR_TWO) { color("orange") @@ -82,7 +91,7 @@ scale([0.2, 0.2, 0.2]) { module name(name, font, rl, ht, ru) { writecylinder( text=name, - where=[0,0,0], + where=[0, 0, 0], radius=rl+0.5, height=ht/13*7, h=ht/13*4, @@ -91,22 +100,22 @@ module name(name, font, rl, ht, ru) { } module logo(logo, rl, ht, ru, width) { - echo("logo: ", logo); + echo(logo=logo); if(logo == "") { ear_size=ht; - echo("ht: ", ht); - echo("ru: ", ru); - echo("rl: ", rl); - echo("width: ", width); - translate([0,-max(ru,rl),ht*3/4+.5]) - rotate([90,0,0]) - scale([1,1,1]) - scale([ht/100,ht/100,1]) - translate([0, -ht/2,0]) + echo(ht=ht); + echo(ru=ru); + echo(rl=rl); + echo(width=width); + translate([0, -max(ru,rl), ht*3/4+.5]) + rotate([90, 0, 0]) + scale([1, 1, 1]) + scale([ht/100, ht/100, 1]) + translate([0, -ht/2, 0]) rotate(-90, [0, 0, 1]) catear_headband( size=ear_size, - height=max(ru,rl), + height=max(ru, rl), thickness=width, stretch_len=0, tip_len=0, @@ -114,38 +123,38 @@ module logo(logo, rl, ht, ru, width) { with_rake=false ); } else { - // The logo has been split in 3 parts. // well was... TODO + // The logo has been split in 3 parts. // well was... TODO /* - rotate([0,0,-48]) translate([0,0,ht*3/4-0.1]) - rotate([90,0,0]) - scale([0.9,0.9,1]) - scale([ht/100,ht/100,1]) - translate([-25,-29,0.5]) - linear_extrude(height=max(ru,rl)*2) - import("logo_1.dxf"); + rotate([0, 0, -48]) translate([0, 0, ht*3/4-0.1]) + rotate([90, 0, 0]) + scale([0.9, 0.9, 1]) + scale([ht/100, ht/100, 1]) + translate([-25, -29, 0.5]) + linear_extrude(height=max(ru,rl)*2) + import("logo_1.dxf"); */ - translate([0,0,ht*3/4-0.1]) - rotate([90,0,0]) - scale([0.8,0.8,1]) - scale([ht/100,ht/100,1]) - translate([-18,-22,0.5]) - linear_extrude(height=max(ru,rl)*2) - import(logo); + translate([0, 0, ht*3/4-0.1]) + rotate([90, 0, 0]) + scale([0.8, 0.8, 1]) + scale([ht/100, ht/100, 1]) + translate([-18, -22, 0.5]) + linear_extrude(height=max(ru, rl)*2) + import(logo); /* - rotate([0,0,44]) translate([0,0,ht*3/4-0.1]) - rotate([90,0,0]) - scale([0.7,0.7,1]) - scale([ht/100,ht/100,1]) - translate([-25,-26,0.5]) - linear_extrude(height=max(ru,rl)*2) - import("logo_3.dxf"); + rotate([0, 0, 44]) translate([0, 0, ht*3/4-0.1]) + rotate([90, 0, 0]) + scale([0.7, 0.7, 1]) + scale([ht/100, ht/100, 1]) + translate([-25, -26, 0.5]) + linear_extrude(height=max(ru,rl)*2) + import("logo_3.dxf"); */ } } module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="c3cat", font="write/orbitron.dxf", logo="") { - e=100; // should be big enough, used for the outer boundary of the text/logo - rotate([0,0,-45]) { + e = 100; // should be big enough, used for the outer boundary of the text/logo + rotate([0, 0, -45]) { // main cylinder if (RENDER_COLOR_ONE) { color("black") difference() { @@ -157,12 +166,12 @@ module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="c3cat", font="write/o } cylinder(r1=rl+width/2, r2=ru+width/2, h=ht); } - translate([0,0,-1]) + translate([0, 0, -1]) cylinder(r1=rl, r2=ru, h=ht+2); - // finally, substract a cube as a gap so we can clip it to the bottle + // finally, subtract a cube as a gap so we can clip it to the bottle rotate([0, 0, 45]) - translate([0,0,-1]) - cube([50,50,50]); + translate([0, 0, -1]) + cube([50, 50, 50]); } } // text @@ -185,11 +194,11 @@ module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="c3cat", font="write/o } module outer_cutoff(rl, e, ru, ht, width) { - // outer cylinder which is substracted, so the text and the logo end + // outer cylinder which is subtracted, so the text and the logo end // somewhere on the outside ;-) difference () { cylinder(r1=rl+e, r2=ru+e, h=ht); - translate([0,0,-1]) + translate([0, 0, -1]) // Note: bottom edges of characters are hard to print when character // depth is > 0.7 cylinder(r1=rl+width+0.7, r2=ru+width+0.7, h=ht+2); -- 2.50.1 From aaf497d73688ecd0c3ea13b0d6eab9a369cf379f Mon Sep 17 00:00:00 2001 From: Ajabep Date: Fri, 9 Jan 2026 20:33:43 +0100 Subject: [PATCH 03/23] Renamed variables for more clarity RENDER_COLOR_ONE -> RENDER_COLOR_CLIP RENDER_COLOR_TWO -> RENDER_COLOR_TEXT RENDER_COLOR_THREE -> RENDER_COLOR_LOGO --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 12 ++++++------ generate_bottle_tag.sh | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index 960978a..a66d7b5 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -32,7 +32,7 @@ $fn = 360; NAME = "c3cat"; LOGO_FILE = ""; // empty string is catear model -RENDER_COLOR_ONE = true; +RENDER_COLOR_CLIP = true; RENDER_COLOR_TEXT = true; RENDER_COLOR_LOGO = true; USE_TINY_EARS = true; @@ -74,13 +74,13 @@ scale([0.2, 0.2, 0.2]) { rotate(-80, [0, 1, 0]) catear(); } - if (RENDER_COLOR_TWO) { + if (RENDER_COLOR_TEXT) { color("orange") translate([ 15*5, 0*5, 18*5]) rotate(80, [0, 1, 0]) catear(); } - if (RENDER_COLOR_THREE) { + if (RENDER_COLOR_LOGO) { color("yellow") translate([-15*5, 0*5, 18*5]) rotate(-80, [0, 1, 0]) @@ -156,7 +156,7 @@ module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="c3cat", font="write/o e = 100; // should be big enough, used for the outer boundary of the text/logo rotate([0, 0, -45]) { // main cylinder - if (RENDER_COLOR_ONE) { + if (RENDER_COLOR_CLIP) { color("black") difference() { cylinder(r1=rl+width, r2=ru+width, h=ht); difference() { @@ -175,7 +175,7 @@ module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="c3cat", font="write/o } } // text - if (RENDER_COLOR_TWO) { + if (RENDER_COLOR_TEXT) { color("orange") difference() { name(name=name, font=font, rl=rl, ht=ht, ru=ru); cylinder(r1=rl+width/2, r2=ru+width/2, h=ht); @@ -183,7 +183,7 @@ module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="c3cat", font="write/o } } // logo - if (RENDER_COLOR_THREE) { + if (RENDER_COLOR_LOGO) { color("yellow") difference() { logo(logo=logo, rl=rl, ht=ht, ru=ru, width=2*width); cylinder(r1=rl+width/2, r2=ru+width/2, h=ht); diff --git a/generate_bottle_tag.sh b/generate_bottle_tag.sh index 8ea378d..42e553e 100755 --- a/generate_bottle_tag.sh +++ b/generate_bottle_tag.sh @@ -15,19 +15,19 @@ NAME="\"$1\"" render() { case "$2" in body) - ONE=true - TWO=false - THREE=false + CLIP=true + TEXT=false + LOGO=false ;; name) - ONE=false - TWO=true - THREE=false + CLIP=false + TEXT=true + LOGO=false ;; logo) - ONE=false - TWO=false - THREE=true + CLIP=false + TEXT=false + LOGO=true ;; *) echo 'fatal: invalid part' >&2 @@ -41,9 +41,9 @@ render() { -D "USE_TINY_EARS=${TINY_EARS}" \ -D "LOGO_FILE=${LOGO_FILE}" \ -D "NAME=${NAME}" \ - -D "RENDER_COLOR_ONE=${ONE}" \ - -D "RENDER_COLOR_TWO=${TWO}" \ - -D "RENDER_COLOR_THREE=${THREE}" \ + -D "RENDER_COLOR_CLIP=${CLIP}" \ + -D "RENDER_COLOR_TEXT=${TEXT}" \ + -D "RENDER_COLOR_LOGO=${LOGO}" \ -o "stls/c3cat-bottle-clip-v${VERSION}_${NAME}_${PART}.stl" \ c3cat-bottle-clip/c3cat-bottle-clip.scad } -- 2.50.1 From 301c06da5f872895f66b9e19678c5bb88d83d879 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Fri, 9 Jan 2026 22:15:35 +0100 Subject: [PATCH 04/23] Add two variables Add the FONT variable Propagate the LOGO_FILE --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index a66d7b5..631a697 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -32,6 +32,7 @@ $fn = 360; NAME = "c3cat"; LOGO_FILE = ""; // empty string is catear model +FONT = ""; // empty string is for Orbitron font RENDER_COLOR_CLIP = true; RENDER_COLOR_TEXT = true; RENDER_COLOR_LOGO = true; @@ -64,7 +65,9 @@ scale([0.2, 0.2, 0.2]) { scale([5, 5, 5]) rotate(45, [0, 0, 1]) bottle_clip( - name=NAME); + name=NAME, + font=FONT, + logo=LOGO_FILE); translate([ 15*5, 0*5, 18*5]) rotate(-80, [0, 1, 0]) @@ -152,7 +155,9 @@ module logo(logo, rl, ht, ru, width) { } } -module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="c3cat", font="write/orbitron.dxf", logo="") { +module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="", font="", logo="") { + name = name == "" ? "c3cat" : name ; + font = font == "" ? "write/orbitron.dxf" : font ; e = 100; // should be big enough, used for the outer boundary of the text/logo rotate([0, 0, -45]) { // main cylinder @@ -219,7 +224,9 @@ module catear() { * bottles (like fritz cola, etc.). All parameters are passed to the module * bottle_clip(), see there for their documentation. */ -module bottle_clip_longneck(name="c3cat", width=2.5, font="write/orbitron.dxf") { +module bottle_clip_longneck(name="", width=2.5, font="") { + name = name == "" ? "c3cat" : name ; + font = font == "" ? "write/orbitron.dxf" : font ; bottle_clip(name=name, ru=13, rl=15, ht=26, width=width, font=font, logo=""); } -- 2.50.1 From 6de29e17521bdfdd90fe1f9258e736e146bdec6a Mon Sep 17 00:00:00 2001 From: Ajabep Date: Sat, 10 Jan 2026 00:10:07 +0100 Subject: [PATCH 05/23] Add documentation of variables (also available in the customizer) --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 26 +++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index 631a697..9e6dc35 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -28,15 +28,27 @@ include use + +// The name that is printed on your name tag. +NAME = "c3cat"; +// The logo that is printed on your name tag. Has to be a DXF file. You can use the thing-logo repository for inspiration. +LOGO_FILE = ""; // empty string is catear model +// The font to be used for the name tag. See Write.scad for details. +FONT = ""; // empty string is for Orbitron font + +/* [Render] */ +// Whether to render the clip, the body. +RENDER_COLOR_CLIP = true; +// Whether to render the text part. +RENDER_COLOR_TEXT = true; +// Whether to render the logo part. +RENDER_COLOR_LOGO = true; +// Add ears to the clip. +USE_TINY_EARS = true; + +// Set the number of facets for circles. $fn = 360; -NAME = "c3cat"; -LOGO_FILE = ""; // empty string is catear model -FONT = ""; // empty string is for Orbitron font -RENDER_COLOR_CLIP = true; -RENDER_COLOR_TEXT = true; -RENDER_COLOR_LOGO = true; -USE_TINY_EARS = true; /** * Creates one instance of a bottle clip name tag. The default values are -- 2.50.1 From bbb63737ccecc8384475e32ebe5471787ad28da6 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Sat, 10 Jan 2026 11:08:01 +0100 Subject: [PATCH 06/23] Add a "FORMAT" variable to change the format for the bottle. --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 67 +++++++++++++++++------- generate_bottle_tag.sh | 20 +++++-- 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index 9e6dc35..f629f0e 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -36,6 +36,10 @@ LOGO_FILE = ""; // empty string is catear model // The font to be used for the name tag. See Write.scad for details. FONT = ""; // empty string is for Orbitron font +// Format of the bottle clip you want to create. +FORMAT = 0; // [0=Club Mate 50cL, 1=Long Neck as 25cL Club Mate or Fritz-kola, 2=Euroform 2] + + /* [Render] */ // Whether to render the clip, the body. RENDER_COLOR_CLIP = true; @@ -76,10 +80,11 @@ scale([0.2, 0.2, 0.2]) { difference() { scale([5, 5, 5]) rotate(45, [0, 0, 1]) - bottle_clip( + bottle_clip_format( name=NAME, font=FONT, - logo=LOGO_FILE); + logo=LOGO_FILE, + format=FORMAT); translate([ 15*5, 0*5, 18*5]) rotate(-80, [0, 1, 0]) @@ -103,6 +108,45 @@ scale([0.2, 0.2, 0.2]) { } } +module bottle_clip_format(name="", font="", logo="", format=0) { + name = name == "" ? "c3cat" : name ; + font = font == "" ? "write/orbitron.dxf" : font ; + + if (format == 0) { + // Club-Mate 0.5L bottle + bottle_clip( + name=name, + font=font, + logo=logo, + ru=13, + rl=17.5, + ht=26, + width=2.5); + } else if (format == 1) { + // 0.25L Long Neck format (like fritz-kola) + bottle_clip( + name=name, + font=font, + logo=logo, + ru=13, + rl=15, + ht=26, + width=2.5); + } else if (format == 2) { + // Euroform 2 bottle + bottle_clip( + name=name, + font=font, + logo=logo, + ru=13, + rl=22.5, + ht=26, + width=2.5); + } else { + assert(false, str("Unknown format ", format, ".")); + } +} + module name(name, font, rl, ht, ru) { writecylinder( text=name, @@ -167,9 +211,7 @@ module logo(logo, rl, ht, ru, width) { } } -module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="", font="", logo="") { - name = name == "" ? "c3cat" : name ; - font = font == "" ? "write/orbitron.dxf" : font ; +module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="c3cat", font="write/orbitron.dxf", logo="") { e = 100; // should be big enough, used for the outer boundary of the text/logo rotate([0, 0, -45]) { // main cylinder @@ -230,18 +272,3 @@ module catear() { } } } - -/** - * Creates one instance of a bottle clip name tag suitable for 0.33l longneck - * bottles (like fritz cola, etc.). All parameters are passed to the module - * bottle_clip(), see there for their documentation. - */ -module bottle_clip_longneck(name="", width=2.5, font="") { - name = name == "" ? "c3cat" : name ; - font = font == "" ? "write/orbitron.dxf" : font ; - bottle_clip(name=name, ru=13, rl=15, ht=26, width=width, font=font, logo=""); -} - -/** - * The Steinie-Tag has been removed since it does not support logos. -**/ diff --git a/generate_bottle_tag.sh b/generate_bottle_tag.sh index 42e553e..6707ea4 100755 --- a/generate_bottle_tag.sh +++ b/generate_bottle_tag.sh @@ -11,7 +11,7 @@ FN=90 NAME="\"$1\"" -# usage: render NAME PART +# usage: render NAME PART [BOTTLE_FORMAT] render() { case "$2" in body) @@ -35,6 +35,12 @@ render() { ;; esac + if [ $# -lt 3 ]; then + BOTTLE_FORMAT="0" + else + BOTTLE_FORMAT="$3" + fi + echo rendering "$1" "$2" openscad \ -D "\$fn=${FN}" \ @@ -44,14 +50,18 @@ render() { -D "RENDER_COLOR_CLIP=${CLIP}" \ -D "RENDER_COLOR_TEXT=${TEXT}" \ -D "RENDER_COLOR_LOGO=${LOGO}" \ - -o "stls/c3cat-bottle-clip-v${VERSION}_${NAME}_${PART}.stl" \ + -o "stls/c3cat-bottle-clip-v${VERSION}_BOTTLE${BOTTLE_FORMAT}_${NAME}_${PART}.stl" \ c3cat-bottle-clip/c3cat-bottle-clip.scad } cd "$(dirname $0)" -for PART in body logo name +for BOTTLE_FORMAT in 0 1 2 do - render "$NAME" "$PART" - sleep 1 + echo "Generating bottle format ${BOTTLE_FORMAT}" + for PART in body logo name + do + render "$NAME" "$PART" "$BOTTLE_FORMAT" + sleep 1 + done done -- 2.50.1 From f492bdd2cd7b9121891b5da62c6b9a0ad24ecdc0 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Sat, 10 Jan 2026 11:36:09 +0100 Subject: [PATCH 07/23] Add the Steinie bottles --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index f629f0e..ed49767 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -37,7 +37,7 @@ LOGO_FILE = ""; // empty string is catear model FONT = ""; // empty string is for Orbitron font // Format of the bottle clip you want to create. -FORMAT = 0; // [0=Club Mate 50cL, 1=Long Neck as 25cL Club Mate or Fritz-kola, 2=Euroform 2] +FORMAT = 3; // [0=Club Mate 50cL, 1=Long Neck as 25cL Club Mate or Fritz-kola, 2=Euroform 2, 3=Steinie bottles] /* [Render] */ @@ -142,6 +142,16 @@ module bottle_clip_format(name="", font="", logo="", format=0) { rl=22.5, ht=26, width=2.5); + } else if (format == 3) { + // Steinie bottle + bottle_clip( + name=name, + font=font, + logo=logo, + ru=13, + rl=17.5, + ht=13, + width=2.5); } else { assert(false, str("Unknown format ", format, ".")); } -- 2.50.1 From 6364985e5531fc661114f08acc2fb8d3fad0f6a3 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Sun, 11 Jan 2026 10:03:00 +0100 Subject: [PATCH 08/23] Add a function to generate a the bottle clip depending on the asked format --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 116 +++++++++-------------- 1 file changed, 44 insertions(+), 72 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index ed49767..c0f1388 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -77,84 +77,55 @@ $fn = 360; * using meshlab to run `Filters` -> `Cleaning and Repairing` -> `Remove T-Vertices` by `Edge-Flip` with `Ratio` of `1000000` before importing the stl works but but two errors remain. */ scale([0.2, 0.2, 0.2]) { - difference() { - scale([5, 5, 5]) - rotate(45, [0, 0, 1]) - bottle_clip_format( - name=NAME, - font=FONT, - logo=LOGO_FILE, - format=FORMAT); - - translate([ 15*5, 0*5, 18*5]) - rotate(-80, [0, 1, 0]) - catear(); - - translate([-15*5, 0*5, 18*5]) - rotate(-80, [0, 1, 0]) - catear(); - } - if (RENDER_COLOR_TEXT) { - color("orange") - translate([ 15*5, 0*5, 18*5]) - rotate(80, [0, 1, 0]) - catear(); - } - if (RENDER_COLOR_LOGO) { - color("yellow") - translate([-15*5, 0*5, 18*5]) - rotate(-80, [0, 1, 0]) - catear(); - } + render_bottle_clip( + name=NAME, + font=FONT, + logo=LOGO_FILE, + format=FORMAT, + has_ears=USE_TINY_EARS); } -module bottle_clip_format(name="", font="", logo="", format=0) { +module render_bottle_clip(name="", font="", logo="", format=0, has_ears=true) { name = name == "" ? "c3cat" : name ; font = font == "" ? "write/orbitron.dxf" : font ; - if (format == 0) { - // Club-Mate 0.5L bottle - bottle_clip( - name=name, - font=font, - logo=logo, - ru=13, - rl=17.5, - ht=26, - width=2.5); - } else if (format == 1) { - // 0.25L Long Neck format (like fritz-kola) - bottle_clip( - name=name, - font=font, - logo=logo, - ru=13, - rl=15, - ht=26, - width=2.5); - } else if (format == 2) { - // Euroform 2 bottle - bottle_clip( - name=name, - font=font, - logo=logo, - ru=13, - rl=22.5, - ht=26, - width=2.5); - } else if (format == 3) { - // Steinie bottle - bottle_clip( - name=name, - font=font, - logo=logo, - ru=13, - rl=17.5, - ht=13, - width=2.5); - } else { + if (format < 0 || format > 3) { assert(false, str("Unknown format ", format, ".")); } + + // Format == 0: Club-Mate 0.5L bottle + // Format == 1: Long Neck bottle (like fritz-kola) 0.25L + // Format == 2: Euroform 2 bottle + // Format == 3: Steinie bottle + ru = 13; + rl = format == 1 ? 15 : format == 2 ? 22.5 : 17.5 ; + ht = format == 3 ? 13 : 26 ; + width = 2.5; + + + scale([5, 5, 5]) + rotate(45, [0, 0, 1]) { + difference() { + bottle_clip( + name=NAME, + font=FONT, + logo=LOGO_FILE, + ru=ru, + rl=rl, + ht=ht, + width=width); + + // Render ears if requested + if (has_ears) { + ears(ht=ht, ru=ru, rl=rl); + } + } + + // Render ears if requested + if (has_ears) { + ears(ht=ht, ru=ru, rl=rl); + } + } } module name(name, font, rl, ht, ru) { @@ -171,6 +142,7 @@ module name(name, font, rl, ht, ru) { module logo(logo, rl, ht, ru, width) { echo(logo=logo); if(logo == "") { + // No logo file specified? Let's print a catear headband instead! ear_size=ht; echo(ht=ht); echo(ru=ru); @@ -274,7 +246,7 @@ module outer_cutoff(rl, e, ru, ht, width) { } } -module catear() { +module ear() { if (USE_TINY_EARS) { rotate(-90, [0, 0, 1]) union() { scale([1, 1 ,1]) translate([0, -85]) import("catear.stl"); -- 2.50.1 From e2aa0c2a83fc548e8a38c83f38dea734f2f3a9b1 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Sun, 11 Jan 2026 10:08:56 +0100 Subject: [PATCH 09/23] Add the HAS_EARS var and rename the RENDER_EARS to RENDER_COLOR_EARS Add the HAS_EARS variable to be able to generate bottle clips without ears. Rename the RENDER_EARS to RENDER_COLOR_EARS --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 10 ++++++---- generate_bottle_tag.sh | 12 +++++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index c0f1388..3d11766 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -39,6 +39,8 @@ FONT = ""; // empty string is for Orbitron font // Format of the bottle clip you want to create. FORMAT = 3; // [0=Club Mate 50cL, 1=Long Neck as 25cL Club Mate or Fritz-kola, 2=Euroform 2, 3=Steinie bottles] +// Append ears to the bottle clip +HAS_EARS = true; /* [Render] */ // Whether to render the clip, the body. @@ -47,8 +49,8 @@ RENDER_COLOR_CLIP = true; RENDER_COLOR_TEXT = true; // Whether to render the logo part. RENDER_COLOR_LOGO = true; -// Add ears to the clip. -USE_TINY_EARS = true; +// Whether to render the cat ears part. +RENDER_COLOR_EARS = true; // Set the number of facets for circles. $fn = 360; @@ -82,7 +84,7 @@ scale([0.2, 0.2, 0.2]) { font=FONT, logo=LOGO_FILE, format=FORMAT, - has_ears=USE_TINY_EARS); + has_ears=HAS_EARS); } module render_bottle_clip(name="", font="", logo="", format=0, has_ears=true) { @@ -122,7 +124,7 @@ module render_bottle_clip(name="", font="", logo="", format=0, has_ears=true) { } // Render ears if requested - if (has_ears) { + if (has_ears && RENDER_COLOR_EARS) { ears(ht=ht, ru=ru, rl=rl); } } diff --git a/generate_bottle_tag.sh b/generate_bottle_tag.sh index 6707ea4..bc90159 100755 --- a/generate_bottle_tag.sh +++ b/generate_bottle_tag.sh @@ -18,16 +18,25 @@ render() { CLIP=true TEXT=false LOGO=false + EARS=false ;; name) CLIP=false TEXT=true LOGO=false + EARS=false ;; logo) CLIP=false TEXT=false LOGO=true + EARS=false + ;; + ears) + CLIP=false + TEXT=false + LOGO=false + EARS=true ;; *) echo 'fatal: invalid part' >&2 @@ -50,6 +59,7 @@ render() { -D "RENDER_COLOR_CLIP=${CLIP}" \ -D "RENDER_COLOR_TEXT=${TEXT}" \ -D "RENDER_COLOR_LOGO=${LOGO}" \ + -D "RENDER_COLOR_EARS=${EARS}" \ -o "stls/c3cat-bottle-clip-v${VERSION}_BOTTLE${BOTTLE_FORMAT}_${NAME}_${PART}.stl" \ c3cat-bottle-clip/c3cat-bottle-clip.scad } @@ -59,7 +69,7 @@ cd "$(dirname $0)" for BOTTLE_FORMAT in 0 1 2 do echo "Generating bottle format ${BOTTLE_FORMAT}" - for PART in body logo name + for PART in body logo name ears do render "$NAME" "$PART" "$BOTTLE_FORMAT" sleep 1 -- 2.50.1 From 2ef1e01eaaacc02b86d3c0b274f66d3894b56375 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Tue, 13 Jan 2026 16:18:01 +0100 Subject: [PATCH 10/23] USE_TINY_EARS -> HAS_EARS --- generate_bottle_tag.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/generate_bottle_tag.sh b/generate_bottle_tag.sh index bc90159..dcde946 100755 --- a/generate_bottle_tag.sh +++ b/generate_bottle_tag.sh @@ -6,7 +6,7 @@ set -u VERSION=2.2 LOGO_FILE='""' -TINY_EARS=false +TINY_EARS=true FN=90 NAME="\"$1\"" @@ -53,7 +53,7 @@ render() { echo rendering "$1" "$2" openscad \ -D "\$fn=${FN}" \ - -D "USE_TINY_EARS=${TINY_EARS}" \ + -D "HAS_EARS=${TINY_EARS}" \ -D "LOGO_FILE=${LOGO_FILE}" \ -D "NAME=${NAME}" \ -D "RENDER_COLOR_CLIP=${CLIP}" \ -- 2.50.1 From 5172886c422869e48e017e48c760ef4c0cdf4994 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Wed, 14 Jan 2026 17:22:24 +0100 Subject: [PATCH 11/23] typo --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index 3d11766..c8dbaf9 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -8,7 +8,7 @@ * * This file was modified again by djerun to use the catars printed * by c3cat: https://www.printables.com/model/35076-cat-ears - * as the logo and optionally allow use the easrs from + * as the logo and optionally allow use the ears from * https://www.thingiverse.com/thing:5029374 * printed at scale 0.2 as glue-ins for additional ears. * -- 2.50.1 From f91c00bd74313f7a123919259ea3a7f836f76f0a Mon Sep 17 00:00:00 2001 From: Ajabep Date: Wed, 14 Jan 2026 17:26:49 +0100 Subject: [PATCH 12/23] Fix anti-aliasing problem in preview Was solve previously with magic value and was not documented. --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index c8dbaf9..c33dcdb 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -209,8 +209,10 @@ module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="c3cat", font="write/o } cylinder(r1=rl+width/2, r2=ru+width/2, h=ht); } - translate([0, 0, -1]) - cylinder(r1=rl, r2=ru, h=ht+2); + + clear_anti_aliasing = 0.01; // The margin to avoid empty surfaces in the preview. + translate([0, 0, -clear_anti_aliasing/2]) + cylinder(r1=rl, r2=ru, h=ht+clear_anti_aliasing); // finally, subtract a cube as a gap so we can clip it to the bottle rotate([0, 0, 45]) translate([0, 0, -1]) -- 2.50.1 From 2ac86c04a9035ddcff2d1c9d20ed51a7928c84e4 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Wed, 14 Jan 2026 17:29:00 +0100 Subject: [PATCH 13/23] Fix rotation problem --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 102 +++++++++++------------ 1 file changed, 50 insertions(+), 52 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index c33dcdb..807a29b 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -105,29 +105,28 @@ module render_bottle_clip(name="", font="", logo="", format=0, has_ears=true) { width = 2.5; - scale([5, 5, 5]) - rotate(45, [0, 0, 1]) { - difference() { - bottle_clip( - name=NAME, - font=FONT, - logo=LOGO_FILE, - ru=ru, - rl=rl, - ht=ht, - width=width); - - // Render ears if requested - if (has_ears) { - ears(ht=ht, ru=ru, rl=rl); - } - } + scale([5, 5, 5]) { + difference() { + bottle_clip( + name=NAME, + font=FONT, + logo=LOGO_FILE, + ru=ru, + rl=rl, + ht=ht, + width=width); // Render ears if requested - if (has_ears && RENDER_COLOR_EARS) { - ears(ht=ht, ru=ru, rl=rl); + if (has_ears) { + ears(ht=ht, ru=ru, rl=rl, clip_width=width, ears_style=ears_style); } } + + // Render ears if requested + if (has_ears && RENDER_COLOR_EARS) { + ears(ht=ht, ru=ru, rl=rl, clip_width=width, ears_style=ears_style); + } + } } module name(name, font, rl, ht, ru) { @@ -197,43 +196,42 @@ module logo(logo, rl, ht, ru, width) { module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="c3cat", font="write/orbitron.dxf", logo="") { e = 100; // should be big enough, used for the outer boundary of the text/logo - rotate([0, 0, -45]) { - // main cylinder - if (RENDER_COLOR_CLIP) { - color("black") difference() { - cylinder(r1=rl+width, r2=ru+width, h=ht); - difference() { - union() { - name(name=name, font=font, rl=rl, ht=ht, ru=ru); - logo(logo=logo, rl=rl, ht=ht, ru=ru, width=width); - } - cylinder(r1=rl+width/2, r2=ru+width/2, h=ht); - } - clear_anti_aliasing = 0.01; // The margin to avoid empty surfaces in the preview. - translate([0, 0, -clear_anti_aliasing/2]) - cylinder(r1=rl, r2=ru, h=ht+clear_anti_aliasing); - // finally, subtract a cube as a gap so we can clip it to the bottle - rotate([0, 0, 45]) - translate([0, 0, -1]) - cube([50, 50, 50]); - } - } - // text - if (RENDER_COLOR_TEXT) { - color("orange") difference() { - name(name=name, font=font, rl=rl, ht=ht, ru=ru); + // main cylinder + if (RENDER_COLOR_CLIP) { + color("black") difference() { + cylinder(r1=rl+width, r2=ru+width, h=ht); + difference() { + union() { + name(name=name, font=font, rl=rl, ht=ht, ru=ru); + logo(logo=logo, rl=rl, ht=ht, ru=ru, width=width); + } cylinder(r1=rl+width/2, r2=ru+width/2, h=ht); - outer_cutoff(rl, e, ru, ht, width); } + + clear_anti_aliasing = 0.01; // The margin to avoid empty surfaces in the preview. + translate([0, 0, -clear_anti_aliasing/2]) + cylinder(r1=rl, r2=ru, h=ht+clear_anti_aliasing); + // finally, subtract a cube as a gap so we can clip it to the bottle + rotate([0, 0, 45]) + translate([0, 0, -1]) + cube([50, 50, 50]); } - // logo - if (RENDER_COLOR_LOGO) { - color("yellow") difference() { - logo(logo=logo, rl=rl, ht=ht, ru=ru, width=2*width); - cylinder(r1=rl+width/2, r2=ru+width/2, h=ht); - outer_cutoff(rl, e, ru, ht, width); - } + } + // text + if (RENDER_COLOR_TEXT) { + color("orange") difference() { + name(name=name, font=font, rl=rl, ht=ht, ru=ru); + cylinder(r1=rl+width/2, r2=ru+width/2, h=ht); + outer_cutoff(rl, e, ru, ht, width); + } + } + // logo + if (RENDER_COLOR_LOGO) { + color("yellow") difference() { + logo(logo=logo, rl=rl, ht=ht, ru=ru, width=2*width); + cylinder(r1=rl+width/2, r2=ru+width/2, h=ht); + outer_cutoff(rl, e, ru, ht, width); } } } -- 2.50.1 From 8d0fae76521ca9088b1bc0ca5782ce9d3ad3908a Mon Sep 17 00:00:00 2001 From: Ajabep Date: Wed, 14 Jan 2026 17:30:33 +0100 Subject: [PATCH 14/23] Add the ear generation in this file. --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 381 +++++++++++++++++++++-- 1 file changed, 362 insertions(+), 19 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index 807a29b..74072f0 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -12,13 +12,6 @@ * https://www.thingiverse.com/thing:5029374 * printed at scale 0.2 as glue-ins for additional ears. * - * `Ohren_4.stl` and `catears.stl` need to be placed in `../stls/` - * for the symlinks to work otherwise `catears.stl` needs to be placed - * at `./catears.stl` and `Ohren_4.stl` at `./catear.stl`. - * - * Version of the modification: 1.0 - * - * See examples.scad for examples on how to use this module. * * The contents of this file are licenced under CC-BY-SA 3.0 Unported. * See https://creativecommons.org/licenses/by-sa/3.0/deed for the @@ -73,11 +66,6 @@ $fn = 360; * font: the path to a font for Write.scad. */ -/** - * currently openscad fails to render the original `Ohren_4.stl` outside of the preview mode - * according to [the wiki](https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/FAQ#Why_is_my_imported_STL_file_appearing_with_F5_but_not_F6?) this is the stls fault - * using meshlab to run `Filters` -> `Cleaning and Repairing` -> `Remove T-Vertices` by `Edge-Flip` with `Ratio` of `1000000` before importing the stl works but but two errors remain. - */ scale([0.2, 0.2, 0.2]) { render_bottle_clip( name=NAME, @@ -248,11 +236,366 @@ module outer_cutoff(rl, e, ru, ht, width) { } } -module ear() { - if (USE_TINY_EARS) { - rotate(-90, [0, 0, 1]) union() { - scale([1, 1 ,1]) translate([0, -85]) import("catear.stl"); - scale([1, -1, 1]) translate([0, -85]) import("catear.stl"); - } - } +/** + * Will create a pair of ears to the bottle clip. By default, they are cat ears. + * + * Parameters: + * + * Positioning: + * ht: the height of the bottle clip, used to scale the ears + * ru: the radius on the upper side of the clip, used to position the ears + * rl: the radius on the lower side of the clip, used to position the ears + * clip_width: the thickness of the bottle clip + * + * Ears: + * space_between_ears: Space between both ears. + * ear_tilt: The X orientation of the ears, to give a more organic look. Angle in degree + * + * ear_depth: the depth of the ear + * ear_thickness: the thickness of the ear arcs + * ear_side_len: the length of one side of the ear base triangle + * ear_bend_factor: how much the ear is bent. 1 = half circle, 0.00001 = almost straight + * ear_stretch_factor: how much the ear is stretched, useful for fox ears or this kind of shapes + * + * ear_chamfer: size of the chamfer to apply to the edges + * ear_chamfer_shape: The shape of the chamfer: "cone", "curve", "curve-in", "pyramid", maybe some others + * ear_details: whether to chamfer also the partial arcs + * + */ +module ears(ht, ru, rl, clip_width, space_between_ears = 15, ear_tilt = 15, ear_depth=2, ear_thickness=.6, ear_side_len=6, ear_bend_factor=0.5, ear_stretch_factor=1.2, ear_chamfer=1, ear_chamfer_shape="curve", ear_details=true) { + + // This is the radius on which the ears has to be to be centered on the clip. + radius = ru+(clip_width/2); + + + // Math time! + + // The ears are not that bad without this calculation, but I want to improve the base and the join between ears and the clip. + // Actually, by tilting the ear, a side of the end will go deeper withing the clip, but the other side may be separated from the clip. + // So we need to translate the ears more deeper within the clip to avoid any separation + // + // So! Trigonometry! + // + // Viewed from the right of the ear, an ear is like that: + // + // A + // |\ + // | \ + // h | \ s + // | \ + // +----\ D + // C l \ + // \ + // \ + // \ + // B + // + // A is the side of the ear which is going upper + // B is the side of the ear which is going deeper + // C is the clip level + // D is the midpoint between A and B + // s is the depth of the ear (from A to B!) + // h is the heigh we are searching for. We will lower the ear of this size. + // l is the half of length of the fingerprint of the ear in the clip. + // ADC is the tilt angle + // ACD is a 90 degree angle + // + // So, to find the h length, we can use s and the angle ADC. + // + // CAH => We don't have the adjacent length. So, we can't use this formula + // SOH => sin(ADC) = h / (s/2) + // TOA => We don't have the adjacent length. So, we can't use this formula + // + // sin(ADC) = h / (s/2) + // sin(ADC) * (s/2) = h + // + // BUT! Since we will raise the Clip level C to the end of the ear A, the print of the ear within the clip will be shifted of `l`! + // So, let's calculate it! + // + // + // CAH => cos(ADC) = l / (s/2) + // SOH => Is not targeting what we want. + // TOA => The opposed side is less precise than the hypothenus, so, we will not use this formula + // + // cos(ADC) = l / (s/2) + // cos(ADC) * (s/2) = l + + tilt_compensation = sin(ear_tilt) * (ear_depth/2); + tilt_shift = cos(ear_tilt) * (ear_depth/2); + + // Pythagoras! + // Viewed from the top, an ear is like that: + // + // A s + // +----- E + // | / + // x | / r + // | / + // |/ + // c + // + // c is the center of the circle forming the top surface of the clip + // E is the perfect position of the ear + // r is the radius, calculated previously + // s is the half of the space between ears + // x is the position of the ears on the Y axis. It's what we want to calculate + // A is the projection of the ears on the Y axis + // A is a 90 degrees angle + // + // x**2 + s**2 = r**2 + // x**2 = r**2 - s**2 + // x = sqrt( r**2 - s**2 ) + s = space_between_ears / 2; + pos_ears_yaxis = sqrt(pow(radius, 2) - pow(s, 2)); + + // Trigonometry! + // Now, lets, rotate the ears to be at tangent to the clip + // + // From the top, an ear is like this: + // + // A s + // +------- E + // | / + // | / + // x | / r + // | / + // | / + // | / + // |/ + // C + // + // E is the position of the ears + // C is the center of the clip + // A is the projection of the ears on the Y axis + // r is the radius + // s is the half of the space between the ears. + // x is the same from the last figure. + // A is a 90 degrees angle + // + // The angle of the ear is the same as the angle ACE. + // So, we need to calculate this angle + // + // Since, we already know every lengths s, r and x, we can choose the formula we want! + // IDK which one is faster, but that could be a nice optimization. + // + // CAH => cos(ACE) = x/r + // SOH => sin(ACE) = s/r + // TOA => tan(ACE) = x/s + // + // Let's choose the sine, since it's linked to the our best raw non-rounded values. It will give us values with the best accuracy. + ears_angle = asin(s/radius); + + // Like that, it's good enough. And it could be committed like that. Actually, my first model (never published it) was like that. + // BUT we can do better and smarter! (and we will probably need to refine it) + + // If we stop our math here, we centered the middle of the ear on the clip. But the end of the ears are NOT centered. + // + // So, we will use (again) Pythagoras! + // We want to position the ear (on its center) to put the end of the ear in the radius. + // + // Form the top, an ear looks like that: + // e + // /| + // / | + // / | + // r / | h + // / | + // / | + // / | + // C /-------+ E + // p + // + // C is the center of the clip + // e is the end of the ear + // E is the middle of the ear. + // r is the Radius + // h is the half of the width of the ear + // E is a 90 degrees angles + // p is wanted length to position correctly the center of the ear. + // + // h**2 + p**2 = r**2 + // p**2 = r**2 - h**2 + // p = sqrt(r**2 - h**2) + + center_radius = sqrt( pow(radius, 2) - pow((ear_side_len+(tilt_shift))/2, 2) ); + + // So, we need to update the projection of the ears on the Y axis, based on center_radius, instead of radius. + pos_ears_yaxis_end_ear = sqrt(pow(center_radius, 2) - pow(s, 2)); + + translate([0, -pos_ears_yaxis_end_ear, ht-tilt_compensation-(ear_thickness/2)]) + rotate(90, [0, 0, 1]) + rotate(90, [0, 1, 0]) + union() { + scale([1, 1 ,1]) + translate([0, space_between_ears/2, 0]) + rotate(ears_angle, [1, 0, 0]) + rotate(-ear_tilt, [0, 1, 0]) + ear( + depth=ear_depth, + thickness=ear_thickness, + side_len=ear_side_len, + bend_factor=ear_bend_factor, + stretch_factor=ear_stretch_factor, + chamfer=ear_chamfer, + chamfer_shape=ear_chamfer_shape, + details=ear_details + ); + scale([1, -1, 1]) + translate([0, space_between_ears/2, 0]) + rotate(ears_angle, [1, 0, 0]) + rotate(-ear_tilt, [0, 1, 0]) + ear( + depth=ear_depth, + thickness=ear_thickness, + side_len=ear_side_len, + bend_factor=ear_bend_factor, + stretch_factor=ear_stretch_factor, + chamfer=ear_chamfer, + chamfer_shape=ear_chamfer_shape, + details=ear_details + ); + } +} + +/** + * Module that creates an ear shape. + * + * Parameters: + * + * depth: the depth of the ear + * thickness: the thickness of the ear arcs + * side_len: the length of one side of the ear base triangle + * bend_factor: how much the ear is bent. 1 = half circle, 0.00001 = almost straight + * stretch_factor: how much the ear is stretched, useful for fox ears or this kind of shapes + * + * chamfer: size of the chamfer to apply to the edges + * chamfer_shape: The shape of the chamfer: "cone", "curve", "curve-in", "pyramid", maybe some others + * details: whether to chamfer also the partial arcs + * + * By default, this module creates an ear. + * + * I don't remember the original author of this module. I refactored it and improved it a bit. Documentation is from me. + */ +module ear(depth, thickness, side_len=30, bend_factor=0.5, stretch_factor=1.2, chamfer=1, chamfer_shape="curve", details=true) { + depth = depth == undef ? 20 : depth ; + thickness = thickness == undef ? 3 : thickness ; + + echo("Generating ONE single ear", + depth = depth, + thickness = thickness, + side_len = side_len, + bend_factor = bend_factor, + stretch_factor = stretch_factor, + chamfer = chamfer, + chamfer_shape = chamfer_shape, + details = details + ); + + $A=[0, side_len/2]; + $B=[0,-side_len/2]; + $C=[-(side_len/2/sin(120))*1.5*stretch_factor, 0]; + $c=sqrt(pow($A.x-$B.x, 2)+pow($A.y-$B.y, 2)); + $b=sqrt(pow($A.x-$C.x, 2)+pow($A.y-$C.y, 2)); + $a=sqrt(pow($C.x-$B.x, 2)+pow($C.y-$B.y, 2)); + $hc=-$C.x; + $alpha=asin($hc/$b); + $beta=$alpha; + $gamma=180-$alpha-$beta; + $delta=180*bend_factor; + $bend_radius=$a/(2*cos(90-$delta/2)); + $bend_offset=$bend_radius*sin(90-$delta/2); + + translate([0, -$c/2, 0]) + rotate($beta, [0, 0, 1]) + translate([0, $a/2, 0]) + translate([$bend_offset, 0, 0]) + color("#00ffff") + chamfer(size=(details)?chamfer:0, child_h=depth, child_bot=-depth/2, shape=chamfer_shape) + partial_ring( + part=$delta/360, + radius=$bend_radius, + thickness=thickness, + height=depth + ); + translate([0, $c/2, 0]) + rotate(-$alpha, [0, 0, 1]) + translate([0, -$b/2, 0]) + translate([$bend_offset, 0, 0]) + color("#ff00ff") + chamfer(size=(details)?chamfer:0, child_h=depth, child_bot=-depth/2, shape=chamfer_shape) + partial_ring( + part=$delta/360, + radius=$bend_radius, + thickness=thickness, + height=depth + ); + translate($A) color("#aaaaaa") + chamfer(size=chamfer, child_h=depth, child_bot=-depth/2, shape=chamfer_shape) + cylinder(h=depth, d=thickness, center=true); + translate($B) color("#bbbbbb") + chamfer(size=chamfer, child_h=depth, child_bot=-depth/2, shape=chamfer_shape) + cylinder(h=depth, d=thickness, center=true); + translate($C) color("#cccccc") + chamfer(size=chamfer, child_h=depth, child_bot=-depth/2, shape=chamfer_shape) + cylinder(h=depth, d=thickness, center=true); +} + +/** + * This module is not mine. + */ +module partial_ring(part, radius, thickness, height) { + rotate(180-180*part, [0, 0, 1]) + rotate_extrude(angle=360*part) + translate([radius, 0]) + square([thickness, height], center=true); +} + +/** + * This module is not mine. + */ +module chamfer(size=2, child_h=5, child_bot=0, shape="curve") { + chamfer_size=size; + + module chamfer_shape() { + if (shape == "cone") { + $fn=16; + cylinder(chamfer_size/2,chamfer_size/2,0); + } else if (shape == "curve") { + $fn=4; + for( y = [0:1/$fn:1]) { + cylinder(chamfer_size/2*(1-y),chamfer_size/2/cos(180/$fn)*y,0); + } + } else if (shape == "curve-in") { + $fn=16; + intersection() { + sphere(chamfer_size/2/cos(180/$fn)); + translate([0,0,chamfer_size/2]) + cube(chamfer_size, center=true); + } + } else if (shape == "pyramid") { + $fn=4; + cylinder(chamfer_size/2/cos(180/$fn),chamfer_size/2,0); + } + } + + module lower_chamfer() { + minkowski() + { + linear_extrude(0.0001) difference() { + square([1000,1000],center=true); + projection()children(0); + } + chamfer_shape(); + } + } + + module upper_chamfer() { + scale([1,1,-1])lower_chamfer()children(); + } + + render()difference() { + children(); + translate([0,0,child_bot])lower_chamfer()children(); + translate([0,0,child_bot+child_h])upper_chamfer()children(); + } } -- 2.50.1 From 8b7f829cffd9bc183e787a6e70d9fb511ddb6670 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Wed, 14 Jan 2026 21:35:12 +0100 Subject: [PATCH 15/23] typo --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index 74072f0..ccc1400 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -315,7 +315,7 @@ module ears(ht, ru, rl, clip_width, space_between_ears = 15, ear_tilt = 15, ear_ // // CAH => cos(ADC) = l / (s/2) // SOH => Is not targeting what we want. - // TOA => The opposed side is less precise than the hypothenus, so, we will not use this formula + // TOA => The opposed side is less precise than the hypotenuse, so, we will not use this formula // // cos(ADC) = l / (s/2) // cos(ADC) * (s/2) = l -- 2.50.1 From 9bebad59c39e150b836f50cdb26c5623c27812f6 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Wed, 14 Jan 2026 21:45:32 +0100 Subject: [PATCH 16/23] Fixing trigonometry --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 50 ++++++++++++++++++------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index ccc1400..a868512 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -480,24 +480,50 @@ module ear(depth, thickness, side_len=30, bend_factor=0.5, stretch_factor=1.2, c depth = depth == undef ? 20 : depth ; thickness = thickness == undef ? 3 : thickness ; - echo("Generating ONE single ear", - depth = depth, - thickness = thickness, - side_len = side_len, - bend_factor = bend_factor, - stretch_factor = stretch_factor, - chamfer = chamfer, - chamfer_shape = chamfer_shape, - details = details - ); - + // $A and $B are the end of the ear + // $C is the higher end point. $A=[0, side_len/2]; $B=[0,-side_len/2]; - $C=[-(side_len/2/sin(120))*1.5*stretch_factor, 0]; + // From the front, an ear looks like: + // + // $C + // /|\ + // / | \ + // / | \ + // /---+---\ + // $B c $A + // + // c is the origin of the ear. + // $Bc$C and $Ac$C are 90deg angles. + // + // The length [$B $A] is `side_len` + // Thus, [$B c] == [c $A] == side_len/2 + // + // Since SOH, sin(angle) = Opposed / Hypotenuse + // sin(angle) = Opposed / Hypotenuse + // sin(angle) * Hypotenuse = Opposed + // Hypotenuse = Opposed / sin(angle) + // + // Thus, the `(side_len/2/sin(120))` implies that "The opposed side of the 120 angle is `(side_len/2)` length" and is the formula to know the size of the hypotenuse. + // Moreover, a 120deg angle in a right rectangle does not exists: the sum of all the angles of a triangle needs to be 180deg. I think the author tried, instead, to use the 60 as an angle, since the sinus is the same. + // + // The correct formula should be `((side_len/2)*tan(60))`: since $C$B$A should a 60deg angle to have an equilateral `$A$B$C` triangle, the product of the tangent ("TOA") of this angle and the adjacent segment [$B c] will give us the size of length of the opposed segment [$C c]. + // + // MOREOVER, the product of the magic number `1.5` with the divisor `sin(120)`... is actually the result of the `tan(60)`! + // + // The original formula was: + // $C=[-(side_len/2/sin(120))*1.5*stretch_factor, 0]; + $C=[-((side_len/2)*tan(60))*stretch_factor, 0]; + + // $a, $b and $c are the distance between the different points of the triangle. $c=sqrt(pow($A.x-$B.x, 2)+pow($A.y-$B.y, 2)); $b=sqrt(pow($A.x-$C.x, 2)+pow($A.y-$C.y, 2)); $a=sqrt(pow($C.x-$B.x, 2)+pow($C.y-$B.y, 2)); + + // $hc is the height of $C. $hc=-$C.x; + + // If I correctly understood, $alpha should be the angle of the $B$A$C. This should be 60deg... but is not. idk why. $alpha=asin($hc/$b); $beta=$alpha; $gamma=180-$alpha-$beta; -- 2.50.1 From 37c2e71138bef14bff5bf6ce8e9be4db15dd3014 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Thu, 15 Jan 2026 18:24:34 +0100 Subject: [PATCH 17/23] Add 2 styles for ears --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 72 +++++++++++++++++++++--- generate_bottle_tag.sh | 4 +- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index a868512..b2f3a7c 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -32,8 +32,8 @@ FONT = ""; // empty string is for Orbitron font // Format of the bottle clip you want to create. FORMAT = 3; // [0=Club Mate 50cL, 1=Long Neck as 25cL Club Mate or Fritz-kola, 2=Euroform 2, 3=Steinie bottles] -// Append ears to the bottle clip -HAS_EARS = true; +// Style of the ears to add... or not add. This parameter can add a LOT of calculation time. +EARS_STYLE = 0; // [0=No ears, 1=Just arcs, 2=Filled ears with the same thickness, 3=Filled ears with the a thinner thickness inside] /* [Render] */ // Whether to render the clip, the body. @@ -72,10 +72,10 @@ scale([0.2, 0.2, 0.2]) { font=FONT, logo=LOGO_FILE, format=FORMAT, - has_ears=HAS_EARS); + ears_style=EARS_STYLE); } -module render_bottle_clip(name="", font="", logo="", format=0, has_ears=true) { +module render_bottle_clip(name="", font="", logo="", format=0, ears_style=1) { name = name == "" ? "c3cat" : name ; font = font == "" ? "write/orbitron.dxf" : font ; @@ -105,13 +105,13 @@ module render_bottle_clip(name="", font="", logo="", format=0, has_ears=true) { width=width); // Render ears if requested - if (has_ears) { + if (ears_style > 0) { ears(ht=ht, ru=ru, rl=rl, clip_width=width, ears_style=ears_style); } } // Render ears if requested - if (has_ears && RENDER_COLOR_EARS) { + if (ears_style > 0 && RENDER_COLOR_EARS) { ears(ht=ht, ru=ru, rl=rl, clip_width=width, ears_style=ears_style); } } @@ -262,7 +262,7 @@ module outer_cutoff(rl, e, ru, ht, width) { * ear_details: whether to chamfer also the partial arcs * */ -module ears(ht, ru, rl, clip_width, space_between_ears = 15, ear_tilt = 15, ear_depth=2, ear_thickness=.6, ear_side_len=6, ear_bend_factor=0.5, ear_stretch_factor=1.2, ear_chamfer=1, ear_chamfer_shape="curve", ear_details=true) { +module ears(ht, ru, rl, clip_width, space_between_ears = 15, ear_tilt = 15, ear_depth=2, ear_thickness=.6, ear_side_len=6, ear_bend_factor=0.5, ear_stretch_factor=1.2, ear_chamfer=1, ear_chamfer_shape="curve", ear_details=true, ears_style=1) { // This is the radius on which the ears has to be to be centered on the clip. radius = ru+(clip_width/2); @@ -430,7 +430,8 @@ module ears(ht, ru, rl, clip_width, space_between_ears = 15, ear_tilt = 15, ear_ translate([0, space_between_ears/2, 0]) rotate(ears_angle, [1, 0, 0]) rotate(-ear_tilt, [0, 1, 0]) - ear( + ear_with_style( + style=ears_style, depth=ear_depth, thickness=ear_thickness, side_len=ear_side_len, @@ -444,7 +445,8 @@ module ears(ht, ru, rl, clip_width, space_between_ears = 15, ear_tilt = 15, ear_ translate([0, space_between_ears/2, 0]) rotate(ears_angle, [1, 0, 0]) rotate(-ear_tilt, [0, 1, 0]) - ear( + ear_with_style( + style=ears_style, depth=ear_depth, thickness=ear_thickness, side_len=ear_side_len, @@ -457,6 +459,58 @@ module ears(ht, ru, rl, clip_width, space_between_ears = 15, ear_tilt = 15, ear_ } } +module ear_with_style(style = 1, depth, thickness, side_len=30, bend_factor=0.5, stretch_factor=1.2, chamfer=1, chamfer_shape="curve", details=true) { + if (style == 1 || style == 2 || style == 3) { + // Style 1: Just arcs + // Style 2: Filled ears with the same thickness + // Style 3: Filled ears with the a thinner thickness inside + + ear( + depth=depth, + thickness=thickness, + side_len=side_len, + bend_factor=bend_factor, + stretch_factor=stretch_factor, + chamfer=chamfer, + chamfer_shape=chamfer_shape, + details=details + ); + } + + + if (style == 2 || style == 3) { + // Style 2: Filled ears with the same thickness + // Style 3: Filled ears with the a thinner thickness inside + + thickness_steps = thickness*0.5; + depth_steps = (depth-thickness)/((side_len / thickness_steps)); + + for (round_side_len = [side_len-thickness_steps:-thickness_steps:0]) { + // round_side_len = side_len - (thickness_steps * x) + // (thickness_steps * x) + round_side_len = side_len + // (thickness_steps * x) = side_len - round_side_len + // x = (side_len - round_side_len) / thickness_steps + x = (side_len - round_side_len) / thickness_steps; + round_shift = depth_steps * x; + current_depth = depth - (style == 3 ? round_shift : 0); + translation = style == 2 ? 0 : round_shift / 2; + + translate([0, 0, translation]) + color("green") + ear( + depth=current_depth, + thickness=thickness, + side_len=round_side_len, + bend_factor=bend_factor, + stretch_factor=stretch_factor, + chamfer=chamfer, + chamfer_shape=chamfer_shape, + details=true + ); + } + } +} + /** * Module that creates an ear shape. * diff --git a/generate_bottle_tag.sh b/generate_bottle_tag.sh index dcde946..3c726dc 100755 --- a/generate_bottle_tag.sh +++ b/generate_bottle_tag.sh @@ -6,8 +6,8 @@ set -u VERSION=2.2 LOGO_FILE='""' -TINY_EARS=true FN=90 +EARS_STYLE=1 NAME="\"$1\"" @@ -53,7 +53,7 @@ render() { echo rendering "$1" "$2" openscad \ -D "\$fn=${FN}" \ - -D "HAS_EARS=${TINY_EARS}" \ + -D "EARS_STYLE=${EARS_STYLE}" \ -D "LOGO_FILE=${LOGO_FILE}" \ -D "NAME=${NAME}" \ -D "RENDER_COLOR_CLIP=${CLIP}" \ -- 2.50.1 From 7c49687f9cbdf258f970400cd6e9998d68cb1dd4 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Thu, 15 Jan 2026 18:26:36 +0100 Subject: [PATCH 18/23] Add a new ear style --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index b2f3a7c..7af3bbc 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -30,10 +30,10 @@ LOGO_FILE = ""; // empty string is catear model FONT = ""; // empty string is for Orbitron font // Format of the bottle clip you want to create. -FORMAT = 3; // [0=Club Mate 50cL, 1=Long Neck as 25cL Club Mate or Fritz-kola, 2=Euroform 2, 3=Steinie bottles] +FORMAT = 0; // [0=Club Mate 50cL, 1=Long Neck as 25cL Club Mate or Fritz-kola, 2=Euroform 2, 3=Steinie bottles] // Style of the ears to add... or not add. This parameter can add a LOT of calculation time. -EARS_STYLE = 0; // [0=No ears, 1=Just arcs, 2=Filled ears with the same thickness, 3=Filled ears with the a thinner thickness inside] +EARS_STYLE = 0; // [0=No ears, 1=Just arcs, 2=Filled ears with the same thickness, 3=Filled ears with the a thinner thickness inside, 4=Filled ears with the same thickness but shifted] /* [Render] */ // Whether to render the clip, the body. @@ -460,10 +460,11 @@ module ears(ht, ru, rl, clip_width, space_between_ears = 15, ear_tilt = 15, ear_ } module ear_with_style(style = 1, depth, thickness, side_len=30, bend_factor=0.5, stretch_factor=1.2, chamfer=1, chamfer_shape="curve", details=true) { - if (style == 1 || style == 2 || style == 3) { + if (style == 1 || style == 2 || style == 3 || style == 4) { // Style 1: Just arcs // Style 2: Filled ears with the same thickness // Style 3: Filled ears with the a thinner thickness inside + // Style 4: Filled ears with the same thickness but shifted ear( depth=depth, @@ -478,9 +479,10 @@ module ear_with_style(style = 1, depth, thickness, side_len=30, bend_factor=0.5, } - if (style == 2 || style == 3) { + if (style == 2 || style == 3 || style == 4) { // Style 2: Filled ears with the same thickness // Style 3: Filled ears with the a thinner thickness inside + // Style 4: Filled ears with the same thickness but shifted thickness_steps = thickness*0.5; depth_steps = (depth-thickness)/((side_len / thickness_steps)); -- 2.50.1 From 7bb36f9f89e957f2dc5cd9d0657f20910b382001 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Thu, 15 Jan 2026 18:29:59 +0100 Subject: [PATCH 19/23] Change a preview color Improve the third dimension perception --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index 7af3bbc..d8557f0 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -187,7 +187,7 @@ module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="c3cat", font="write/o // main cylinder if (RENDER_COLOR_CLIP) { - color("black") difference() { + color("#006168") difference() { cylinder(r1=rl+width, r2=ru+width, h=ht); difference() { union() { -- 2.50.1 From ebc235de4281b81c9fc7996896f74d4e4f3f89ed Mon Sep 17 00:00:00 2001 From: Ajabep Date: Thu, 15 Jan 2026 19:57:13 +0100 Subject: [PATCH 20/23] Add documentation --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 82 +++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index d8557f0..0542908 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -12,6 +12,10 @@ * https://www.thingiverse.com/thing:5029374 * printed at scale 0.2 as glue-ins for additional ears. * + * This file has also being modified and reformatted by Ajabep to: + * - be easier to understand + * - be easier to modify and extend + * - be easier to use and build * * The contents of this file are licenced under CC-BY-SA 3.0 Unported. * See https://creativecommons.org/licenses/by-sa/3.0/deed for the @@ -75,6 +79,30 @@ scale([0.2, 0.2, 0.2]) { ears_style=EARS_STYLE); } +/** + * Render a bottle clip with a name, logo and ears (depending the `ears_style` argument) for a bottle given as `format`. + * + * Parameters: + * name: The name to write on the clip + * font: The font file to use + * logo: The logo file to print on the clip + * + * format: id of the bottle clip format + * 0: Club Mate 50cL + * 1: Long Neck as 25cL Club Mate or Fritz-kola + * 2: Euroform 2 + * 3: Steinie bottles (with logo and name) + * + * ear_style: id of the ear style we want: + * -1 or less: Should NEVER being used. Unknown behavior. + * 0: No ears, just a bottle clip with a name and a logo. + * 1: Just arcs. + * 2: Filled ears with the same thickness + * 3: Filled ears with the a thinner thickness inside + * 4: Filled ears with the same thickness but shifted + * + * By default, thus function will generate a clip for a 50cL Club-Mate Bottle, with a headband with cat ears as a logo, and with a cat ears of the clip formed by 2 arcs. + */ module render_bottle_clip(name="", font="", logo="", format=0, ears_style=1) { name = name == "" ? "c3cat" : name ; font = font == "" ? "write/orbitron.dxf" : font ; @@ -182,6 +210,25 @@ module logo(logo, rl, ht, ru, width) { } } +/** + * Creates a Bootle Clip with logo and/or text, depending what is asked using the RENDER_COLOR_* variables. + * + * Parameters: + * + * Size: + * ht: the height of the bottle clip + * ru: the radius on the upper side of the clip + * rl: the radius on the lower side of the clip + * width: the thickness of the bottle clip + * + * name: The name to write on the clip + * font: The font file to use + * logo: The logo file to print on the clip + * + * By default, will create a Club-Mate 0.5L bottle clip. + * + * An empty logo file will create a headband with cat ears as a logo. + */ module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="c3cat", font="write/orbitron.dxf", logo="") { e = 100; // should be big enough, used for the outer boundary of the text/logo @@ -261,6 +308,12 @@ module outer_cutoff(rl, e, ru, ht, width) { * ear_chamfer_shape: The shape of the chamfer: "cone", "curve", "curve-in", "pyramid", maybe some others * ear_details: whether to chamfer also the partial arcs * + * ear_style: id of the ear style we want: + * 0 or less: Should NEVER being used. Unknown behavior. + * 1: Just arcs. + * 2: Filled ears with the same thickness + * 3: Filled ears with the a thinner thickness inside + * 4: Filled ears with the same thickness but shifted */ module ears(ht, ru, rl, clip_width, space_between_ears = 15, ear_tilt = 15, ear_depth=2, ear_thickness=.6, ear_side_len=6, ear_bend_factor=0.5, ear_stretch_factor=1.2, ear_chamfer=1, ear_chamfer_shape="curve", ear_details=true, ears_style=1) { @@ -459,6 +512,30 @@ module ears(ht, ru, rl, clip_width, space_between_ears = 15, ear_tilt = 15, ear_ } } +/** + * Module that creates an ear given a style id. + * + * Parameters: + * + * style: id of the ear style we want: + * 0 or less: Should NEVER being used. Unknown behavior. + * 1: Just arcs. + * 2: Filled ears with the same thickness + * 3: Filled ears with the a thinner thickness inside + * 4: Filled ears with the same thickness but shifted + * + * depth: the depth of the ear + * thickness: the thickness of the ear arcs + * side_len: the length of one side of the ear base triangle + * bend_factor: how much the ear is bent. 1 = half circle, 0.00001 = almost straight + * stretch_factor: how much the ear is stretched, useful for fox ears or this kind of shapes + * + * chamfer: size of the chamfer to apply to the edges + * chamfer_shape: The shape of the chamfer: "cone", "curve", "curve-in", "pyramid", maybe some others + * details: whether to chamfer also the partial arcs + * + * By default, this module creates a cat ear countour. + */ module ear_with_style(style = 1, depth, thickness, side_len=30, bend_factor=0.5, stretch_factor=1.2, chamfer=1, chamfer_shape="curve", details=true) { if (style == 1 || style == 2 || style == 3 || style == 4) { // Style 1: Just arcs @@ -528,7 +605,7 @@ module ear_with_style(style = 1, depth, thickness, side_len=30, bend_factor=0.5, * chamfer_shape: The shape of the chamfer: "cone", "curve", "curve-in", "pyramid", maybe some others * details: whether to chamfer also the partial arcs * - * By default, this module creates an ear. + * By default, this module creates a cat ear. * * I don't remember the original author of this module. I refactored it and improved it a bit. Documentation is from me. */ @@ -579,9 +656,10 @@ module ear(depth, thickness, side_len=30, bend_factor=0.5, stretch_factor=1.2, c // $hc is the height of $C. $hc=-$C.x; - // If I correctly understood, $alpha should be the angle of the $B$A$C. This should be 60deg... but is not. idk why. + // $alpha should be the angle of the $B$A$C. This should is 60deg if `stretch_factor` is `1`, but this angle change with this factor. $alpha=asin($hc/$b); $beta=$alpha; + // $gamma is the $A$C$B angle. $gamma=180-$alpha-$beta; $delta=180*bend_factor; $bend_radius=$a/(2*cos(90-$delta/2)); -- 2.50.1 From 536b0e5eb14687daeda380921831080c5626ad3c Mon Sep 17 00:00:00 2001 From: Ajabep Date: Thu, 15 Jan 2026 19:57:25 +0100 Subject: [PATCH 21/23] remove dead code --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index 0542908..9baee81 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -181,16 +181,6 @@ module logo(logo, rl, ht, ru, width) { with_rake=false ); } else { - // The logo has been split in 3 parts. // well was... TODO -/* - rotate([0, 0, -48]) translate([0, 0, ht*3/4-0.1]) - rotate([90, 0, 0]) - scale([0.9, 0.9, 1]) - scale([ht/100, ht/100, 1]) - translate([-25, -29, 0.5]) - linear_extrude(height=max(ru,rl)*2) - import("logo_1.dxf"); -*/ translate([0, 0, ht*3/4-0.1]) rotate([90, 0, 0]) scale([0.8, 0.8, 1]) @@ -198,15 +188,6 @@ module logo(logo, rl, ht, ru, width) { translate([-18, -22, 0.5]) linear_extrude(height=max(ru, rl)*2) import(logo); -/* - rotate([0, 0, 44]) translate([0, 0, ht*3/4-0.1]) - rotate([90, 0, 0]) - scale([0.7, 0.7, 1]) - scale([ht/100, ht/100, 1]) - translate([-25, -26, 0.5]) - linear_extrude(height=max(ru,rl)*2) - import("logo_3.dxf"); -*/ } } -- 2.50.1 From 79710a9d662fec818a6758896bbe18828a896072 Mon Sep 17 00:00:00 2001 From: Ajabep Date: Thu, 15 Jan 2026 21:36:34 +0100 Subject: [PATCH 22/23] Add parameters to customize the ears at a script level --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 60 ++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index 9baee81..ee43243 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -39,6 +39,30 @@ FORMAT = 0; // [0=Club Mate 50cL, 1=Long Neck as 25cL Club Mate or Fritz-kola, 2 // Style of the ears to add... or not add. This parameter can add a LOT of calculation time. EARS_STYLE = 0; // [0=No ears, 1=Just arcs, 2=Filled ears with the same thickness, 3=Filled ears with the a thinner thickness inside, 4=Filled ears with the same thickness but shifted] +/* [Ear Parameters] */ + +// Space between both ears. +SPACE_BETWEEN_EARS = 13; // .1 + +// The X orientation of the ears, to give a more organic look. Angle in degree +EAR_TILT = 15; // .1 + +// The depth of the ear +EAR_DEPTH = 2; // .1 + +// The thickness of the ear arcs +EAR_THICKNESS = .6; // .1 + +// The width of the ear +EAR_SIDE_LEN = 6; + +// How much the ear is bent. 1 = half circle, 0.00001 = almost straight +EAR_BEND_FACTOR = 0.5; // [.01:.01:1] + +// How much the ear is stretched, useful for fox ears or this kind of shapes +EAR_STRETCH_FACTOR = 1.2; + + /* [Render] */ // Whether to render the clip, the body. RENDER_COLOR_CLIP = true; @@ -134,13 +158,39 @@ module render_bottle_clip(name="", font="", logo="", format=0, ears_style=1) { // Render ears if requested if (ears_style > 0) { - ears(ht=ht, ru=ru, rl=rl, clip_width=width, ears_style=ears_style); + ears( + ht=ht, + ru=ru, + rl=rl, + clip_width=width, + space_between_ears=SPACE_BETWEEN_EARS, + ear_tilt=EAR_TILT, + ear_depth=EAR_DEPTH, + ear_thickness=EAR_THICKNESS, + ear_side_len=EAR_SIDE_LEN, + ear_bend_factor=EAR_BEND_FACTOR, + ear_stretch_factor=EAR_STRETCH_FACTOR, + ears_style=ears_style + ); } } // Render ears if requested if (ears_style > 0 && RENDER_COLOR_EARS) { - ears(ht=ht, ru=ru, rl=rl, clip_width=width, ears_style=ears_style); + ears( + ht=ht, + ru=ru, + rl=rl, + clip_width=width, + space_between_ears=SPACE_BETWEEN_EARS, + ear_tilt=EAR_TILT, + ear_depth=EAR_DEPTH, + ear_thickness=EAR_THICKNESS, + ear_side_len=EAR_SIDE_LEN, + ear_bend_factor=EAR_BEND_FACTOR, + ear_stretch_factor=EAR_STRETCH_FACTOR, + ears_style=ears_style + ); } } } @@ -281,7 +331,7 @@ module outer_cutoff(rl, e, ru, ht, width) { * * ear_depth: the depth of the ear * ear_thickness: the thickness of the ear arcs - * ear_side_len: the length of one side of the ear base triangle + * ear_side_len: the width of the ear * ear_bend_factor: how much the ear is bent. 1 = half circle, 0.00001 = almost straight * ear_stretch_factor: how much the ear is stretched, useful for fox ears or this kind of shapes * @@ -507,7 +557,7 @@ module ears(ht, ru, rl, clip_width, space_between_ears = 15, ear_tilt = 15, ear_ * * depth: the depth of the ear * thickness: the thickness of the ear arcs - * side_len: the length of one side of the ear base triangle + * side_len: the width of the ear * bend_factor: how much the ear is bent. 1 = half circle, 0.00001 = almost straight * stretch_factor: how much the ear is stretched, useful for fox ears or this kind of shapes * @@ -578,7 +628,7 @@ module ear_with_style(style = 1, depth, thickness, side_len=30, bend_factor=0.5, * * depth: the depth of the ear * thickness: the thickness of the ear arcs - * side_len: the length of one side of the ear base triangle + * side_len: the width of the ear * bend_factor: how much the ear is bent. 1 = half circle, 0.00001 = almost straight * stretch_factor: how much the ear is stretched, useful for fox ears or this kind of shapes * -- 2.50.1 From daee24ebc9fde5056e379cf467318741fd04631b Mon Sep 17 00:00:00 2001 From: Ajabep Date: Fri, 16 Jan 2026 23:06:54 +0100 Subject: [PATCH 23/23] Adjust the thickness Less precise, but faster and smaller files --- c3cat-bottle-clip/c3cat-bottle-clip.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index ee43243..0abfe50 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -51,7 +51,7 @@ EAR_TILT = 15; // .1 EAR_DEPTH = 2; // .1 // The thickness of the ear arcs -EAR_THICKNESS = .6; // .1 +EAR_THICKNESS = 1; // .1 // The width of the ear EAR_SIDE_LEN = 6; -- 2.50.1