Compare commits

..

No commits in common. "65b9c44579faef3c1208355b61f26e9e2662641c" and "4669017d0ce9047a1354dda3c5d9c685f769b7e5" have entirely different histories.

11 changed files with 2730 additions and 2283 deletions

View File

@ -23,21 +23,16 @@ class Generator:
GENERATOR_SCAD_FILE_NAME = "generator.scad"
GENERATED_STL_FILE_NAME = "generated.stl"
def __init__(self, name: str, tempdir: Path | str, logo: str = None):
def __init__(self, name: str, tempdir: Path | str):
self._name = name
self._tempdir = Path(tempdir)
# sanitize input
if "/" in logo:
raise ValueError("invalid logo name")
self._logo = Path(logo).name
def _generate_scad_template(self) -> str:
return f"""
use <bottle-clip.scad>
$fn=180;
// one name tag for 0.5l Club Mate and similar bottles
bottle_clip(name="{self._name}", logo="thing-logos/{self._logo}.dxf");
bottle_clip(name="{self._name}", logo="thing-logos/fablab-cube2.dxf");
"""
def _generate_files_in_temp_dir(self):
@ -88,7 +83,6 @@ async def generate():
raise ValueError
name = data["name"]
logo = data.get("logo", None)
except (TypeError, KeyError, ValueError):
abort(400)
@ -96,7 +90,7 @@ async def generate():
async with semaphore:
with tempfile.TemporaryDirectory(prefix="fablab-bottle-clip-generator-") as tempdir:
generator = Generator(name, tempdir, logo)
generator = Generator(name, tempdir)
generated_stl_file_name = await generator.generate_stl()

View File

