.
This commit is contained in:
parent
22395da7e4
commit
acd40599ab
4 changed files with 412 additions and 0 deletions
47
01_3D-Druck/blender/README.md
Normal file
47
01_3D-Druck/blender/README.md
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Blender-Workflow — RACI-Konsolen-Board
|
||||||
|
|
||||||
|
Besserer Look als OpenSCAD: weiche Fasen (Bevel), smooth shading, optional Relief,
|
||||||
|
schöne Vorschau-Renders — und trotzdem **maßhaltig** (alle Mulden/Passungen im Skript).
|
||||||
|
|
||||||
|
## Tooling
|
||||||
|
- **Blender** (gratis): https://www.blender.org/download/ (4.2 LTS empfohlen).
|
||||||
|
- Generator-Skript: [`raci-board.py`](raci-board.py) — baut das Board parametrisch.
|
||||||
|
|
||||||
|
## So baust du das Board
|
||||||
|
**Variante A — in der Blender-Oberfläche (empfohlen zum Iterieren):**
|
||||||
|
1. Blender öffnen → oben Reiter **Scripting**.
|
||||||
|
2. **Open** → `raci-board.py` → **Run Script** (▷).
|
||||||
|
3. Das Board wird erzeugt (gerundete Kanten via Bevel). Im **Layout**-Reiter ansehen.
|
||||||
|
4. Export: `File → Export → STL` (bzw. 3MF für den H2D, falls Addon aktiv).
|
||||||
|
|
||||||
|
**Variante B — headless (nur Datei erzeugen):**
|
||||||
|
```
|
||||||
|
blender -b -P raci-board.py
|
||||||
|
```
|
||||||
|
→ legt `raci-board.stl` (+ Vorschau `raci_preview.png`) neben dem Skript ab.
|
||||||
|
|
||||||
|
## Maße (Spec — der „Vertrag", gilt in jedem Tool)
|
||||||
|
| Element | Wert |
|
||||||
|
|---|---|
|
||||||
|
| Board | 210 × 210 × 10 mm, Außenkanten gefast (Bevel ~1,2 mm) |
|
||||||
|
| Acryl-Chip-Mulde (Mitte) | Ø **40,6** × Tiefe **1,8** mm (+ Greifkerbe Ø12) — für Chip Ø40 × 2 mm |
|
||||||
|
| Figuren-Sockel-Mulden | **10×** Ø **25,3** × Tiefe **1,5** mm (Sockel Ø24,5) |
|
||||||
|
| Sockel-Ring-Radius | 48 mm (Mitte–Mitte), 36° Teilung |
|
||||||
|
| RACI-Sektoren | **R 3 · A 1 · C 4 · I 2** (= 10), Trennstege erhaben |
|
||||||
|
| Sektor-Beschriftung | groß **R/A/C/I** + Wörter RESPONSIBLE/ACCOUNTABLE/CONSULTED/INFORMED |
|
||||||
|
| Action-Card-Halter | Steh-Schlitz **63 × 4 mm**, ~12° nach hinten geneigt (Karte 60×90) |
|
||||||
|
| Druck | 6× in Phasenfarbe (5 Phasen + Gate-Rot), einfarbig je Board |
|
||||||
|
|
||||||
|
## „Edel" machen (manueller Feinschliff in Blender)
|
||||||
|
Nach dem Skript-Lauf für den Premium-Look:
|
||||||
|
1. **Smooth shading:** Objekt wählen → Rechtsklick → *Shade Auto Smooth*.
|
||||||
|
2. **Mehr/Schärfere Fasen:** Bevel-Modifier-Breite/Segmente erhöhen, oder Kanten markieren
|
||||||
|
(*Edge → Mark Sharp/Crease*) und Bevel nach Winkel/Weight.
|
||||||
|
3. **Relief/Thema (optional):** flaches Stadt-/Platinen-Relief als Bild → *Displace*-Modifier
|
||||||
|
mit einer Textur auf einer Unterteilungsfläche; oder Mikro-Rillen im Mittelfeld.
|
||||||
|
4. **Typo schöner:** eigene Schriftart laden (Text-Objekt → Font), Bold, leicht erhaben
|
||||||
|
statt graviert.
|
||||||
|
5. **Export:** STL (mm) für den Slicer; Wandstärke/Mulden vorm Druck einmal prüfen.
|
||||||
|
|
||||||
|
> Das Skript ist ein **Startgerüst v1** — sag mir nach deinem ersten Lauf, was hakt
|
||||||
|
> (Version/Fehlermeldung), dann fix ich es gezielt.
|
||||||
BIN
01_3D-Druck/blender/__pycache__/raci-board.cpython-312.pyc
Normal file
BIN
01_3D-Druck/blender/__pycache__/raci-board.cpython-312.pyc
Normal file
Binary file not shown.
193
01_3D-Druck/blender/raci-board.py
Normal file
193
01_3D-Druck/blender/raci-board.py
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
# RACI-Konsolen-Board — Blender-Generator (bpy)
|
||||||
|
# SLC-Workshop Tabletop · Einheiten: 1 Blender-Unit = 1 mm
|
||||||
|
# Baut das Board parametrisch mit gefasten Kanten (Bevel), Mulden, Sektoren,
|
||||||
|
# gravierten Labels, Action-Card-Steckschlitz; rendert eine Vorschau und
|
||||||
|
# exportiert eine STL. Startgeruest v1 — in Blender 4.x getestet gegen die API,
|
||||||
|
# einzelne Schritte sind per try/except abgesichert (Labels optional).
|
||||||
|
#
|
||||||
|
# Lauf: Blender -> Scripting -> Open -> Run ODER blender -b -P raci-board.py
|
||||||
|
|
||||||
|
import bpy, bmesh, math, os
|
||||||
|
from mathutils import Vector
|
||||||
|
|
||||||
|
# ----------------------------- Parameter (mm) -----------------------------
|
||||||
|
BOARD_W, BOARD_D, BASE_H = 210.0, 210.0, 10.0
|
||||||
|
EDGE_BEVEL, EDGE_SEG = 1.4, 4
|
||||||
|
|
||||||
|
DIAL_CX, DIAL_CY = 0.0, -15.0
|
||||||
|
CHIP_D, CHIP_DEP = 40.6, 1.8 # Acryl-Chip Ø40 x 2 mm
|
||||||
|
NOTCH_D = 12.0
|
||||||
|
|
||||||
|
SOCK_D, SOCK_DEP = 25.3, 1.5 # Figuren-Sockel Ø24,5
|
||||||
|
RING_R = 48.0
|
||||||
|
N_SOCK = 10 # Winkel ab oben (90°), 36°-Teilung
|
||||||
|
RIDGE_H, RIDGE_W = 2.6, 3.2
|
||||||
|
DIVIDERS = [72, -72, -144, 108]
|
||||||
|
# Sektor-Mitten (fuer grosse Buchstaben) R3 A1 C4 I2
|
||||||
|
LETTERS = [(90, "A"), (0, "C"), (-108, "I"), (162, "R")]
|
||||||
|
|
||||||
|
CARD_CY = 78.0
|
||||||
|
CARD_BW, CARD_BD, CARD_BH = 84.0, 22.0, 16.0
|
||||||
|
SLOT_W, SLOT_T, SLOT_TILT = 63.0, 4.0, 12.0
|
||||||
|
|
||||||
|
LET_SIZE, LET_DEP = 11.0, 1.0
|
||||||
|
WORD_SIZE, WORD_DEP = 5.0, 0.8
|
||||||
|
TOP = BASE_H
|
||||||
|
|
||||||
|
HERE = os.path.dirname(bpy.data.filepath) or os.path.dirname(os.path.abspath(__file__))
|
||||||
|
STL_OUT = os.path.join(HERE, "raci-board.stl")
|
||||||
|
PNG_OUT = os.path.join(HERE, "raci_preview.png")
|
||||||
|
|
||||||
|
# ----------------------------- Helfer -----------------------------
|
||||||
|
def clear_scene():
|
||||||
|
bpy.ops.object.select_all(action='SELECT')
|
||||||
|
bpy.ops.object.delete(use_global=False)
|
||||||
|
for blk in (bpy.data.meshes, bpy.data.materials, bpy.data.curves):
|
||||||
|
for d in list(blk):
|
||||||
|
if d.users == 0: blk.remove(d)
|
||||||
|
|
||||||
|
def cube(sx, sy, sz, loc):
|
||||||
|
bpy.ops.mesh.primitive_cube_add(size=1, location=loc)
|
||||||
|
o = bpy.context.object; o.scale = (sx, sy, sz)
|
||||||
|
bpy.ops.object.transform_apply(scale=True)
|
||||||
|
return o
|
||||||
|
|
||||||
|
def cyl(d, h, loc, verts=96):
|
||||||
|
bpy.ops.mesh.primitive_cylinder_add(radius=d/2.0, depth=h, location=loc, vertices=verts)
|
||||||
|
return bpy.context.object
|
||||||
|
|
||||||
|
def boolean(obj, tool, op='DIFFERENCE'):
|
||||||
|
m = obj.modifiers.new("bool", 'BOOLEAN'); m.operation = op
|
||||||
|
m.object = tool
|
||||||
|
try: m.solver = 'EXACT'
|
||||||
|
except Exception: pass
|
||||||
|
bpy.context.view_layer.objects.active = obj
|
||||||
|
bpy.ops.object.modifier_apply(modifier=m.name)
|
||||||
|
bpy.data.objects.remove(tool, do_unlink=True)
|
||||||
|
|
||||||
|
def apply_bevel(obj, width=EDGE_BEVEL, seg=EDGE_SEG, ang=30):
|
||||||
|
m = obj.modifiers.new("bevel", 'BEVEL')
|
||||||
|
m.width = width; m.segments = seg
|
||||||
|
m.limit_method = 'ANGLE'; m.angle_limit = math.radians(ang)
|
||||||
|
bpy.context.view_layer.objects.active = obj
|
||||||
|
bpy.ops.object.modifier_apply(modifier=m.name)
|
||||||
|
|
||||||
|
def cut_text(board, body, x, y, rotz=0, size=LET_SIZE, dep=LET_DEP):
|
||||||
|
try:
|
||||||
|
bpy.ops.object.text_add(location=(x, y, TOP - dep))
|
||||||
|
t = bpy.context.object; t.data.body = body
|
||||||
|
t.data.size = size; t.data.extrude = dep + 1.5
|
||||||
|
t.data.align_x = 'CENTER'; t.data.align_y = 'CENTER'
|
||||||
|
t.rotation_euler = (0, 0, math.radians(rotz))
|
||||||
|
bpy.ops.object.convert(target='MESH')
|
||||||
|
boolean(board, t, 'DIFFERENCE')
|
||||||
|
except Exception as e:
|
||||||
|
print("Label uebersprungen (%s): %s" % (body, e))
|
||||||
|
|
||||||
|
# ----------------------------- Aufbau -----------------------------
|
||||||
|
clear_scene()
|
||||||
|
|
||||||
|
# Basisplatte + Kartenblock, beide gefast, dann vereinen
|
||||||
|
base = cube(BOARD_W, BOARD_D, BASE_H, (0, 0, BASE_H/2))
|
||||||
|
apply_bevel(base)
|
||||||
|
block = cube(CARD_BW, CARD_BD, BASE_H + CARD_BH, (0, CARD_CY, (BASE_H + CARD_BH)/2))
|
||||||
|
apply_bevel(block, width=1.0)
|
||||||
|
boolean(base, block, 'UNION')
|
||||||
|
|
||||||
|
# Gefaste Ecke vorne rechts (Optik)
|
||||||
|
cc = cube(28, 28, BASE_H + 4, (BOARD_W/2, -BOARD_D/2, BASE_H/2))
|
||||||
|
cc.rotation_euler = (0, 0, math.radians(45)); bpy.ops.object.transform_apply(rotation=True)
|
||||||
|
boolean(base, cc, 'DIFFERENCE')
|
||||||
|
|
||||||
|
# Chip-Mulde + Greifkerbe
|
||||||
|
boolean(base, cyl(CHIP_D, 6, (DIAL_CX, DIAL_CY, TOP - CHIP_DEP + 3)), 'DIFFERENCE')
|
||||||
|
boolean(base, cyl(NOTCH_D, 6, (DIAL_CX, DIAL_CY - CHIP_D/2, TOP - CHIP_DEP + 3)), 'DIFFERENCE')
|
||||||
|
|
||||||
|
# 10 Sockelmulden im Ring
|
||||||
|
for i in range(N_SOCK):
|
||||||
|
a = math.radians(90 - i * 36)
|
||||||
|
x = DIAL_CX + RING_R * math.cos(a); y = DIAL_CY + RING_R * math.sin(a)
|
||||||
|
boolean(base, cyl(SOCK_D, 6, (x, y, TOP - SOCK_DEP + 3)), 'DIFFERENCE')
|
||||||
|
|
||||||
|
# Deko-Rillen im Mittelfeld
|
||||||
|
for rr in [24, 27, 30, 33]:
|
||||||
|
ring = cyl(rr*2, 0.8, (DIAL_CX, DIAL_CY, TOP - 0.25))
|
||||||
|
inner = cyl((rr-0.8)*2, 1.2, (DIAL_CX, DIAL_CY, TOP - 0.25))
|
||||||
|
boolean(ring, inner, 'DIFFERENCE')
|
||||||
|
boolean(base, ring, 'DIFFERENCE')
|
||||||
|
|
||||||
|
# Sektor-Trennstege (erhaben)
|
||||||
|
ri, ro = CHIP_D/2 + 3, RING_R + SOCK_D/2 + 4
|
||||||
|
for a in DIVIDERS:
|
||||||
|
r = cube(ro - ri, RIDGE_W, RIDGE_H, ((ri+ro)/2, 0, TOP + RIDGE_H/2))
|
||||||
|
r.rotation_euler = (0, 0, math.radians(a))
|
||||||
|
# um Dial-Zentrum rotieren: erst an Zentrum verschieben
|
||||||
|
r.location = (DIAL_CX + ((ri+ro)/2)*math.cos(math.radians(a)),
|
||||||
|
DIAL_CY + ((ri+ro)/2)*math.sin(math.radians(a)), TOP + RIDGE_H/2)
|
||||||
|
bpy.ops.object.transform_apply(rotation=False)
|
||||||
|
boolean(base, r, 'UNION')
|
||||||
|
|
||||||
|
# Action-Card-Steckschlitz (oben offen, nach hinten geneigt)
|
||||||
|
slot = cube(SLOT_W, SLOT_T, 38, (0, CARD_CY, 22))
|
||||||
|
slot.rotation_euler = (math.radians(-SLOT_TILT), 0, 0)
|
||||||
|
bpy.ops.object.transform_apply(rotation=True)
|
||||||
|
boolean(base, slot, 'DIFFERENCE')
|
||||||
|
|
||||||
|
# Gravierte Rand-Linie
|
||||||
|
outer = cube(BOARD_W-14, BOARD_D-14, 1.4, (0, 0, TOP-0.7))
|
||||||
|
inner = cube(BOARD_W-17, BOARD_D-17, 2.0, (0, 0, TOP-0.7))
|
||||||
|
boolean(outer, inner, 'DIFFERENCE'); boolean(base, outer, 'DIFFERENCE')
|
||||||
|
|
||||||
|
# Labels: grosse R/A/C/I + Woerter
|
||||||
|
for ang, ch in LETTERS:
|
||||||
|
rl = RING_R + 13
|
||||||
|
cut_text(base, ch, DIAL_CX + rl*math.cos(math.radians(ang)),
|
||||||
|
DIAL_CY + rl*math.sin(math.radians(ang)))
|
||||||
|
cut_text(base, "RESPONSIBLE", -(RING_R+29), DIAL_CY+6, 90, WORD_SIZE, WORD_DEP)
|
||||||
|
cut_text(base, "CONSULTED", (RING_R+29), DIAL_CY+6, -90, WORD_SIZE, WORD_DEP)
|
||||||
|
cut_text(base, "INFORMED", DIAL_CX, DIAL_CY-(RING_R+30), 0, WORD_SIZE, WORD_DEP)
|
||||||
|
cut_text(base, "ACCOUNTABLE", DIAL_CX, CARD_CY - CARD_BD/2 - 8, 0, WORD_SIZE, WORD_DEP)
|
||||||
|
|
||||||
|
base.name = "RACI-Board"
|
||||||
|
bpy.ops.object.shade_smooth()
|
||||||
|
|
||||||
|
# ----------------------------- Material -----------------------------
|
||||||
|
mat = bpy.data.materials.new("BoardBlue"); mat.use_nodes = True
|
||||||
|
bsdf = mat.node_tree.nodes.get("Principled BSDF")
|
||||||
|
if bsdf:
|
||||||
|
bsdf.inputs["Base Color"].default_value = (0.10, 0.16, 0.30, 1)
|
||||||
|
try: bsdf.inputs["Roughness"].default_value = 0.55
|
||||||
|
except Exception: pass
|
||||||
|
base.data.materials.append(mat)
|
||||||
|
|
||||||
|
# ----------------------------- Vorschau-Render (guarded) -----------------------------
|
||||||
|
try:
|
||||||
|
bpy.ops.object.light_add(type='SUN', location=(120, -160, 220))
|
||||||
|
bpy.context.object.data.energy = 3.0
|
||||||
|
bpy.ops.object.camera_add(location=(150, -190, 210))
|
||||||
|
cam = bpy.context.object
|
||||||
|
cam.rotation_euler = (math.radians(58), 0, math.radians(38))
|
||||||
|
bpy.context.scene.camera = cam
|
||||||
|
sc = bpy.context.scene
|
||||||
|
sc.render.resolution_x, sc.render.resolution_y = 1400, 1000
|
||||||
|
sc.render.filepath = PNG_OUT
|
||||||
|
try: sc.render.engine = 'BLENDER_EEVEE_NEXT'
|
||||||
|
except Exception:
|
||||||
|
try: sc.render.engine = 'BLENDER_EEVEE'
|
||||||
|
except Exception: pass
|
||||||
|
bpy.ops.render.render(write_still=True)
|
||||||
|
print("Vorschau:", PNG_OUT)
|
||||||
|
except Exception as e:
|
||||||
|
print("Render uebersprungen:", e)
|
||||||
|
|
||||||
|
# ----------------------------- STL-Export (versionstolerant) -----------------------------
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
base.select_set(True); bpy.context.view_layer.objects.active = base
|
||||||
|
try:
|
||||||
|
bpy.ops.wm.stl_export(filepath=STL_OUT, export_selected_objects=True) # Blender >= 4.1
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
bpy.ops.export_mesh.stl(filepath=STL_OUT, use_selection=True) # Blender <= 4.0
|
||||||
|
except Exception as e:
|
||||||
|
print("STL-Export fehlgeschlagen — bitte manuell File>Export>STL:", e)
|
||||||
|
print("STL:", STL_OUT)
|
||||||
172
01_3D-Druck/openscad/raci-board.scad
Normal file
172
01_3D-Druck/openscad/raci-board.scad
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
// RACI-Konsolen-Board — SLC-Workshop Tabletop · Einheiten: mm
|
||||||
|
// Quadratisches Premium-Board: zentrale Acryl-Chip-Mulde (Stations-/Gate-ID),
|
||||||
|
// 10 Figurenmulden im Ring, gruppiert in 4 RACI-Sektoren (R3 · A1 · C4 · I2),
|
||||||
|
// erhabene Trennstege, gravierte Sektor-Buchstaben + Wörter, gravierte Rand-Linie,
|
||||||
|
// stehender Action-Card-Halter (60x90) hinten.
|
||||||
|
// Druck: 6x in den Phasenfarben (5 Phasen + Gate-Rot). Einfarbig je Board (Filament).
|
||||||
|
|
||||||
|
/* [Board] */
|
||||||
|
board_w = 210;
|
||||||
|
board_d = 210;
|
||||||
|
base_h = 10;
|
||||||
|
corner_r = 8;
|
||||||
|
cut_corner = 22; // gefaste Ecke (vorne rechts), Optik
|
||||||
|
|
||||||
|
/* [Dial / Mitte] */
|
||||||
|
dial_cx = 0;
|
||||||
|
dial_cy = -15; // Scheibe etwas nach vorn -> Platz fuer Card-Halter hinten
|
||||||
|
chip_d = 40.6; // Acryl-Chip Ø40 + Spiel
|
||||||
|
chip_dep = 1.8; // Chip 2 mm steht ~0,2 vor (greifbar)
|
||||||
|
notch_r = 6; // Greifkerbe
|
||||||
|
|
||||||
|
/* [Sockel] — Figuren-Sockel Ø24,5 -> Mulde Ø25,3 */
|
||||||
|
sock_d = 25.3;
|
||||||
|
sock_dep = 1.5;
|
||||||
|
sock_lead = 0.6;
|
||||||
|
ring_R = 48; // Radius Sockelkreis (Mitte-Mitte)
|
||||||
|
|
||||||
|
/* [Sektor-Trennstege] */
|
||||||
|
ridge_h = 2.6;
|
||||||
|
ridge_w = 3.2;
|
||||||
|
|
||||||
|
/* [Gravur] */
|
||||||
|
let_size = 11; // grosse R/A/C/I
|
||||||
|
let_dep = 0.9;
|
||||||
|
word_size = 5; // RESPONSIBLE ...
|
||||||
|
word_dep = 0.7;
|
||||||
|
border_inset = 7;
|
||||||
|
border_w = 1.6;
|
||||||
|
border_dep= 0.7;
|
||||||
|
|
||||||
|
/* [Action-Card-Halter] (Karte 60x90, steht leicht nach hinten geneigt) */
|
||||||
|
card_w = 84; // Plinthen-Breite
|
||||||
|
card_block_d = 22;
|
||||||
|
card_block_h = 16;
|
||||||
|
card_cy = 78; // Position hinten
|
||||||
|
slot_w = 63; // Kartenbreite 60 + Spiel
|
||||||
|
slot_t = 4; // Kartendicke + Spiel
|
||||||
|
slot_tilt = 12; // Grad Neigung nach hinten
|
||||||
|
|
||||||
|
$fn = 64;
|
||||||
|
|
||||||
|
// 10 Sockel: Winkel ab oben (90°) im Uhrzeigersinn, 36° Schritt.
|
||||||
|
// Gruppen: A=[0] C=[1..4] I=[5,6] R=[7,8,9]
|
||||||
|
function ang(i) = 90 - i*36;
|
||||||
|
GROUP_A = [0];
|
||||||
|
GROUP_C = [1,2,3,4];
|
||||||
|
GROUP_I = [5,6];
|
||||||
|
GROUP_R = [7,8,9];
|
||||||
|
// Sektor-Mittenwinkel (fuer Buchstaben) und Grenzwinkel (fuer Trennstege)
|
||||||
|
A_mid = 90; C_mid = 0; I_mid = -108; R_mid = 162;
|
||||||
|
DIVIDERS = [72, -72, -144, 108];
|
||||||
|
|
||||||
|
module rrect(w, d, h, r) {
|
||||||
|
linear_extrude(h) offset(r) offset(-r) square([w, d], center=true);
|
||||||
|
}
|
||||||
|
|
||||||
|
module base_slab() {
|
||||||
|
difference() {
|
||||||
|
rrect(board_w, board_d, base_h, corner_r);
|
||||||
|
// gefaste Ecke vorne rechts
|
||||||
|
translate([board_w/2, -board_d/2, -1])
|
||||||
|
rotate([0,0,45]) cube([cut_corner, cut_corner, base_h+2], center=true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module figure_socket(a, r=ring_R) {
|
||||||
|
x = dial_cx + r*cos(a); y = dial_cy + r*sin(a);
|
||||||
|
translate([x, y, base_h - sock_dep]) {
|
||||||
|
cylinder(d=sock_d, h=sock_dep + 0.2);
|
||||||
|
translate([0,0,sock_dep - sock_lead])
|
||||||
|
cylinder(d1=sock_d, d2=sock_d + 2*sock_lead, h=sock_lead + 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module chip_well() {
|
||||||
|
translate([dial_cx, dial_cy, base_h - chip_dep]) cylinder(d=chip_d, h=chip_dep + 0.2);
|
||||||
|
// Greifkerbe Richtung vorn
|
||||||
|
translate([dial_cx, dial_cy - chip_d/2, base_h - chip_dep])
|
||||||
|
cylinder(r=notch_r, h=chip_dep + 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
module grooves() { // konzentrische Deko-Rillen im Innenfeld
|
||||||
|
for (rr = [23 : 2 : 34])
|
||||||
|
translate([dial_cx, dial_cy, base_h - 0.5])
|
||||||
|
linear_extrude(0.6)
|
||||||
|
difference() { circle(r=rr); circle(r=rr-0.8); }
|
||||||
|
}
|
||||||
|
|
||||||
|
module dividers() {
|
||||||
|
for (a = DIVIDERS) {
|
||||||
|
ri = chip_d/2 + 3; ro = ring_R + sock_d/2 + 4;
|
||||||
|
translate([dial_cx, dial_cy, base_h])
|
||||||
|
rotate([0,0,a])
|
||||||
|
translate([(ri+ro)/2, 0, ridge_h/2])
|
||||||
|
cube([ro-ri, ridge_w, ridge_h], center=true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module letter(a, ch) {
|
||||||
|
rl = ring_R + 13;
|
||||||
|
x = dial_cx + rl*cos(a); y = dial_cy + rl*sin(a);
|
||||||
|
translate([x, y, base_h - let_dep])
|
||||||
|
linear_extrude(let_dep + 0.1)
|
||||||
|
text(ch, size=let_size, halign="center", valign="center",
|
||||||
|
font="DejaVu Sans:style=Bold");
|
||||||
|
}
|
||||||
|
|
||||||
|
module word(txt, x, y, rot) {
|
||||||
|
translate([x, y, base_h - word_dep])
|
||||||
|
rotate([0,0,rot])
|
||||||
|
linear_extrude(word_dep + 0.1)
|
||||||
|
text(txt, size=word_size, halign="center", valign="center");
|
||||||
|
}
|
||||||
|
|
||||||
|
module border_channel() {
|
||||||
|
translate([0,0, base_h - border_dep])
|
||||||
|
linear_extrude(border_dep + 0.1)
|
||||||
|
difference() {
|
||||||
|
offset(-border_inset) offset(corner_r) offset(-corner_r) square([board_w, board_d], center=true);
|
||||||
|
offset(-border_inset-border_w) offset(corner_r) offset(-corner_r) square([board_w, board_d], center=true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module card_block() {
|
||||||
|
translate([0, card_cy, 0]) rrect(card_w, card_block_d, base_h + card_block_h, 4);
|
||||||
|
}
|
||||||
|
module card_slot() {
|
||||||
|
// oben offener Schlitz, leicht nach HINTEN geneigt; Boden bleibt ~3 mm stehen
|
||||||
|
translate([0, card_cy, 22])
|
||||||
|
rotate([-slot_tilt, 0, 0])
|
||||||
|
cube([slot_w, slot_t, 38], center=true);
|
||||||
|
}
|
||||||
|
|
||||||
|
module raci_board() {
|
||||||
|
difference() {
|
||||||
|
union() {
|
||||||
|
base_slab();
|
||||||
|
dividers();
|
||||||
|
card_block();
|
||||||
|
}
|
||||||
|
chip_well();
|
||||||
|
grooves();
|
||||||
|
for (i=[0:9]) figure_socket(ang(i));
|
||||||
|
// grosse Buchstaben
|
||||||
|
letter(A_mid, "A");
|
||||||
|
letter(C_mid, "C");
|
||||||
|
letter(I_mid, "I");
|
||||||
|
letter(R_mid, "R");
|
||||||
|
// Woerter
|
||||||
|
word("RESPONSIBLE", -ring_R-29, dial_cy+6, 90);
|
||||||
|
word("CONSULTED", ring_R+29, dial_cy+6, -90);
|
||||||
|
word("INFORMED", dial_cx, dial_cy - ring_R - 30, 0);
|
||||||
|
word("ACCOUNTABLE", dial_cx, card_cy - card_block_d/2 - 7, 0);
|
||||||
|
border_channel();
|
||||||
|
card_slot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
raci_board();
|
||||||
|
|
||||||
|
echo(board=[board_w, board_d, base_h], sockets=10, raci="R3 A1 C4 I2",
|
||||||
|
chip_well=chip_d, sock=sock_d);
|
||||||
Loading…
Add table
Add a link
Reference in a new issue