""" Feldbett Konstruktion - FreeCAD Python Script ============================================= Ausführen in FreeCAD: Menü → Makro → Makro ausführen → diese Datei wählen Koordinatensystem: X = Längsrichtung (Liegeachse) Y = Höhe Z = Querrichtung (Breite) Struktur: A = Längsstangen (2x, durchgehend, Z=±hwA, Y=totalH) Q = Querstreben (3x, quer in Z, kürzer als Breite, Y=legH) D = Diagonalen (12x, A-Knoten → Q-Ende, 3D-diagonal) B = Beine (12x, Q-Ende → Bodenknoten, gespiegelt zu D) F = Füße (kurze senkrechte Stücke unter Bodenknoten) """ import FreeCAD as App import FreeCADGui as Gui import Part import math # ============================================================ # PARAMETER — hier anpassen # ============================================================ L = 35.0 # Stangenlänge [mm] — ALLE Stangen gleich lang BREITE_AA = 70.0 # Abstand zwischen den zwei Längsstangen (A–A) [mm] MODULE = 3 # Anzahl Module # Rohr-Parameter ROHR_DA = 25.0 # Außendurchmesser Rohr [mm] ROHR_WAND = 2.0 # Wandstärke [mm] FUSS_LAENGE = 10.0 # Länge der Fußstücke nach unten [mm] # Farben (R, G, B) je 0.0–1.0 FARBE_A = (0.20, 0.45, 0.75) # blau FARBE_D = (0.06, 0.43, 0.34) # grün FARBE_Q = (0.73, 0.46, 0.09) # amber FARBE_B = (0.42, 0.25, 0.63) # lila FARBE_F = (0.50, 0.50, 0.50) # grau # ============================================================ # BERECHNETE GEOMETRIE # ============================================================ hwA = BREITE_AA / 2.0 # halbe Breite A–A hwQ = L / 2.0 # halbe Q-Länge (Q = L lang) dZ = hwA - hwQ # Z-Einzug von A nach Q legH2 = 0.75 * L**2 - dZ**2 if legH2 <= 0: raise ValueError( f"Geometrie unmöglich: L={L} zu kurz für Breite={BREITE_AA}. " f"Mindest-L = {math.sqrt(dZ**2/0.75 + dZ**2/0.75*0.25):.1f} mm" ) legH = math.sqrt(legH2) # Höhe einer Ebene (A→Q oder Q→Boden) totalH = 2.0 * legH # Gesamthöhe step = 2.0 * L # Abstand Modul-zu-Modul (inkl. Lücke) aEnd = (MODULE - 1) * step + L # X-Ende der Längsstangen # Kontrollrechnung D-Länge dLen = math.sqrt((L/2)**2 + legH**2 + dZ**2) print("=" * 50) print("FELDBETT GEOMETRIE") print("=" * 50) print(f" Stangenlänge L = {L:.1f} mm") print(f" Breite A–A = {BREITE_AA:.1f} mm") print(f" Q-Länge = {L:.1f} mm (= L ✓)") print(f" Liegelänge (A-Ende) = {aEnd:.1f} mm") print(f" Höhe gesamt = {totalH:.1f} mm") print(f" legH (pro Ebene) = {legH:.2f} mm") print(f" D/B-Länge = {dLen:.2f} mm (sollte = {L:.1f})") print(f" Module = {MODULE}") print(f" Rohr DA/Wand = {ROHR_DA}/{ROHR_WAND} mm") print() # ============================================================ # HILFSFUNKTIONEN # ============================================================ ROHR_DI = ROHR_DA - 2.0 * ROHR_WAND # Innendurchmesser def make_tube(p1, p2, label, color): """ Erzeugt ein Hohlrohr von Punkt p1 nach p2. p1, p2: FreeCAD.Vector """ direction = p2 - p1 length = direction.Length if length < 0.01: return None # Außenzylinder outer = Part.makeCylinder(ROHR_DA / 2.0, length) # Innenzylinder (Hohlraum) inner = Part.makeCylinder(ROHR_DI / 2.0, length) tube = outer.cut(inner) # Ausrichten: Zylinder liegt standardmäßig entlang Z-Achse # → Rotation auf Zielrichtung z_axis = App.Vector(0, 0, 1) norm_dir = direction.normalize() cross = z_axis.cross(norm_dir) dot = z_axis.dot(norm_dir) if cross.Length > 1e-6: angle = math.degrees(math.acos(max(-1.0, min(1.0, dot)))) rot = App.Rotation(cross, angle) elif dot < 0: # 180° Rotation um beliebige Querachse rot = App.Rotation(App.Vector(1, 0, 0), 180) else: rot = App.Rotation() placement = App.Placement(p1, rot) tube.Placement = placement # FreeCAD Objekt anlegen obj = doc.addObject("Part::Feature", label) obj.Shape = tube # Farbe setzen if hasattr(obj, "ViewObject") and obj.ViewObject: obj.ViewObject.ShapeColor = color return obj def vec(x, y, z): return App.Vector(x, y, z) # ============================================================ # DOKUMENT ANLEGEN # ============================================================ doc_name = "Feldbett" if doc_name in App.listDocuments(): App.closeDocument(doc_name) doc = App.newDocument(doc_name) # ============================================================ # LÄNGSSTANGEN A (2x durchgehend) # ============================================================ make_tube( vec(0, totalH, -hwA), vec(aEnd, totalH, -hwA), "A_links", FARBE_A ) make_tube( vec(0, totalH, +hwA), vec(aEnd, totalH, +hwA), "A_rechts", FARBE_A ) # ============================================================ # MODULE # ============================================================ for m in range(MODULE): xStart = m * step xAL = xStart # X linker A-Knoten xAR = xStart + L # X rechter A-Knoten xQ = xStart + L / 2 # X Mitte → Q-Position suffix = f"_M{m+1}" # --- Querstrebe Q --- make_tube( vec(xQ, legH, -hwQ), vec(xQ, legH, +hwQ), "Q" + suffix, FARBE_Q ) # --- Diagonalen D (4 pro Modul) --- # Von A-Knoten (oben, außen) nach Q-Ende (mitte, innen) for side, zS in [("L", -1), ("R", +1)]: zA = zS * hwA zQe = zS * hwQ # linker A-Knoten → Q-Ende make_tube( vec(xAL, totalH, zA), vec(xQ, legH, zQe), f"D_xAL_{side}{suffix}", FARBE_D ) # rechter A-Knoten → Q-Ende make_tube( vec(xAR, totalH, zA), vec(xQ, legH, zQe), f"D_xAR_{side}{suffix}", FARBE_D ) # --- Beine B (4 pro Modul, gespiegelt zu D) --- for side, zS in [("L", -1), ("R", +1)]: zA = zS * hwA zQe = zS * hwQ # Q-Ende → linker Bodenknoten make_tube( vec(xQ, legH, zQe), vec(xAL, 0, zA), f"B_xAL_{side}{suffix}", FARBE_B ) # Q-Ende → rechter Bodenknoten make_tube( vec(xQ, legH, zQe), vec(xAR, 0, zA), f"B_xAR_{side}{suffix}", FARBE_B ) # --- Füße F (4 pro Modul, senkrecht nach unten) --- for xF in [xAL, xAR]: for zS in [-1, +1]: zA = zS * hwA make_tube( vec(xF, 0, zA), vec(xF, -FUSS_LAENGE, zA), f"F_x{int(xF)}_z{int(zA)}{suffix}", FARBE_F ) # ============================================================ # ABSCHLUSS # ============================================================ doc.recompute() # Kamera auf Objekt ausrichten try: Gui.activeDocument().activeView().fitAll() Gui.SendMsgToActiveView("ViewFit") except Exception: pass print("Feldbett erfolgreich erstellt!") print(f" Objekte im Dokument: {len(doc.Objects)}") print() print("STÜCKLISTE:") print(f" A Längsstangen : 2 Stück à {aEnd:.0f} mm") print(f" Q Querstreben : {MODULE} Stück à {L:.0f} mm") print(f" D Diagonalen : {MODULE*4} Stück à {dLen:.1f} mm") print(f" B Beine : {MODULE*4} Stück à {dLen:.1f} mm") print(f" F Füße : {MODULE*4} Stück à {FUSS_LAENGE:.0f} mm") print() print("CONNECTOR-WINKEL:") alpha_AD = math.degrees(math.acos( abs(App.Vector(1,0,0).dot(App.Vector(L/2,-legH,-dZ).normalize())) )) print(f" Connector 1 — Winkel A↔D : {alpha_AD:.1f}°") print(f" Connector 2 — Winkel Q↔D : 60.0°") print(f" Connector 2 — Winkel D↔B : 90.0°")