@ -11,25 +11,6 @@
include <write/Write.scad>
/**
* Module for multi colored print.
* All children are the given color.
* Using the global CURRENT_COLOR it is possible to only render and export everything of one color.
* Doing this for all colors the resulting stls can be put together again to a multi filament print in the slicer.
* If CURRENT_COLOR is set to "ALL" all colors are displayed.
* If color is "DEFAULT", it is not colored in the preview.
* Inspired by https://erik.nygren.org/2018-3dprint-multicolor-openscad.html
*/
module multicolor(color) {
if (is_undef(CURRENT_COLOR) || CURRENT_COLOR == "ALL" || CURRENT_COLOR == color) {
if (color != "DEFAULT") {
color(color) children();
} else {
children();
}
}
}
/**
* Creates one instance of a bottle clip name tag. The default values are
* suitable for 0.5l Club Mate bottles (and similar bottles). By default, logo
@ -40,41 +21,38 @@ module multicolor(color) {
* 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.
* gap: width of the opening gap, in degrees. For rigid materials this value
* usually needs to be near 180 (but if you set it to >= 180, you won't have
* anything left for holding the clip on the bottle). For flexible materials
* like Ninjaflex, choose something near 0. For springy materials like PLA or
* ABS, 90 has proven to be a good value.
* usually needs to be near 180 (but if you set it to >= 180, you won't have
* anything left for holding the clip on the bottle). For flexible materials
* like Ninjaflex, choose something near 0. For springy materials like PLA or
* ABS, 90 has proven to be a good value.
* logo: the path to a DXF file representing a logo that should be put above
* the name. Logo files should be no larger than 50 units in height and should
* be centered on the point (25,25). Also all units in the DXF file should be
* in mm. This parameter can be empty; in this case, the text uses the total
* height of the name tag.
* the name. Logo files should be no larger than 50 units in height and should
* be centered on the point (25,25). Also all units in the DXF file should be
* in mm. This parameter can be empty; in this case, the text uses the total
* height of the name tag.
* font: the path to a font for Write.scad.
* bg_color: The color of the background (the clip itself)
* text_color: The color of the text
* logo_color: The color of the logo
*/
module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="", gap=90,
logo="thing-logos/stratum0-lowres.dxf", font="write/orbitron.dxf",
bg_color="DEFAULT", text_color="DEFAULT", logo_color="DEFAULT") {
logo="thing-logos/stratum0-lowres.dxf", font="write/orbitron.dxf") {
e=100; // should be big enough, used for the outer boundary of the text/logo
difference() {
rotate([0,0,-45]) union() {
// main cylinder
multicolor(bg_color) cylinder(r1=rl+width, r2=ru+width, h=ht);
cylinder(r1=rl+width, r2=ru+width, h=ht);
// text and logo
if(logo == "") {
multicolor(text_color) writecylinder(name, [0,0,3], rl+0.5, ht/13*7, h=ht/13*8, t=max(rl,ru),
writecylinder(name, [0,0,3], rl+0.5, ht/13*7, h=ht/13*8, t=max(rl,ru),
font=font);
} else {
multicolor(text_color) writecylinder(name, [0,0,0], rl+0.5, ht/13*7, h=ht/13*4, t=max(rl,ru),
writecylinder(name, [0,0,0], rl+0.5, ht/13*7, h=ht/13*4, t=max(rl,ru),
font=font);
multicolor(logo_color) translate([0,0,ht*3/4-0.1])
translate([0,0,ht*3/4-0.1])
rotate([90,0,0])
scale([ht/100,ht/100,1])
translate([-25,-25,0.5])
@ -111,10 +89,9 @@ module bottle_clip(ru=13, rl=17.5, ht=26, width=2.5, name="", gap=90,
* bottle_clip(), see there for their documentation.
*/
module bottle_clip_longneck(name="", width=2.5, gap=90,
logo="thing-logos/stratum0-lowres.dxf", font="write/orbitron.dxf",
bg_color="DEFAULT", text_color="DEFAULT", logo_color="DEFAULT") {
logo="thing-logos/stratum0-lowres.dxf", font="write/orbitron.dxf") {
bottle_clip(name=name, ru=13, rl=15, ht=26, width=width, logo=logo, gap=gap,
font=font, bg_color=bg_color, text_color=text_color, logo_color=logo_color);
font=font);
}
/**
@ -123,10 +100,9 @@ module bottle_clip_longneck(name="", width=2.5, gap=90,
* reasons, there is no logo, but all other parameters are passed to the module
* bottle_clip(), see there for their documentation.
*/
module bottle_clip_steinie(name="", width=2.5, gap=90, font="write/orbitron.dxf",
bg_color="DEFAULT", text_color="DEFAULT", logo_color="DEFAULT") {
module bottle_clip_steinie(name="", width=2.5, gap=90, font="write/orbitron.dxf") {
bottle_clip(name=name, ru=13, rl=17.5, ht=13, width=width, logo="", gap=gap,
font=font, bg_color=bg_color, text_color=text_color, logo_color=logo_color);
font=font);
}
/*
@ -135,10 +111,9 @@ module bottle_clip_steinie(name="", width=2.5, gap=90, font="write/orbitron.dxf"
* passed to the module bottle_clip(), see there for their documentation.
*/
module bottle_clip_euro2(name="", width=2.5, gap=90,
logo="thing-logos/stratum0-lowres.dxf", font="write/orbitron.dxf",
bg_color="DEFAULT", text_color="DEFAULT", logo_color="DEFAULT") {
bottle_clip(name=name, ru=13, rl=22.5, ht=26, width=width, logo=logo, gap=gap,
font=font, bg_color=bg_color, text_color=text_color, logo_color=logo_color);
logo="thing-logos/stratum0-lowres.dxf", font="write/orbitron.dxf") {
bottle_clip(name=name, ru=13, rl=22.5, ht=26, width=width, logo=logo, gap=gap,
font=font);
}
// vim: set noet ts=2 sw=2 tw=80:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
/* Version 4
Added bold property bold=0 (not bold) bold=1(bolder by 1.1) bold=2(bolder by 1.2)
/* Version 3
Added support for font selection (default is Letters.dxf)
Added WriteCube module
Added Rotate for text (rotates on the plane of the text)
@ -27,7 +26,6 @@
// defaults.
//default settings
bold=0;
center=false;
h = 4; //mm letter height
t = 1; //mm letter thickness
@ -75,19 +73,19 @@ module writecylinder(text,where,radius,height){
if (face=="top" ){
if (center==true){
writecircle(text,where+[0,0,height/2],radius-h,rotate=rotate,font=font,h=h,t=t,
space=space,east=east,west=west,middle=middle,ccw=ccw,bold=bold);
space=space,east=east,west=west,middle=middle,ccw=ccw);
}else{
writecircle(text,where+[0,0,height],radius-h,rotate=rotate,font=font,h=h,t=t,
space=space,east=east,west=west,middle=middle,ccw=ccw,bold=bold);
space=space,east=east,west=west,middle=middle,ccw=ccw);
}
}else{
rotate(180,[1,0,0])
if (center==true){
writecircle(text,where+[0,0,height/2],radius-h,rotate=rotate,font=font,h=h,t=t,
space=space,east=east,west=west,middle=middle,ccw=ccw,bold=bold);
space=space,east=east,west=west,middle=middle,ccw=ccw);
}else{
writecircle(text,where+[0,0,0],radius-h,rotate=rotate,font=font,h=h,t=t,
space=space,east=east,west=west,middle=middle,ccw=ccw,bold=bold);
space=space,east=east,west=west,middle=middle,ccw=ccw);
}
}
@ -98,13 +96,13 @@ module writecylinder(text,where,radius,height){
translate(where)
writethecylinder(text,where,radius,height,r1=radius,r2=radius,h=h,
rotate=rotate,t=t,font=font,face=face,up=up,down=down,
east=east,west=west,center=center,space=space,rounded=rounded,bold=bold);
east=east,west=west,center=center,space=space,rounded=rounded);
} else{
rotate(-mmangle(radius)*(1-abs(rotate)/90),[0,0,1])
translate(where+[0,0,height/2])
writethecylinder(text,where,radius,height,r1=radius,r2=radius,h=h,
rotate=rotate,t=t,font=font,face=face,up=up,down=down,
east=east,west=west,center=center,space=space,rounded=rounded,bold=bold);
east=east,west=west,center=center,space=space,rounded=rounded);
}
// the remarked out code is for cone shaped cylinders (not complete)
// }else{
@ -142,7 +140,7 @@ module writecircle(text,where,radius){
//rotate(90,[1,0,0])
//rotate(90,[0,1,0])
rotate(-270,[0,0,1]) // flip text (botom out = -270)
write(text[r],center=true,h=h,t=t,font=font,bold=bold);
write(text[r],center=true,h=h,t=t,font=font);
}
}
}
@ -156,7 +154,7 @@ module writecircle(text,where,radius){
//rotate(90,[1,0,0])
//rotate(90,[0,1,0])
rotate(270,[0,0,1]) // flip text (botom out = -270)
write(text[r],center=true,h=h,t=t,font=font,bold=bold);
write(text[r],center=true,h=h,t=t,font=font);
}
}
}
@ -178,7 +176,7 @@ module writethecylinder(text,where,radius,height,r1,r2){
translate([radius,0,-r*((rotate)/90*wid)+(len(text)-1)/2*((rotate)/90*wid)])
rotate(90,[1,0,0])
rotate(90,[0,1,0])
write(text[r],center=true,h=h,rotate=rotate,t=t,font=font,bold=bold);
write(text[r],center=true,h=h,rotate=rotate,t=t,font=font);
//echo("zloc=",height/2-r*((rotate)/90*wid)+(len(text)-1)/2*((rotate)/90*wid));
}
@ -206,7 +204,7 @@ module writesphere(text,where,radius){
translate([radius,0,0])
rotate(90,[1,0,0])
rotate(90,[0,1,0])
write(text[r],center=true,h=h,rotate=rotate,t=t,font=font,bold=bold);
write(text[r],center=true,h=h,rotate=rotate,t=t,font=font);
}
}else{
difference(){
@ -216,7 +214,7 @@ module writesphere(text,where,radius){
translate([radius,0,0])
rotate(90,[1,0,0])
rotate(90,[0,1,0])
write(text[r],center=true,h=h,rotate=rotate,t=t*2+h,font=font,bold=bold);
write(text[r],center=true,h=h,rotate=rotate,t=t*2+h,font=font);
}
difference(){ //rounded outside
sphere(radius+(t*2+h)*2);
@ -234,12 +232,12 @@ module writecube(text,where,size){
if (str(size)[0] != "["){
// its a square cube (size was not a matrix so make it one)
writethecube(text,where,[size,size,size],h=h,rotate=rotate,space=space,
t=t,font=font,face=face,up=up,down=down,right=right,left=left,bold=bold);
t=t,font=font,face=face,up=up,down=down,right=right,left=left);
}else{
// its not square
writethecube(text,where,size,h=h,rotate=rotate,space=space,
t=t,font=font,face=face,up=up,down=down,right=right,left=left,bold=bold);
t=t,font=font,face=face,up=up,down=down,right=right,left=left);
}
}
// I split the writecube module into 2 pieces.. easier to add features later
@ -247,34 +245,34 @@ module writethecube(text,where,size){
if (face=="front") {
translate([where[0]+right-left,where[1]-size[1]/2,where[2]+up-down])
rotate(90,[1,0,0])
write(text,center=true,h=h,rotate=rotate,t=t,font=font,space=space,bold=bold);
write(text,center=true,h=h,rotate=rotate,t=t,font=font);
}
if (face=="back") {
translate([where[0]+right-left,where[1]+size[1]/2,where[2]+up-down])
rotate(90,[1,0,0]) // rotate around the x axis
rotate(180,[0,1,0]) // rotate around the y axis (z before rotation)
write(text,center=true,h=h,rotate=rotate,t=t,font=font,space=space,bold=bold);
write(text,center=true,h=h,rotate=rotate,t=t,font=font);
}
if (face=="left") {
translate([where[0]-size[0]/2,where[1]-right+left,where[2]+up-down ])
rotate(90,[1,0,0]) // rotate around the x axis
rotate(90,[0,-1,0]) // rotate around the y axis (z before rotation)
write(text,center=true,h=h,rotate=rotate,t=t,font=font,space=space,bold=bold);
write(text,center=true,h=h,rotate=rotate,t=t,font=font);
}
if (face=="right") {
translate([where[0]+size[0]/2,where[1]+right-left,where[2] +up-down])
rotate(90,[1,0,0]) // rotate around the x axis
rotate(90,[0,1,0]) // rotate around the y axis (z before rotation)
write(text,center=true,h=h,rotate=rotate,t=t,font=font,space=space,bold=bold);
write(text,center=true,h=h,rotate=rotate,t=t,font=font);
}
if (face=="top") {
translate([where[0]+right-left,where[1]+up-down,where[2]+size[2]/2 ])
write(text,center=true,h=h,rotate=rotate,t=t,font=font,space=space,bold=bold);
write(text,center=true,h=h,rotate=rotate,t=t,font=font);
}
if (face=="bottom") {
translate([where[0]+right-left,where[1]-up+down,where[2]-size[2]/2 ])
rotate(180,[1,0,0])
write(text,center=true,h=h,rotate=rotate,t=t,font=font,space=space,bold=bold);
write(text,center=true,h=h,rotate=rotate,t=t,font=font);
}
}
@ -285,9 +283,7 @@ module write(word){
echo ("There are " ,len(word) ," letters in this string");
// echo ("The second letter is ",word[1]);
// echo (str(word[0],"_"));
minkowski() {
rotate(rotate,[0,0,-1]){
for (r = [0:len(word)]){ // count off each character
// if the letter is lower case, add an underscore to the end for file lookup
if ((word[r] == "a" ) || (word[r]== "b") || (word[r]== "c")
@ -303,20 +299,18 @@ rotate(rotate,[0,0,-1]){
translate([0,-h/2,0]){
scale([.125*h,.125*h,t]){
translate([ (-len(word)*5.5*space/2) + (r*5.5*space),0,0])
//offset(delta = 20, join_type = "round") {
linear_extrude(height=1,convexity=10,center=true){
import(file = font,layer=str(word[r],"_"));
}//}
}
}
}
}else{
translate([0,0,t/2]){
scale([.125*h,.125*h,t]){
translate([r*5.5*space,0,0])
//offset(delta = 20, join_type = "round") {
linear_extrude(height=1,convexity=10,center=true){
import(file = font,layer=str(word[r],"_"));
}//}
}
}
}
}
@ -326,28 +320,24 @@ rotate(rotate,[0,0,-1]){
translate([0,-h/2,0]){
scale([.125*h,.125*h,t]){
translate([ (-len(word)*5.5*space/2) + (r*5.5*space),0,0])
//offset(delta = 20, join_type = "round") {
linear_extrude(height=1,convexity=10,center=true){
import(file = font,layer=str(word[r]));
}//}
}
}
}
}else{
translate([0,0,t/2]){
scale([.125*h,.125*h,t]){
translate([r*5.5*space,0,0])
//offset(delta = 20, join_type = "round") {
linear_extrude(height=1,convexity=10,center=true){
import(file = font,layer=str(word[r]));
}//}
}
}
}
}
}
}
}
cube([bold*.1*h,bold*.1*h,.00001]);
}
}
/*writecylinder test

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,9 +1,8 @@
<!DOCTYPE html>
<html lang="de" xmlns="http://www.w3.org/1999/html" data-theme="dark">
<head>
<title>FabLab Bottle Clip Generator</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/pico.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='pico.css') }}">
<style>
.error {
background-color: rgb(183, 28, 28);
@ -17,16 +16,15 @@
<body>
<script src="{{ url_for('static', filename='js/download.js') }}"></script>
<script src="{{ url_for('static', filename='download.js') }}"></script>
<script type="module">
import {createApp} from '{{ url_for("static", filename="js/vue.esm-browser.js") }}'
import {createApp} from '{{ url_for("static", filename="vue.esm-browser.js") }}'
createApp({
data() {
return {
name: "",
logo: "fablab",
fetching: false,
error: false,
}
@ -51,7 +49,6 @@
method: "post",
body: JSON.stringify({
"name": this.name,
"logo": this.logo,
}),
headers: {
"Content-Type": "application/json",
@ -77,7 +74,7 @@
<main class="container">
<center>
<img src="{{ url_for('static', filename='img/logo.svg') }}" style="max-width: 350px; margin-bottom: 30px;">
<img src="{{ url_for('static', filename='logo.svg') }}" style="max-width: 350px; margin-bottom: 30px;">
<h1>FabLab Bottle Clip Generator</h1>
@ -89,10 +86,6 @@
<form ref="form" action="{{ url_for('generate') }}" method="post">
<label for="name">Name:</label>
<input type="text" id="name" name="name" style="text-align: center;" placeholder="Name" v-model="name" :disabled="fetching" :aria-invalid="name === ''">
<select id="logo" name="logo" v-model="logo" :disabled="fetching" aria-label="Logo auswählen" required>
<option value="fablab">FabLab</option>
<option value="thw">THW</option>
</select>
<button v-if="!fetching" ref="submitButton" type="submit" @click="generate" :disabled="name === ''">Generieren</button>
<button v-if="fetching" style="display: none" v-show="true" type="button" disabled="true" aria-busy="true">Generiere...</button>
</form>
@ -102,5 +95,4 @@
</main>
</body>
</html>