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/c3cat-bottle-clip/c3cat-bottle-clip.scad b/c3cat-bottle-clip/c3cat-bottle-clip.scad index 19a51e7..0abfe50 100644 --- a/c3cat-bottle-clip/c3cat-bottle-clip.scad +++ b/c3cat-bottle-clip/c3cat-bottle-clip.scad @@ -8,17 +8,14 @@ * * 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. * - * `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. + * 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 @@ -28,14 +25,57 @@ 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 + +// 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, 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, 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 = 1; // .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; +// Whether to render the text part. +RENDER_COLOR_TEXT = true; +// Whether to render the logo part. +RENDER_COLOR_LOGO = true; +// Whether to render the cat ears part. +RENDER_COLOR_EARS = true; + +// Set the number of facets for circles. $fn = 360; -NAME = "c3cat"; -LOGO_FILE = ""; // empty string is catear model -RENDER_COLOR_ONE = true; -RENDER_COLOR_TWO = true; -RENDER_COLOR_THREE = true; -USE_TINY_EARS = true; /** * Creates one instance of a bottle clip name tag. The default values are @@ -48,41 +88,117 @@ 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. */ -/** - * 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]) { - 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(); + render_bottle_clip( + name=NAME, + font=FONT, + logo=LOGO_FILE, + format=FORMAT, + 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 ; + + if (format < 0 || format > 3) { + assert(false, str("Unknown format ", format, ".")); } - if (RENDER_COLOR_TWO) { - color("orange") - translate([ 15*5, 0*5, 18*5]) - rotate(80, [0, 1, 0]) - catear(); - } - if (RENDER_COLOR_THREE) { - color("yellow") - translate([-15*5, 0*5, 18*5]) - rotate(-80, [0, 1, 0]) - catear(); + + // 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]) { + difference() { + bottle_clip( + name=NAME, + font=FONT, + logo=LOGO_FILE, + ru=ru, + rl=rl, + ht=ht, + width=width); + + // Render ears if requested + if (ears_style > 0) { + 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, + 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 + ); + } } } 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 +207,23 @@ module name(name, font, rl, ht, ru) { } module logo(logo, rl, ht, ru, width) { - echo("logo: ", logo); + 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); - 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,106 +231,562 @@ 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]) - 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"); -*/ + 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); } } +/** + * 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 - rotate([0,0,-45]) { - // main cylinder - if (RENDER_COLOR_ONE) { - 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); + e = 100; // should be big enough, used for the outer boundary of the text/logo + + // main cylinder + if (RENDER_COLOR_CLIP) { + color("#006168") 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); } - 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 - rotate([0, 0, 45]) - translate([0,0,-1]) - cube([50,50,50]); - } - } - // text - if (RENDER_COLOR_TWO) { - 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); } + + 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_THREE) { - 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); } } } 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); } } -module catear() { - 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 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 + * + * 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 + * + * 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) { + + // 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 hypotenuse, 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_with_style( + style=ears_style, + 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_with_style( + style=ears_style, + 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 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 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 + * + * 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 + // 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, + 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 == 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)); + + 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 + ); } } } /** - * 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="c3cat", width=2.5, font="write/orbitron.dxf") { - bottle_clip(name=name, ru=13, rl=15, ht=26, width=width, font=font, logo=""); + * Module that creates an ear shape. + * + * Parameters: + * + * depth: the depth of the ear + * thickness: the thickness of the ear arcs + * 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 + * + * 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. + * + * 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 ; + + // $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]; + // 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; + + // $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)); + $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); } /** - * The Steinie-Tag has been removed since it does not support logos. -**/ + * 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(); + } +} diff --git a/generate_bottle_tag.sh b/generate_bottle_tag.sh index 8ea378d..3c726dc 100755 --- a/generate_bottle_tag.sh +++ b/generate_bottle_tag.sh @@ -6,28 +6,37 @@ set -u VERSION=2.2 LOGO_FILE='""' -TINY_EARS=false FN=90 +EARS_STYLE=1 NAME="\"$1\"" -# usage: render NAME PART +# usage: render NAME PART [BOTTLE_FORMAT] render() { case "$2" in body) - ONE=true - TWO=false - THREE=false + CLIP=true + TEXT=false + LOGO=false + EARS=false ;; name) - ONE=false - TWO=true - THREE=false + CLIP=false + TEXT=true + LOGO=false + EARS=false ;; logo) - ONE=false - TWO=false - THREE=true + CLIP=false + TEXT=false + LOGO=true + EARS=false + ;; + ears) + CLIP=false + TEXT=false + LOGO=false + EARS=true ;; *) echo 'fatal: invalid part' >&2 @@ -35,23 +44,34 @@ render() { ;; esac + if [ $# -lt 3 ]; then + BOTTLE_FORMAT="0" + else + BOTTLE_FORMAT="$3" + fi + echo rendering "$1" "$2" openscad \ -D "\$fn=${FN}" \ - -D "USE_TINY_EARS=${TINY_EARS}" \ + -D "EARS_STYLE=${EARS_STYLE}" \ -D "LOGO_FILE=${LOGO_FILE}" \ -D "NAME=${NAME}" \ - -D "RENDER_COLOR_ONE=${ONE}" \ - -D "RENDER_COLOR_TWO=${TWO}" \ - -D "RENDER_COLOR_THREE=${THREE}" \ - -o "stls/c3cat-bottle-clip-v${VERSION}_${NAME}_${PART}.stl" \ + -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 } 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 ears + do + render "$NAME" "$PART" "$BOTTLE_FORMAT" + sleep 1 + done done 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