From 8f2fc3842cdab831d9d2201590058e221c2e28d5 Mon Sep 17 00:00:00 2001 From: Simon Steuer Date: Wed, 26 Oct 2022 00:36:06 +0200 Subject: [PATCH] =?UTF-8?q?Der=20Algorithmus=20zur=20Berechnung=20der=20Pr?= =?UTF-8?q?ofilh=C3=B6hen=20von=20Freitr=C3=A4gern=20wurde=20erweitert,=20?= =?UTF-8?q?sodass=20nun=20auch=20die=20Profilh=C3=B6hen=20von=20St=C3=BCtz?= =?UTF-8?q?tr=C3=A4gern=20berechnet=20werden=20k=C3=B6nnen.=20Ferner=20wur?= =?UTF-8?q?de=20ein=20Slider=20zur=20Darstellung=20der=20aktuellen=20Profi?= =?UTF-8?q?lh=C3=B6he=20und=20des=20aktuellen=20Biegemoments=20erg=C3=A4nz?= =?UTF-8?q?t.=20Erg=C3=A4nzend=20dazu=20befindet=20sich=20im=20obersten=20?= =?UTF-8?q?Subplot=20nun=20ein=20Freik=C3=B6rperbild=20vom=20aktuellen=20L?= =?UTF-8?q?astfall.=20Dadurch=20ist=20ein=20einfacheres=20Verst=C3=A4ndnis?= =?UTF-8?q?=20der=20Biegemomentenlinie=20und=20des=20Graphen=20zum=20Profi?= =?UTF-8?q?lh=C3=B6henverlauf=20in=20den=20beiden=20darunterliegenden=20Su?= =?UTF-8?q?bplots=20gegeben.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rectengular_cs.py | 583 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 472 insertions(+), 111 deletions(-) diff --git a/rectengular_cs.py b/rectengular_cs.py index 89f6ccd..cb52877 100644 --- a/rectengular_cs.py +++ b/rectengular_cs.py @@ -1,147 +1,508 @@ -import argparse from matplotlib import pyplot as plt -from math import floor +from matplotlib.widgets import Slider +from matplotlib.ticker import FormatStrFormatter +from math import floor, ceil +import ctypes class ProfileHeights: - bobbin_train_segment_types = {165: 0.38, 210: 0.248, 225: 0.354648} # Key = Spulenzug-Typ - Value = Gewicht + # Ortsfaktor = Universalwert + _location_factor = 9.81 # m/s^2 + + # Sicherheitsfaktor gemäß Bauvorschrift + safety_factor = 1.7 + + # Fahrstreckengewichte nach Produktprogramm "OMNIFLO" + _rail_masses = {"AP110": 1.94, # K: Art-Nr. 821106001 - V: in kg + "APG110": 1.66, # K: Art-Nr. 821106002 - V: in kg + "APS110": 3.89, # K: Art-Nr. 821716001 - V: in kg + "AP60": 1.46 # K: Art-Nr. 821096001 - V: in kg + } + + # Spulenzugsegment-Gewichte nach CAD + _train_segment_masses = {"T165": 0.38, # K: Art.-Nr. 826906201 - V: in kg + "T210": 0.248, # K: Art.-Nr. 826906213 - V: in kg + "T225": 0.355 # K: Art.-Nr. 826906200 - V: in kg + } def __init__(self, cross_section_type, - datum, - width, - thickness, - ap_positions=None, - beam_length=2000, - min_ap_distance=200, - yield_strength=235, - segment_type=165, + cross_section_width, + cross_section_thickness, + has_two_supports, + beam_length, + individual_rail_positions=None, + individual_rail_forces=None, + rail_type="AP110", + min_rail_distance=200, + min_pillar_distance=300, + material_yield_strength=235, + bobbin_train_segment_type="T165", bobbin_mass=4, - column_distance=3.6, + colum_row_distance=3600, + pillar_width=100 ): + self.min_rail_distance = min_rail_distance + self.min_dist_first_rail_to_pillar = min_pillar_distance self.cross_section_type = cross_section_type - self.datum = datum - self.width = width - self.thickness = thickness - self.ap_positions = ap_positions + self.cross_section_width = cross_section_width + self.cross_section_thickness = cross_section_thickness + self.individual_rail_forces = individual_rail_forces + self.has_two_supports = has_two_supports self.beam_length = beam_length - self.min_ap_distance = min_ap_distance - self.yield_strength = yield_strength - self.segment_type = segment_type - self.bobbin_mass = bobbin_mass - self.column_distance = column_distance + self.material_yield_strength = material_yield_strength + self.column_row_distance = colum_row_distance + self.rail_type = rail_type + self.individual_rail_positions = individual_rail_positions + self.pillar_width = pillar_width + self.rail_dist_from_support_a = self._set_rail_dist_from_support_a(individual_rail_positions) + self.train_segment_type = bobbin_train_segment_type + self.single_bobbin_mass = bobbin_mass + self.train_segment_mass = self._set_train_segment_mass() + self.rail_mass = self._set_rail_mass() + self.rail_forces = self._set_rail_forces() + self.bend_moments_from_a = self._summed_bend_moment_from_a() + self.support_bending_moment = self._set_support_a_bending_moment() + self.transverse_force_support_b = self._set_transverse_force_support_b() + self.transverse_force_support_a = self._set_transverse_force_support_a() + self._section_starts = self._set_beam_section_start_positions() + self.all_force_positions = self._set_all_force_positions() + self._beam_section_lengths = self._set_beam_section_lengths() + self._beam_section_forces = self._set_section_forces() + self.allowed_bend_stress = self._set_allowed_bend_stress() + self.bending_moments = self._set_bending_moments() + self.heights = self.all_profile_heights() - def ap_position_check(self): - # Überprüft, ob alle AP's den zulässigen Mindestabstand zueinander einhalten + def _set_rail_dist_from_support_a(self, individual_rail_positions): + # Gibt Abstände zwischen den einzelnen AP-Profilen am Träger und der Säule zurück. + # Wenn 2 Säulen gegeben → Abstände zur "linken" Säule - if not [abs(new_pos - old_pos) < self.min_ap_distance for old_pos, new_pos in zip(self.ap_positions, self.ap_positions[1:])]: - raise ValueError(f"Abstände zwischen ") - else: - return True + # Fall 1: Es wurden keine individuellen Abstände zwischen den AP-Profilen und der Säule definiert. + # In diesem Fall berechnet die Methode die Abstände der AP-Schienen vom Lager "A" ausgehend unter + # Berücksichtigung der Mindestabstände zwischen den Schienen sowie zur Säule am Träger hängen kann. - def ap_amount(self): - return floor(self.beam_length / self.min_ap_distance) + if individual_rail_positions is None: - def individual_ap_positions(self): - # Funktion gibt Einzelabstände der AP-Profile zur Säule zurück - - # Fall 1: Es wurden keine Einzelabstände zur Säule definiert. Abstand zwischen AP's = zulässiger Mindestabstand - # Fall 2: Es wurden individuelle AP-Abstände definiert. - - if self.ap_positions is None: - return [ap_count * self.min_ap_distance for ap_count in range(1, self.ap_amount() + 1)] - elif self.ap_position_check(): - return self.ap_positions - - def force_per_ap_meter(self): - if self.bobbin_mass > 4: - raise ValueError(f"Gewicht der Spulen darf max. 4 kg betragen!") - else: - column_rows = 2 - location_factor = 9.81 # m/s^2 - ap_weight_per_meter = 1.88 - - total_bobbin_mass = self.bobbin_mass * (1000 / self.segment_type) - mass = total_bobbin_mass + 2 * self.bobbin_train_segment_types[self.segment_type] + ap_weight_per_meter - - return mass * location_factor * self.column_distance / column_rows - - def ref_moment_of_resistance(self, current_beam_length, ap_column_distances, force_per_rail): - # Funktion berechnet Widerstandsmoment für die aktuelle Trägerlänge - - total_column_distance = 0 - for ap_column_distance in ap_column_distances: - if current_beam_length >= ap_column_distance: - total_column_distance += ap_column_distance + if self.has_two_supports: + distance_first_to_last_rail = (self.beam_length - self.pillar_width - 2 * self.min_dist_first_rail_to_pillar) + rail_amount = ceil(distance_first_to_last_rail / self.min_rail_distance) + distance_two_rails = distance_first_to_last_rail / (rail_amount - 1) else: - break + distance_first_to_last_rail = self.beam_length - self.pillar_width / 2 - self.min_dist_first_rail_to_pillar + rail_amount = ceil(distance_first_to_last_rail / self.min_rail_distance) + distance_two_rails = distance_first_to_last_rail / (rail_amount - 1) + return [(self.min_dist_first_rail_to_pillar + self.pillar_width / 2) + (i * distance_two_rails) for i in range(rail_amount)] - safety_factor = 1.7 - bend_moment = force_per_rail * total_column_distance - allowed_bend_stress = (self.yield_strength * 1.2) / safety_factor + # Fall 2: Es wurden individuelle Abstände zwischen den Lasten und der Säule definiert. + return self.individual_rail_positions - return bend_moment / allowed_bend_stress + def _set_rail_forces(self): + # Gibt die Kräfte der einzelnen Fahrschienen am Träger zurück + + # Fall 1: Es wurden keine individuellen Kräfte für die einzelnen Fahrschienen definiert. + # Methode berechnet dann für alle Fahrstrecken dieselbe Kraft. + if self.individual_rail_forces is None: + single_rail_force = ((self.train_segment_mass + self.rail_mass) * ProfileHeights._location_factor) + return [single_rail_force] * len(self.rail_dist_from_support_a) + + # Fall 2: Es wurden individuelle Kräfte für die einzelnen Fahrstrecken definiert. + # Methode gibt die individuellen Kräfte unverändert zurück + return self.individual_rail_forces + + def _set_train_segment_mass(self): + # Methode ermittelt das Spulenzuggewicht zwischen zwei Säulenreihen + + bobbin_amount_per_meter = floor(1000 / int(self.train_segment_type.replace("T", ""))) # Anzahl Spulen pro Meter + + bobbin_mass_per_meter = self.single_bobbin_mass * bobbin_amount_per_meter # Gesamtes Spulengewicht pro Meter + total_segment_mass_per_meter = 2 * ProfileHeights._train_segment_masses[self.train_segment_type] + bobbin_mass_per_meter + return total_segment_mass_per_meter * (self.column_row_distance / 1000) / 2 # kg pro Säulenreihe + + def _set_rail_mass(self): + # Berechnet gesamtes Fahrstreckengewicht für Länge (Abstand) zwischen Säulenreihen zurück. + return ProfileHeights._rail_masses[self.rail_type] * self.column_row_distance / 1000 + + def _summed_bend_moment_from_a(self): + # Berechnet Gesamtmoment (Summe der Einzelmomente) am Balken vom Lager "A" ausgehend + return sum([force * distance for force, distance in zip(self.rail_forces, self.rail_dist_from_support_a)]) + + def _set_support_a_bending_moment(self): + # Berechnet Biegemoment im Lager "A", wenn Lager "A" ein dreiwertiges Lager ist (feste Einspannung) + if not self.has_two_supports: + return self.bend_moments_from_a + return 0 + + def _set_transverse_force_support_a(self): + # Berechnet Querkraft im Lager "A" + return abs(self.transverse_force_support_b - sum(self.rail_forces)) + + def _set_transverse_force_support_b(self): + # Berechnet Querkraft im Lager "B", wenn sowohl Lager "A", als auch Lager "B" existiert (nur bei Stützträgern) + if self.has_two_supports: + return self.bend_moments_from_a / self.beam_length + return 0 + + def _set_beam_section_start_positions(self): + # Bestimmt den Abstand der Bereichsanfänge zum Lager "A" + section_start_positions = self.rail_dist_from_support_a.copy() + section_start_positions.insert(0, 0) + return section_start_positions + + def _set_section_forces(self): + # Fügt Querkraft im Lager "A" hinzu, da immer linkes Schnittufer betrachtet wird. + # Dies ist notwendig, da Querkraft "A" im Flächenschwerpunkt des Schnittes in allen Trägerbereichen ein + # Moment erzeugt + section_forces = self.rail_forces.copy() + section_forces.insert(0, self.transverse_force_support_a) + return section_forces + + def _set_all_force_positions(self): + if self.has_two_supports: + all_force_positions = self._section_starts.copy() + all_force_positions.insert(len(all_force_positions), self.beam_length) + return all_force_positions + return self._section_starts + + def all_forces(self): + all_forces = self._beam_section_forces.copy() + all_forces.insert(len(self.all_force_positions), self.transverse_force_support_b) + return all_forces + + def _set_beam_section_lengths(self): + # Bereichslänge = Differenz zwischen nächster Kraftposition und vorherigen Kraftposition + return [new_pos - old_pos for new_pos, old_pos in zip(self.all_force_positions[1:], self.all_force_positions)] + + def _set_bending_moments(self): + # Methode unterteilt Träger in Bereiche (vom linken Lager "A" ausgehend zum rechten Lager "b") und gibt die + # Schnittreaktionen (konkret Biegemomente) zurück. Aus diesen wird der Momentenverlauf gebildet. + + section_bending_moments = [] + subsection_lengths = self._beam_section_lengths.copy() + + for i, (section_start, section_length) in enumerate(zip(self._section_starts, self._beam_section_lengths)): + + section_moment = (self._beam_section_forces[0] * (section_start + section_length) - self.support_bending_moment) + summed_subsection_lengths = 0 + + for subsection_length, force in zip(subsection_lengths[:i], self._beam_section_forces[1:]): + summed_subsection_lengths += subsection_length + section_moment -= force * (section_start - summed_subsection_lengths + section_length) + + section_bending_moments.append(abs(round((section_moment / 1000), 2))) + + if self.has_two_supports: + section_bending_moments.insert(0, 0.0) + else: + section_bending_moments.insert(0, self.support_bending_moment / 1000) + + return section_bending_moments + + def reference_moment_of_resistance(self, bending_moment): + # Funktion berechnet Referenz-Widerstandsmoment für die aktuelle Trägerlänge + return bending_moment / self.allowed_bend_stress + + def _set_allowed_bend_stress(self): + return (self.material_yield_strength * 1.2) / ProfileHeights.safety_factor def recalculated_moment_of_resistance(self, height): - # Berechnet das Widerstandsmoment mit der derzeitigen Profilhöhe für verschiedene Querschnitts-Arten + # Aktuelle Profilhöhe wird zur Ermittlung des derzeitigen Widerstandsmoments in die Widerstandsmoment-Formeln + # der unterschiedlichen Querschnittsarten eingesetzt. - inner_width = self.width - 2 * self.thickness - inner_height = height - 2 * self.thickness - bar_width = self.width - self.thickness + inner_width = self.cross_section_width - 2 * self.cross_section_thickness + inner_height = height - 2 * self.cross_section_thickness + bar_width = self.cross_section_width - self.cross_section_thickness - # 1. Widerstandsmoment-Berechnung für idealisiertes rechteckiges Hohlprofil: - if self.cross_section_type == "hohlprofil": - return (self.width * height ** 3 - inner_width * inner_height ** 3) / (6 * height) + # 1. Idealisiertes rechteckiges Hohlprofil: + if self.cross_section_type == "Hohlprofil": + return (self.cross_section_width * height ** 3 - inner_width * inner_height ** 3) / (6 * height) - # 2. Widerstandsmoment-Berechnung für idealisiertes IPE-Profil / C-Profil: - elif self.cross_section_type in ["c", "ipe"]: - return (self.width * height ** 3 - bar_width * inner_height ** 3) / (6 * height) - - def datum_reference(self): - # Legt fest, ob die notwendige Profilhöhe im Plot in Abhängigkeit von jedem Millimeter der Gesamtlänge [l] - # des Trägers oder der Trägerlänge an der Stelle jedes AP-Profils [n] dargestellt werden soll. - - if self.datum == "l": - return list(range(self.min_ap_distance, self.beam_length + 1)) - - elif self.datum == "n": - return self.individual_ap_positions() - - def height_calculation(self): - - start_height = 2 * self.thickness - - beam_lengths = self.datum_reference() - ap_column_distance = self.individual_ap_positions() - force_per_ap_meter = self.force_per_ap_meter() + # 2. Idealisiertes IPE-Profil / C-Profil: + elif self.cross_section_type in ["C-Profil", "IPE"]: + return (self.cross_section_width * height ** 3 - bar_width * inner_height ** 3) / (6 * height) + def all_profile_heights(self): heights = [] - lengths = [] - moment_of_resistances = [] - recalculated_moment_of_resistance = 0 - for count, beam_length in enumerate(beam_lengths): - reference_moment_of_resistance = self.ref_moment_of_resistance(beam_length, ap_column_distance, force_per_ap_meter) + bending_moments_converted = [i * 1000 for i in self.bending_moments] # Umrechnung von Nm in Nmm + + for bending_moment, force_position in zip(bending_moments_converted, self.all_force_positions): + reference_moment_of_resistance = self.reference_moment_of_resistance(abs(bending_moment)) + recalculated_moment_of_resistance = 0 + height = 2 * self.cross_section_thickness while recalculated_moment_of_resistance < reference_moment_of_resistance: - recalculated_moment_of_resistance = self.recalculated_moment_of_resistance(start_height) - start_height += 0.01 + recalculated_moment_of_resistance = self.recalculated_moment_of_resistance(height) + height += 0.01 + heights.append(height) + return heights - moment_of_resistances.append(reference_moment_of_resistance) - heights.append(start_height) - lengths.append(beam_length) - - return heights, lengths + def current_value(self, current_beam_length, elements): + for i, force_position in enumerate(self.all_force_positions): + if current_beam_length <= force_position: + # m = Steigung, x = aktuelle Länge im derzeitigen Bereich, t = Höhe bei Bereichsbeginn + m = (elements[i] - elements[i - 1]) / self._beam_section_lengths[i - 1] + x = current_beam_length - self.all_force_positions[i - 1] + t = elements[i - 1] + return m * x + t -a = ProfileHeights(cross_section_type="hohlprofil", datum="l", width=50, thickness=2.5) +height_1 = ProfileHeights(cross_section_type="Hohlprofil", + cross_section_width=50, + cross_section_thickness=3, + individual_rail_positions=[200, 500, 700, 1100], + individual_rail_forces=[500, 500, 500, 500], + beam_length=1200, + min_rail_distance=200, + has_two_supports=True) -ya_heights, xa_lengths = a.height_calculation() -plt.plot(xa_lengths, ya_heights) -plt.xlabel("Länge des Trägers in mm") -plt.ylabel("Benötigte Querschnittshöhe des Trägers in mm") -plt.grid(True) +# =========================================================================================================================== +# = PLOTTEN = +# =========================================================================================================================== + +# Monitordaten für Figure-Größe +user32 = ctypes.windll.user32 +user32.SetProcessDPIAware() +monitor_width = user32.GetSystemMetrics(0) +monitor_height = user32.GetSystemMetrics(1) +dpi = 120 + +fig, (ax_drawing, ax_bending_moment, ax_heights) = plt.subplots(nrows=3, ncols=1, sharex=True, figsize=(monitor_width / dpi, monitor_height / dpi)) + +# Globale Einstellungen +ax_drawing.set_title("Tool zur Bestimmung der Mindestquerschnittshöhe eines Profilträgers", fontsize=20) +subplot_adjust_left = 0.1 +plt.subplots_adjust(left=subplot_adjust_left, right=1 - subplot_adjust_left, top=1 - subplot_adjust_left, bottom=subplot_adjust_left, hspace=0) + +# Abszisseneinstellungen +x_min_val = -(max(height_1.all_force_positions) * 0.1) +x_max_val = max(height_1.all_force_positions) * 1.1 + +ax_heights.set_xlim(x_min_val, x_max_val) +ax_heights.set_xticks(height_1.all_force_positions) +ax_heights.xaxis.set_major_formatter(FormatStrFormatter("%.1f")) + +# Einstellungen Biegemoment-Plot +ax_bending_moment.set_ylabel("Biegemoment [Nm]") +ax_bending_moment_y_max = max(height_1.bending_moments) * 1.25 +ax_bending_moment.set_ylim(0, ax_bending_moment_y_max) + +# Einstellungen Profilhöhen-Plot +ax_heights.set_xlabel("Kraftpositionen [mm]") +ax_heights.set_ylabel("Benötigte Profilhöhe [mm]") + +ax_heights_y_max = max(height_1.heights) * 1.25 +ax_heights.set_ylim(0, ax_heights_y_max) +ax_heights.set_xticks(height_1.all_force_positions) + +# Berechnet horizontale Pixelanzahl der Plots +x_plot_px_count = monitor_width * (1 - 2 * subplot_adjust_left) + +# Rechnet Längen der x-Achse in Pixel um (wie viele Pixel sind 1 mm?). +# Wichtig, damit die Pfeillängen der Kräfte unabhängig von der Länge des Balkens immer gleich lang dargestellt werden. +mm_in_px_converter = (abs(x_min_val) + abs(x_max_val)) / x_plot_px_count + + +# =========================================================================================================================== +# = Zeichnung = +# =========================================================================================================================== + +# Extremwerte der Zeichnungsordinate: +y_max_drawing = 300 +ax_drawing.set_ylim(0, y_max_drawing) +ax_drawing.set_yticks([]) + +# Darstellung eines schematischen Trägers: +beam_y_pos_in_plot = y_max_drawing * 0.35 +ax_drawing.hlines(beam_y_pos_in_plot, height_1.all_force_positions[0], height_1.all_force_positions[len(height_1.all_force_positions) - 1], + color="black", linewidth=3) + +force_arrow_length = 0.5 * beam_y_pos_in_plot + + +def draw_transverse_force(x_pos, y_beam_pos, arrow_length, support_name): + + # Darstellung des Balkenendes als Punkt mit Lagerbenennung + ax_drawing.plot(x_pos, y_beam_pos, "o", color="black") + + # Pfeildarstellung für Querkraft + ax_drawing.annotate('', xytext=(x_pos, y_beam_pos), xycoords='data', xy=(x_pos, y_beam_pos - arrow_length), textcoords='data', + arrowprops=dict(color="red", width=1, headwidth=6)) + + # Pfeilbeschreibung: + if support_name == "A": + txt_alignment = "left" + txt_x_offset = -120 + name_x_offset = -50 + name_y_offset = 15 + transverse_force = height_1.transverse_force_support_a + else: + txt_alignment = "left" + txt_x_offset = 20 + name_x_offset = 20 + name_y_offset = 15 + transverse_force = height_1.transverse_force_support_b + + # Plottet Querkraft-Wert entweder am Festlager "A", oder am Loslager "B" + ax_drawing.annotate(f"F{support_name}y: {transverse_force:.0f}N", xy=(x_pos, y_beam_pos - arrow_length), + xycoords='data', xytext=(txt_x_offset, 0), textcoords='offset pixels', horizontalalignment=txt_alignment) + + # Plottet Lagerbezeichnung + ax_drawing.annotate(f"{support_name}", xy=(x_pos, y_beam_pos), xycoords='data', xytext=(name_x_offset, name_y_offset), + textcoords='offset pixels', ha=txt_alignment, fontsize=20) + + +def draw_fixed_support(y_beam_pos, px_amount_per_mm, arrow_length, support_name): + # Querkraft: + draw_transverse_force(x_pos=height_1.all_force_positions[0], y_beam_pos=y_beam_pos, + arrow_length=arrow_length, support_name=support_name) + + # Normalkraft: + x_arrow_length = px_amount_per_mm * arrow_length + ax_drawing.annotate('', xycoords='data', xytext=(height_1.all_force_positions[0], y_beam_pos), xy=(-x_arrow_length, y_beam_pos), + textcoords='data', arrowprops=dict(color="red", width=1, headwidth=6)) + + +def draw_couple(y_beam_pos, px_amount_per_mm, arrow_length, support_name): + # Feste Einspannung + draw_fixed_support(y_beam_pos=y_beam_pos, px_amount_per_mm=px_amount_per_mm, arrow_length=arrow_length, support_name=support_name) + + # Plottet Querkraft-Pfeil entweder am Festlager "A", oder am Loslager "B" + ax_drawing.annotate(f"M1: {(height_1.support_bending_moment / 1000):.0f} Nm", xy=(height_1.all_force_positions[0], y_beam_pos - arrow_length), + xycoords='data', xytext=(-120, -20), textcoords='offset pixels', horizontalalignment="left") + + ax_drawing.plot([height_1.all_force_positions[0] - 2.5], [y_beam_pos], marker=r'$\circlearrowright$', ms=30, color="red") + + +def drawing_dimensions(y_pos_beam, px_amount_per_mm, arrow_length): + dim_line_distance_from_beam = 70 + dim_line_y_pos = y_pos_beam + dim_line_distance_from_beam + dim_leader_extension = 20 + + for i, (force_pos, force) in enumerate(zip(height_1.all_force_positions, height_1.all_forces())): + # Darstellung Maßhilfslinien + ax_drawing.vlines(force_pos, y_pos_beam, dim_line_y_pos + dim_leader_extension, colors="black", linewidth=1) + + if height_1.has_two_supports: + end_index = len(height_1.all_force_positions) - 1 + else: + end_index = len(height_1.all_force_positions) + + if 0 < i < end_index: + # Kraftpfeile + # Pfeildarstellung + ax_drawing.annotate('', xycoords='data', xytext=(height_1.all_force_positions[i], beam_y_pos_in_plot + arrow_length), + xy=(height_1.all_force_positions[i], beam_y_pos_in_plot), textcoords='data', + arrowprops=dict(color="red", width=1, headwidth=6)) + + # Pfeilbeschreibung + ax_drawing.annotate(f'F{i}:\n{force:.1f}N', xy=(force_pos, y_pos_beam - arrow_length), xycoords='data', xytext=(0, 0), + textcoords='offset pixels', horizontalalignment="center", fontsize=10) + + if i < (len(height_1.all_force_positions) - 1): + # Darstellung Maßlinien + ax_drawing.annotate('', xy=(height_1.all_force_positions[i], dim_line_y_pos), xycoords='data', + xytext=(height_1.all_force_positions[i + 1], dim_line_y_pos), textcoords='data', arrowprops={'arrowstyle': '<->'}) + + # Darstellung Maße (Bereichslängen) + section_length = (height_1.all_force_positions[i + 1] - force_pos) + dim_x_pos = (section_length / 2) / px_amount_per_mm + ax_drawing.annotate(f'{section_length:.1f}', xy=(force_pos, dim_line_y_pos), xycoords='data', xytext=(dim_x_pos, 5), + textcoords='offset pixels', horizontalalignment="center") + + +drawing_dimensions(y_pos_beam=beam_y_pos_in_plot, px_amount_per_mm=mm_in_px_converter, arrow_length=force_arrow_length) + +if height_1.has_two_supports: + draw_transverse_force(x_pos=height_1.all_force_positions[len(height_1.all_force_positions) - 1], y_beam_pos=beam_y_pos_in_plot, + arrow_length=force_arrow_length, support_name="B") + + draw_fixed_support(y_beam_pos=beam_y_pos_in_plot, px_amount_per_mm=mm_in_px_converter, arrow_length=force_arrow_length, support_name="A") +else: + draw_couple(y_beam_pos=beam_y_pos_in_plot, px_amount_per_mm=mm_in_px_converter, arrow_length=force_arrow_length, support_name="A") + + +# =========================================================================================================================== +# = Höhe und Biegemoment = +# =========================================================================================================================== + + +def text_alignment(): + if height_1.has_two_supports: + return "center" + return "left" + + +def plot_bending_moment(): + + # Momentenlinie + ax_bending_moment.plot(height_1.all_force_positions, height_1.bending_moments, color="black") + ax_bending_moment.fill_between(height_1.all_force_positions, height_1.bending_moments, hatch="/", facecolor="#FFFFFF") + + # Maximalmoment + bending_moments_and_beam_lengths = dict(zip(height_1.bending_moments, height_1.all_force_positions)) + max_bending_moment = max(bending_moments_and_beam_lengths.keys()) + len_at_max_bending_moment = bending_moments_and_beam_lengths[max_bending_moment] + text = f"max: {round(max_bending_moment, 2)} Nm" + + ax_bending_moment.plot(len_at_max_bending_moment, max_bending_moment, "o", color="black") + + ax_bending_moment.annotate(text, xy=(len_at_max_bending_moment, max_bending_moment), xytext=(0, 10), textcoords='offset points', + horizontalalignment=text_alignment()) + + +def plot_heights(): + + # Hauptplot + ax_heights.plot(height_1.all_force_positions, height_1.heights, color="black", + label=f"{height_1.cross_section_type} - Breite: {height_1.cross_section_width} mm") + ax_heights.fill_between(height_1.all_force_positions, height_1.heights, hatch="/", facecolor="#FFFFFF") + + # Plot Maximalhöhe + heights_and_beam_lengths = dict(zip(height_1.heights, height_1.all_force_positions)) + max_height = max(heights_and_beam_lengths.keys()) + beam_length_at_max_height = heights_and_beam_lengths[max_height] + text = f"max: {max_height:.2f} mm" + + ax_heights.plot(beam_length_at_max_height, max_height, "o", color="black") + ax_heights.annotate(text, xy=(beam_length_at_max_height, max_height), xytext=(0, 10), textcoords='offset points', + horizontalalignment=text_alignment()) + + ax_heights.legend(loc=1) + + +def update_slider_length(current_length): + ax_heights.clear() + ax_bending_moment.clear() + + plot_heights() + current_height = round(height_1.current_value(current_length, height_1.heights), 2) + current_bending_moment = round(height_1.current_value(current_length, height_1.bending_moments), 2) + + # Plot aktuelle Höhe + plot_bending_moment() + ax_heights.plot(current_length, current_height, "o", color="black") + ax_heights.annotate(current_height, xy=(current_length, current_height), xytext=(0, 7), textcoords='offset points', + horizontalalignment="center") + + # Plot aktuelles Moment + ax_bending_moment.plot(current_length, current_bending_moment, "o", color="black") + ax_bending_moment.annotate(current_bending_moment, xy=(current_length, current_bending_moment), xytext=(0, 7), textcoords='offset points', + horizontalalignment="center") + + ax_heights.vlines(current_length, 0, max(height_1.heights) * 1.25, color="gray", linewidth=1) + ax_bending_moment.vlines(current_length, 0, max(height_1.bending_moments) * 1.25, color="gray", linewidth=1) + + plt.draw() + + +plot_bending_moment() +plot_heights() + +ax_slider = plt.axes([0.125, 0.0025, 0.775, 0.05], facecolor="blue") +slider = Slider(ax_slider, "Trägerlänge", valmin=0, valmax=height_1.beam_length, valstep=1, valinit=0) +slider.on_changed(update_slider_length) + plt.show()