├── .gitignore ├── docs ├── samples.png ├── samples_box.png ├── slicer_sliced.png └── slicer_settings.png ├── samples.csv ├── gen_samples.py ├── FilamentSamples.json ├── README.md └── FilamentSamples.scad /.gitignore: -------------------------------------------------------------------------------- 1 | *.3mf -------------------------------------------------------------------------------- /docs/samples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zen/FilamentSamples/master/docs/samples.png -------------------------------------------------------------------------------- /docs/samples_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zen/FilamentSamples/master/docs/samples_box.png -------------------------------------------------------------------------------- /docs/slicer_sliced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zen/FilamentSamples/master/docs/slicer_sliced.png -------------------------------------------------------------------------------- /docs/slicer_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zen/FilamentSamples/master/docs/slicer_settings.png -------------------------------------------------------------------------------- /samples.csv: -------------------------------------------------------------------------------- 1 | extrudr,Biofusion,Metallic Grey,225,60 2 | extrudr,PETG,Black,230,80 3 | JAYO,PLA+,Black,205,60 4 | JAYO,PLA+ Silk,Red Copper,210,60 5 | JAYO,PLA+ Silk,Light Gold,210,60 6 | JAYO,PETG,Black,230,80 7 | JAYO,PETG,Orange,230,80 8 | ERYONE,PLA Matte,Navy Blue,215,60 9 | OVERTURE,PETG,Rock White,235,80 -------------------------------------------------------------------------------- /gen_samples.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import subprocess 4 | import csv 5 | import os 6 | import platform 7 | 8 | if platform.system() == 'Windows': 9 | OPENSCAD = 'C:\Program Files\OpenSCAD\openscad.exe' 10 | elif platform.system() == 'Linux': 11 | OPENSCAD = 'openscad' 12 | else: 13 | OPENSCAD = '/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD' 14 | 15 | MYDIR=os.path.dirname(os.path.realpath(__file__)) 16 | 17 | def gen_samples(file=f"{MYDIR}/samples.csv"): 18 | os.makedirs(f"{MYDIR}/stl", exist_ok=True) 19 | with open(file, '+r') as f: 20 | data = csv.reader(f) 21 | 22 | for l in data: 23 | print("Processing:", l) 24 | filename = "stl/" + "_".join(l) + ".stl" 25 | subprocess.run( 26 | [OPENSCAD, 27 | '-o', filename, 28 | '-D', f'BRAND="{l[0]}"', 29 | '-D', f'TYPE="{l[1]}"', 30 | '-D', f'COLOR="{l[2]}"', 31 | '-D', f'TEMP_HOTEND="{l[3]}"', 32 | '-D', f'TEMP_BED="{l[4]}"', 33 | f'{MYDIR}/FilamentSamples.scad', 34 | ], check=True) 35 | 36 | if __name__ == "__main__": 37 | gen_samples() 38 | -------------------------------------------------------------------------------- /FilamentSamples.json: -------------------------------------------------------------------------------- 1 | { 2 | "parameterSets": { 3 | "Standardwerte des Designs": { 4 | "$fn": "50", 5 | "ABS_NOTCH_X": "51", 6 | "BRAND": "extrudr", 7 | "BRAND_SIZE": "4.2", 8 | "CARD_CORNER_RADIUS": "3.3", 9 | "CARD_EDGE_RADIUS": "1.1", 10 | "CARD_HEIGHT": "35", 11 | "CARD_LENGTH": "80", 12 | "CARD_THICKNESS": "2.2", 13 | "COLOR": "Metallic Grey", 14 | "COLOR_SIZE": "5.5", 15 | "FONT": "Liberation Sans:style=Bold", 16 | "INFILL_NOTCH_X": "25.5", 17 | "INFILL_SAMPLE": "0", 18 | "INSET_CORNER_RADIUS": "2.2", 19 | "INSET_DEPTHS": "[1, 0.8, 0.6, 0.4, 0.2]", 20 | "INSET_EDGE_RADIUS": "1", 21 | "INSET_FROM_BOTTOM": "2.5", 22 | "INSET_HEIGHT": "10", 23 | "INSET_WIDTH": "7", 24 | "INVERT_CARD": "1", 25 | "NOTCH_RADIUS": "0", 26 | "OTHER_NOTCH_X": "68", 27 | "PETG_NOTCH_X": "34", 28 | "PLA_NOTCH_X": "17", 29 | "SHOW_FIRSTLAYER_TEMP": "0", 30 | "TEMP_BED": "60", 31 | "TEMP_BED_FIRST_LAYER": "85", 32 | "TEMP_HOTEND": "225", 33 | "TEMP_HOTEND_FIRST_LAYER": "240", 34 | "TEMP_SIZE": "4.2", 35 | "TEXT_DEPTH": "1.5", 36 | "TEXT_X": "4", 37 | "TEXT_Y": "39", 38 | "TYPE": "Biofusion", 39 | "TYPE_SIZE": "4.2" 40 | } 41 | }, 42 | "fileFormatVersion": "1" 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Markus 3D Print Filament Sample Generator 2 | 3 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R8DQO8C) 4 | 5 | Samples | Samples Box (by Zahg) 6 | :---------------------:|:-------------------------: 7 | ![](docs/samples.png) | ![](docs/samples_box.png) 8 | 9 | We've all come across like 100 different 3D print filament samples, but most require you to either request new ones from the author, or you need the special commercial CAD tool to go into the parameters and change the text yourself. 10 | 11 | This generator is based on OpenSCAD, a completely free 'programmable' CAD tool. 12 | But OpenSCAD is scary to 95% of all users because it essentially forces you to program your CAD model. Usually this is also not what I do, but for such samples this is the ideal approach. 13 | 14 | So how is this any good? Easy: The packaged `gen_samples.py` script just reads a `samples.csv` file, where you can put in your list of samples you would like. No programming skills required. 15 | 16 | ## How To 17 | 18 | - make sure you have OpenSCAD and Python available on your machine (see below for OS-specific prerequisite install instructions) 19 | - make sure you have the 'Liberation Sans' Font available, it comes with LibreOffice or you can get it from here: 20 | - edit `samples.csv` and put in the filaments you like to generate samples for 21 | - if you are not on Windows or your OpenSCAD install is not in the `C:\Program Files` standard path, edit `gen_samples.py` and at the top put in the path to the executable for the variable `OPENSCAD`, or just uncomment the `openscad` entry if it is accessible anywhere on your machine because it is part of your PATH variable 22 | - run `gen_samples.py`, if your Python install is in your PATH or `.py` files are linked you should even be able to just double-click it, if not get a command line shell and run `python gen_samples.py` (on Linux you might have to use python3 instead) 23 | 24 | ## Using VS Code 25 | 26 | Another great method across systems is using VS Code. If you cloned this repo 27 | and open it within VS-Code make sure you have the Python extension installed. 28 | Then you can just edit the .csv right there, open the `gen_saples.py` script and immidiately run it with the play button in the upper right. Of course you still need some version of Python and OpenSCAD installed, Python should be found by VS Code if it is there. 29 | 30 | ## Using the stl-Models 31 | 32 | The output will be generated under the `stl`-Folder. 33 | If you pull the models into your Slicer make sure to select 0.2mm line thickness(1), the actual Filament profile(2) you want to print with. 34 | The print time (3) will vary according to your print speed settings for that filament, but should be in the ballpark of 20-30min. 35 | 36 | ![slicer settings](docs/slicer_settings.png) 37 | 38 | There should be approx 5g of filament being used (1) (depending on density of course) 39 | Also make sure after slicing that the 'thickness staircase'(2) is visible (especially the single layer all the way to the right), there is no non-solid infill areas (3) and the letters look proper: 40 | 41 | ![slicer sliced](docs/slicer_sliced.png) 42 | 43 | You can then proceed with printing! Have fun! 44 | 45 | ## Prerequisite Installation 46 | 47 | ### Windows 48 | 49 | The by far easiest method nowadays is to use `winget`, just get a Powershell or a cmd-prompt and run: 50 | 51 | ```sh 52 | winget install OpenSCAD.OpenSCAD 53 | winget install Python.Python.3.11 54 | ``` 55 | 56 | This should work on all recently supported versions of Windows 10 and 11. 57 | 58 | Alternatively go to the OpenSCAD and Python webpages and download the latest version from there. 59 | 60 | ### Linux 61 | 62 | Any reasonably recent distribution will have a Python3 version available in the PATH as `python3`, for very recent ones `python` is also at version 3 already. You can check with `python --version`. 63 | 64 | For OpenSCAD I recommend checking if your standard package manager has it available (like `apt`, `dnf`, `pacman` etc.). Usually the distros software centers also now search Flatpak or AppImage repositories, so I recommend you just search for OpenSCAD there and fetch it this way. 65 | 66 | ### MacOS 67 | 68 | If you are running homebrew you should be able to get both Python 3 and OpenSCAD via `brew`, otherwise go to the webpages of OpenSCAD and Python and get the newest Mac distribution from there. 69 | 70 | ## Ackknowledgements 71 | I derived this OpenSCAD model from blazerat over at printables: 72 | 73 | I really loved the temperature idea, but I like the form factor and Box better offered by Seemomster and originally Zahg () 74 | 75 | So I basically turned the samples by blazerat into the form factor of Zahgs samples and box, with added info, while maintaining the 5-step thickness staircase. 76 | 77 | ## Your Support 78 | 79 | If you like what you see, you can leave me a tip here: 80 | 81 | ![kofi_QR](https://user-images.githubusercontent.com/25175069/178159517-26c0ddef-30bc-4c48-ad57-1b4eeb40206e.png) 82 | -------------------------------------------------------------------------------- /FilamentSamples.scad: -------------------------------------------------------------------------------- 1 | // Change For Each Card 2 | BRAND="extrudr"; 3 | COLOR="Metallic Grey"; 4 | TYPE="Biofusion"; 5 | TEMP_HOTEND="225"; 6 | TEMP_BED="60"; 7 | 8 | // Change only if absolutely necessary 9 | COLOR_SIZE=5.5; 10 | // Change only if absolutely necessary 11 | TEMP_SIZE=4.2; 12 | // Change only if absolutely necessary 13 | BRAND_SIZE=4.2; 14 | // Change only if absolutely necessary 15 | TYPE_SIZE=4.2; 16 | 17 | // change not supported right now 18 | SHOW_FIRSTLAYER_TEMP=0; 19 | // change not supported right now 20 | TEMP_HOTEND_FIRST_LAYER="240"; 21 | // change not supported right now 22 | TEMP_BED_FIRST_LAYER="85"; 23 | // change not supported right now 24 | INVERT_CARD=1; 25 | // change not supported right now 26 | INFILL_SAMPLE=0; 27 | 28 | // General Card Settings 29 | 30 | CARD_LENGTH=80.0; 31 | CARD_HEIGHT=35; 32 | CARD_THICKNESS=2.2; 33 | CARD_CORNER_RADIUS=3.3; 34 | CARD_EDGE_RADIUS=1.1; 35 | 36 | // General Notch 37 | 38 | NOTCH_RADIUS=0.0; 39 | NOTCH_Y=CARD_HEIGHT + 0.1; 40 | 41 | // PLA Notch 42 | 43 | PLA_NOTCH_X=17.0; 44 | 45 | // PETG Notch 46 | 47 | PETG_NOTCH_X=34.0; 48 | 49 | // ABS Notch Location 50 | 51 | ABS_NOTCH_X=51.0; 52 | 53 | // Other Notch Location 54 | 55 | OTHER_NOTCH_X=68.0; 56 | INFILL_NOTCH_X=25.5; 57 | 58 | // Inset 59 | INSET_CORNER_RADIUS=2.2; 60 | INSET_EDGE_RADIUS=1.0; 61 | INSET_HEIGHT=10.0; 62 | INSET_WIDTH=7.0; 63 | INSET_FROM_BOTTOM=2.5; 64 | //INSET_DEPTHS=[0.2, 0.4, 0.6, 0.8, 1.0]; 65 | INSET_DEPTHS=[1.0, 0.8, 0.6, 0.4, 0.2]; 66 | 67 | // Text 68 | // FONT = "Arial Rounded MT Bold:style=Regular"; 69 | FONT = "Liberation Sans:style=Bold"; 70 | TEXT_X=4.0; 71 | TEXT_Y=39.0; 72 | TEXT_DEPTH=1.5; 73 | //TEXT_TEMP=str(TEMP_HOTEND, "\u00B0\u2013", TEMP_BED, "\u00B0"); 74 | TEXT_TEMP=str("N", TEMP_HOTEND, "\u00B0 B", TEMP_BED, "\u00B0"); 75 | TEXT_FL_TEMP=str(" ", TEMP_HOTEND_FIRST_LAYER, "\u00B0\u2013", TEMP_BED_FIRST_LAYER, "\u00B0"); 76 | 77 | $fn = 50; 78 | 79 | module CardCorner(x, y, z) { 80 | translate([x, y, z]) 81 | hull() { 82 | rotate_extrude() 83 | translate([CARD_CORNER_RADIUS - CARD_EDGE_RADIUS, 0, 0]) 84 | circle(CARD_EDGE_RADIUS); 85 | } 86 | } 87 | 88 | module CardBody() { 89 | hull() { 90 | CardCorner(CARD_CORNER_RADIUS, CARD_CORNER_RADIUS, CARD_EDGE_RADIUS); 91 | CardCorner(CARD_CORNER_RADIUS, CARD_CORNER_RADIUS, CARD_THICKNESS - CARD_EDGE_RADIUS); 92 | 93 | CardCorner(CARD_LENGTH - CARD_CORNER_RADIUS, CARD_CORNER_RADIUS, CARD_EDGE_RADIUS); 94 | CardCorner(CARD_LENGTH - CARD_CORNER_RADIUS, CARD_CORNER_RADIUS, CARD_THICKNESS - CARD_EDGE_RADIUS); 95 | 96 | CardCorner(CARD_CORNER_RADIUS, CARD_HEIGHT - CARD_CORNER_RADIUS, CARD_EDGE_RADIUS); 97 | CardCorner(CARD_CORNER_RADIUS, CARD_HEIGHT - CARD_CORNER_RADIUS, CARD_THICKNESS - CARD_EDGE_RADIUS); 98 | 99 | CardCorner(CARD_LENGTH - CARD_CORNER_RADIUS, CARD_HEIGHT - CARD_CORNER_RADIUS, CARD_EDGE_RADIUS); 100 | CardCorner(CARD_LENGTH - CARD_CORNER_RADIUS, CARD_HEIGHT - CARD_CORNER_RADIUS, CARD_THICKNESS - CARD_EDGE_RADIUS); 101 | } 102 | } 103 | 104 | module Notch(X) { 105 | translate([X, NOTCH_Y, 0]) 106 | cylinder(CARD_THICKNESS, NOTCH_RADIUS, NOTCH_RADIUS); 107 | } 108 | 109 | module Insets() { 110 | Inset(42.5, INSET_DEPTHS[0]); 111 | Inset(49.5, INSET_DEPTHS[1]); 112 | Inset(56.5, INSET_DEPTHS[2]); 113 | Inset(63.5, INSET_DEPTHS[3]); 114 | Inset(70.5, INSET_DEPTHS[4]); 115 | } 116 | 117 | module Inset(Inset_X, Thickness) { 118 | hull() { 119 | translate([Inset_X, INSET_FROM_BOTTOM, Thickness]) 120 | cube([INSET_WIDTH, INSET_HEIGHT, CARD_THICKNESS]); 121 | } 122 | } 123 | 124 | module TopInset(Inset_X, Thickness) { 125 | hull() { 126 | translate([Inset_X, INSET_FROM_BOTTOM, Thickness]) 127 | cube([INSET_WIDTH, INSET_HEIGHT, CARD_THICKNESS]); 128 | } 129 | } 130 | 131 | module CardInfo() { 132 | Text(16.5, TEXT_TEMP, TEMP_SIZE, 1.1, "right", CARD_LENGTH-TEXT_X+0.2); 133 | Text(24, COLOR, COLOR_SIZE, 1.0); 134 | Text(10, BRAND, BRAND_SIZE, 1.0); 135 | if (SHOW_FIRSTLAYER_TEMP == 1) { 136 | Text(14, TEXT_FL_TEMP, 3, 1.0); 137 | } 138 | Text(4, TYPE, TYPE_SIZE, 1.0); 139 | } 140 | 141 | module InfillInfo() { 142 | Text(40, BRAND, 6.4, 1.0); 143 | Text(32, COLOR, COLOR_SIZE, 1.1); 144 | } 145 | 146 | module Text(Y, Text, Size, Spacing, Halign="left", X=TEXT_X) { 147 | translate([X, Y, CARD_THICKNESS - TEXT_DEPTH - 0.2]) 148 | linear_extrude(TEXT_DEPTH) 149 | text(text = Text, size = Size, font = FONT, spacing = Spacing, halign=Halign); 150 | } 151 | 152 | module Invert() { 153 | translate([INSET_FROM_BOTTOM, INSET_FROM_BOTTOM, TEXT_DEPTH]) 154 | cube([37.5,CARD_HEIGHT - (2 * INSET_FROM_BOTTOM),4]); 155 | translate([37.5, 15, TEXT_DEPTH]) 156 | cube([40,17.5,4]); 157 | } 158 | 159 | module Infill() { 160 | translate([INSET_FROM_BOTTOM, CARD_HEIGHT - 24.4, TEXT_DEPTH]) 161 | cube([CARD_LENGTH - (2 * INSET_FROM_BOTTOM), 24.4 - INSET_FROM_BOTTOM, 4]); 162 | translate([INSET_FROM_BOTTOM, INSET_FROM_BOTTOM, CARD_THICKNESS - 0.15]) 163 | cube([CARD_LENGTH - (2 * INSET_FROM_BOTTOM), INSET_HEIGHT, .25]); 164 | } 165 | 166 | module Card() { 167 | difference() { 168 | CardBody(); 169 | Notch(PLA_NOTCH_X); 170 | if (INFILL_SAMPLE==1) { 171 | Infill(); 172 | Notch(INFILL_NOTCH_X); 173 | } 174 | if (INFILL_SAMPLE==0) { 175 | if (INVERT_CARD==1) { 176 | Invert(); 177 | } 178 | if (TYPE=="PETG") { 179 | Notch(PETG_NOTCH_X); 180 | } else { 181 | if (TYPE=="ABS") { 182 | Notch(ABS_NOTCH_X); 183 | } else { 184 | if (TYPE != "PLA") { 185 | Notch(OTHER_NOTCH_X); 186 | } 187 | } 188 | } 189 | 190 | Insets(); 191 | if (INVERT_CARD!=1) { 192 | CardInfo(); 193 | } 194 | } 195 | } 196 | 197 | if (INFILL_SAMPLE==0) { 198 | if (INVERT_CARD==1) { 199 | CardInfo(); 200 | } 201 | } 202 | if (INFILL_SAMPLE==1) { 203 | InfillInfo(); 204 | } 205 | } 206 | 207 | Card(); --------------------------------------------------------------------------------