RCAIDE-GUI 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. common_widgets/__init__.py +6 -0
  2. common_widgets/animated_toggle.py +141 -0
  3. common_widgets/color.py +17 -0
  4. common_widgets/data_entry_widget.py +337 -0
  5. common_widgets/image_widget.py +21 -0
  6. common_widgets/unit_picker_widget.py +48 -0
  7. main.py +168 -0
  8. rcaide_gui-1.0.0.dist-info/METADATA +96 -0
  9. rcaide_gui-1.0.0.dist-info/RECORD +170 -0
  10. rcaide_gui-1.0.0.dist-info/WHEEL +5 -0
  11. rcaide_gui-1.0.0.dist-info/entry_points.txt +2 -0
  12. rcaide_gui-1.0.0.dist-info/top_level.txt +5 -0
  13. rcaide_io.py +818 -0
  14. tabs/__init__.py +10 -0
  15. tabs/aircraft_configs/__init__.py +3 -0
  16. tabs/aircraft_configs/aircraft_configs.py +301 -0
  17. tabs/analysis/__init__.py +4 -0
  18. tabs/analysis/analysis.py +92 -0
  19. tabs/analysis/widgets/__init__.py +13 -0
  20. tabs/analysis/widgets/aeroacoustics_widget.py +158 -0
  21. tabs/analysis/widgets/aerodynamics_widget.py +108 -0
  22. tabs/analysis/widgets/analysis_data_widget.py +29 -0
  23. tabs/analysis/widgets/atmosphere_widget.py +49 -0
  24. tabs/analysis/widgets/costs_widget.py +35 -0
  25. tabs/analysis/widgets/energy_widget.py +32 -0
  26. tabs/analysis/widgets/geometry_widget.py +55 -0
  27. tabs/analysis/widgets/planets_widget.py +34 -0
  28. tabs/analysis/widgets/propulsion_widget.py +53 -0
  29. tabs/analysis/widgets/stability_widget.py +34 -0
  30. tabs/analysis/widgets/weights_widget.py +94 -0
  31. tabs/geometry/__init__.py +6 -0
  32. tabs/geometry/aircraft_configs.py +232 -0
  33. tabs/geometry/frames/__init__.py +15 -0
  34. tabs/geometry/frames/booms/__init__.py +5 -0
  35. tabs/geometry/frames/booms/boom_frame.py +238 -0
  36. tabs/geometry/frames/cargo_bays/__init__.py +5 -0
  37. tabs/geometry/frames/cargo_bays/cargo_bay_frame.py +159 -0
  38. tabs/geometry/frames/default_frame.py +23 -0
  39. tabs/geometry/frames/energy_network/__init__.py +2 -0
  40. tabs/geometry/frames/energy_network/turbofan_network/__init__.py +3 -0
  41. tabs/geometry/frames/energy_network/turbofan_network/frames/__init__.py +1 -0
  42. tabs/geometry/frames/energy_network/turbofan_network/widgets/__init__.py +1 -0
  43. tabs/geometry/frames/fuselages/__init__.py +5 -0
  44. tabs/geometry/frames/fuselages/fuselage_frame.py +294 -0
  45. tabs/geometry/frames/geometry_frame.py +91 -0
  46. tabs/geometry/frames/landing_gears/__init__.py +5 -0
  47. tabs/geometry/frames/landing_gears/landing_gear_frame.py +189 -0
  48. tabs/geometry/frames/nacelles/__init__.py +1 -0
  49. tabs/geometry/frames/powertrain/__init__.py +6 -0
  50. tabs/geometry/frames/powertrain/converters/__init__.py +5 -0
  51. tabs/geometry/frames/powertrain/converters/converter_frame.py +137 -0
  52. tabs/geometry/frames/powertrain/distributors/__init__.py +5 -0
  53. tabs/geometry/frames/powertrain/distributors/distributor_frame.py +143 -0
  54. tabs/geometry/frames/powertrain/nacelles/__init__.py +5 -0
  55. tabs/geometry/frames/powertrain/nacelles/nacelle_frame.py +283 -0
  56. tabs/geometry/frames/powertrain/powertrain_frame.py +259 -0
  57. tabs/geometry/frames/powertrain/propulsors/__init__.py +5 -0
  58. tabs/geometry/frames/powertrain/propulsors/propulsor_frame.py +146 -0
  59. tabs/geometry/frames/powertrain/sources/__init__.py +6 -0
  60. tabs/geometry/frames/powertrain/sources/energy_source_frame.py +147 -0
  61. tabs/geometry/frames/vehicle_frame.py +171 -0
  62. tabs/geometry/frames/wings/__init__.py +5 -0
  63. tabs/geometry/frames/wings/wings_frame.py +457 -0
  64. tabs/geometry/geometry.py +513 -0
  65. tabs/geometry/widgets/__init__.py +12 -0
  66. tabs/geometry/widgets/booms/__init__.py +6 -0
  67. tabs/geometry/widgets/booms/boom_section_widget.py +109 -0
  68. tabs/geometry/widgets/cargo_bays/__init__.py +1 -0
  69. tabs/geometry/widgets/fuselages/__init__.py +8 -0
  70. tabs/geometry/widgets/fuselages/cabin_class_widget.py +188 -0
  71. tabs/geometry/widgets/fuselages/cabin_widget.py +217 -0
  72. tabs/geometry/widgets/fuselages/fuselage_section_widget.py +190 -0
  73. tabs/geometry/widgets/geometry_data_widget.py +25 -0
  74. tabs/geometry/widgets/landing_gears/__init__.py +6 -0
  75. tabs/geometry/widgets/nacelles/__init__.py +1 -0
  76. tabs/geometry/widgets/powertrain/__init__.py +9 -0
  77. tabs/geometry/widgets/powertrain/converters/__init__.py +5 -0
  78. tabs/geometry/widgets/powertrain/converters/turboelectric_generator_widget.py +74 -0
  79. tabs/geometry/widgets/powertrain/distributors/__init__.py +5 -0
  80. tabs/geometry/widgets/powertrain/distributors/fuel_line_widget.py +81 -0
  81. tabs/geometry/widgets/powertrain/modulators/__init__.py +5 -0
  82. tabs/geometry/widgets/powertrain/modulators/esc_widget.py +58 -0
  83. tabs/geometry/widgets/powertrain/nacelles/__init__.py +5 -0
  84. tabs/geometry/widgets/powertrain/nacelles/nacelle_section_widget.py +115 -0
  85. tabs/geometry/widgets/powertrain/powertrain_connector_widget.py +136 -0
  86. tabs/geometry/widgets/powertrain/powertrain_widget.py +206 -0
  87. tabs/geometry/widgets/powertrain/propulsors/__init__.py +5 -0
  88. tabs/geometry/widgets/powertrain/propulsors/turbofan_widget.py +462 -0
  89. tabs/geometry/widgets/powertrain/sources/__init__.py +6 -0
  90. tabs/geometry/widgets/powertrain/sources/fuel_tank_widget.py +103 -0
  91. tabs/geometry/widgets/powertrain/sources/source_selector_widget.py +35 -0
  92. tabs/geometry/widgets/wings/__init__.py +7 -0
  93. tabs/geometry/widgets/wings/wing_cs_widget.py +198 -0
  94. tabs/geometry/widgets/wings/wing_section_widget.py +298 -0
  95. tabs/home/__init__.py +3 -0
  96. tabs/home/home.py +585 -0
  97. tabs/mission/__init__.py +4 -0
  98. tabs/mission/mission.py +1124 -0
  99. tabs/mission/widgets/__init__.py +3 -0
  100. tabs/mission/widgets/flight_controls_widget.py +186 -0
  101. tabs/mission/widgets/mission_analysis_widget.py +166 -0
  102. tabs/mission/widgets/mission_segment_helper.py +358 -0
  103. tabs/mission/widgets/mission_segment_widget.py +358 -0
  104. tabs/solve/__init__.py +4 -0
  105. tabs/solve/plots/__init__.py +12 -0
  106. tabs/solve/plots/aeroacoustics/__init__.py +6 -0
  107. tabs/solve/plots/aeroacoustics/plot_2D_noise_contour.py +184 -0
  108. tabs/solve/plots/aeroacoustics/plot_3D_noise_contour.py +219 -0
  109. tabs/solve/plots/aeroacoustics/plot_noise_certification_contour.py +115 -0
  110. tabs/solve/plots/aeroacoustics/plot_noise_level.py +118 -0
  111. tabs/solve/plots/aerodynamics/__init__.py +10 -0
  112. tabs/solve/plots/aerodynamics/plot_aerodynamic_coefficients.py +156 -0
  113. tabs/solve/plots/aerodynamics/plot_aerodynamic_forces.py +155 -0
  114. tabs/solve/plots/aerodynamics/plot_aircraft_aerodynamics.py +136 -0
  115. tabs/solve/plots/aerodynamics/plot_drag_components.py +157 -0
  116. tabs/solve/plots/aerodynamics/plot_lift_distribution.py +107 -0
  117. tabs/solve/plots/aerodynamics/plot_rotor_conditions.py +255 -0
  118. tabs/solve/plots/aerodynamics/plot_rotor_disc_performance.py +144 -0
  119. tabs/solve/plots/aerodynamics/plot_rotor_performance.py +124 -0
  120. tabs/solve/plots/common/__init__.py +4 -0
  121. tabs/solve/plots/common/plot_style.py +71 -0
  122. tabs/solve/plots/common/set_axes.py +55 -0
  123. tabs/solve/plots/create_plot_widgets.py +34 -0
  124. tabs/solve/plots/emissions/__init__.py +3 -0
  125. tabs/solve/plots/emissions/plot_emissions.py +167 -0
  126. tabs/solve/plots/energy/__init__.py +12 -0
  127. tabs/solve/plots/energy/plot_altitude_sfc_weight.py +134 -0
  128. tabs/solve/plots/energy/plot_battery_cell_conditions.py +154 -0
  129. tabs/solve/plots/energy/plot_battery_degradation.py +157 -0
  130. tabs/solve/plots/energy/plot_battery_module_C_rates.py +139 -0
  131. tabs/solve/plots/energy/plot_battery_module_conditions.py +157 -0
  132. tabs/solve/plots/energy/plot_battery_pack_conditions.py +164 -0
  133. tabs/solve/plots/energy/plot_battery_ragone_diagram.py +97 -0
  134. tabs/solve/plots/energy/plot_battery_temperature.py +136 -0
  135. tabs/solve/plots/energy/plot_electric_propulsor_efficiencies.py +136 -0
  136. tabs/solve/plots/energy/plot_propulsor_throttles.py +119 -0
  137. tabs/solve/plots/mission/__init__.py +5 -0
  138. tabs/solve/plots/mission/plot_aircraft_velocities.py +145 -0
  139. tabs/solve/plots/mission/plot_flight_conditions.py +236 -0
  140. tabs/solve/plots/mission/plot_flight_trajectory.py +172 -0
  141. tabs/solve/plots/stability/__init__.py +5 -0
  142. tabs/solve/plots/stability/plot_flight_forces_and_moments.py +163 -0
  143. tabs/solve/plots/stability/plot_lateral_stability.py +151 -0
  144. tabs/solve/plots/stability/plot_longitudinal_stability.py +173 -0
  145. tabs/solve/plots/thermal_management/__init__.py +7 -0
  146. tabs/solve/plots/thermal_management/plot_air_cooled_conditions.py +152 -0
  147. tabs/solve/plots/thermal_management/plot_cross_flow_heat_exchanger_conditions.py +185 -0
  148. tabs/solve/plots/thermal_management/plot_reservoir_conditions.py +141 -0
  149. tabs/solve/plots/thermal_management/plot_thermal_management_performance.py +105 -0
  150. tabs/solve/plots/thermal_management/plot_wavy_channel_conditions.py +161 -0
  151. tabs/solve/plots/weights/__init__.py +4 -0
  152. tabs/solve/plots/weights/plot_load_diagram.py +163 -0
  153. tabs/solve/plots/weights/plot_weight_breakdown.py +164 -0
  154. tabs/solve/solve.py +1498 -0
  155. tabs/style_sheet.py +114 -0
  156. tabs/tab_widget.py +14 -0
  157. tabs/visualize_geometry/__init__.py +4 -0
  158. tabs/visualize_geometry/core_3d_viewer.py +253 -0
  159. tabs/visualize_geometry/features/__init__.py +10 -0
  160. tabs/visualize_geometry/features/axes_gizmo.py +173 -0
  161. tabs/visualize_geometry/features/background.py +161 -0
  162. tabs/visualize_geometry/features/blueprint.py +685 -0
  163. tabs/visualize_geometry/features/camera.py +364 -0
  164. tabs/visualize_geometry/features/drag_aircraft.py +219 -0
  165. tabs/visualize_geometry/features/grid.py +194 -0
  166. tabs/visualize_geometry/features/measurement.py +367 -0
  167. tabs/visualize_geometry/features/screenshot.py +91 -0
  168. tabs/visualize_geometry/geometry_helper_functions.py +3 -0
  169. tabs/visualize_geometry/visualize_geometry.py +731 -0
  170. utilities.py +257 -0
tabs/style_sheet.py ADDED
@@ -0,0 +1,114 @@
1
+ # RCAIDE_GUI/tabs/style_sheet.py
2
+
3
+ # PyQt imports
4
+ from PyQt6.QtCore import Qt
5
+ from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QTreeWidget, QTreeWidgetItem, QFrame, QMainWindow, QApplication, QMainWindow, QTabWidget, QFileDialog, QMenu
6
+
7
+ # ------------------------------------------------------------------------------
8
+ # Style Sheet Utilities
9
+ # ------------------------------------------------------------------------------
10
+ def add_line(shape = "HLine", shadow = "Sunken", color = "gray", height = "20"):
11
+ line = QFrame()
12
+ shape_dict = {
13
+ 'NoFrame': QFrame.Shape.NoFrame,
14
+ 'Box': QFrame.Shape.Box,
15
+ 'Panel': QFrame.Shape.Panel,
16
+ 'StyledPanel': QFrame.Shape.StyledPanel,
17
+ 'HLine': QFrame.Shape.HLine,
18
+ 'VLine': QFrame.Shape.VLine
19
+ }
20
+ shadow_dict = {
21
+ 'Plain': QFrame.Shadow.Plain,
22
+ 'Raised': QFrame.Shadow.Raised,
23
+ 'Sunken': QFrame.Shadow.Sunken
24
+ }
25
+ if shape not in shape_dict:
26
+ raise TypeError("Have problem in shape format, shape should be 'NoFrame , Box , Panel , StyledPanel , HLine, VLine'" )
27
+ #shape:NoFrame , Box , Panel , StyledPanel , HLine, VLine
28
+ line.setFrameShape(shape_dict[shape])
29
+ if shadow not in shadow_dict:
30
+ raise TypeError("Have problem in shadow format, shadow should be 'Plain , Raised and Sunken'" )
31
+ #shadow:Plain , Raised and Sunken
32
+ line.setFrameShadow(shadow_dict[shadow])
33
+ try:
34
+ line.setStyleSheet(f"background-color: {color}; height: {height}px;")
35
+ except:
36
+ raise TypeError("Color format:1.Specific color like:red, blue, lightblue. 2.Hex Code like: #FF5733. 3. RGB like: rgb(255, 87, 51)." )
37
+ return line
38
+
39
+ def add_title(text, font_weight="bold", color = "black", font_size="20", Alignment="AlignLeft"):
40
+ title = QLabel(text)
41
+ Alignment_dict={
42
+ "AlignLeft":Qt.AlignmentFlag.AlignLeft,
43
+ "AlignRight": Qt.AlignmentFlag.AlignRight,
44
+ "AlignHCenter":Qt.AlignmentFlag.AlignHCenter,
45
+ "AlignJustify":Qt.AlignmentFlag.AlignJustify,
46
+ "AlignTop":Qt.AlignmentFlag.AlignTop,
47
+ "AlignBottom":Qt.AlignmentFlag.AlignBottom,
48
+ "AlignVCenter":Qt.AlignmentFlag.AlignCenter,
49
+ "AlignBaseline":Qt.AlignmentFlag.AlignVCenter
50
+ }
51
+ if Alignment not in Alignment_dict:
52
+ raise TypeError("Have problem in Alignment format, Alignment should be 'AlignLeft, AlignRight, AlignHCenter, AlignJustify, AlignTop, AlignBottom, AlignVCenter, AlignBaseline'" )
53
+ else:
54
+ title.setAlignment(Alignment_dict[Alignment])
55
+ try:
56
+ title.setStyleSheet(f"font-size: {font_size}px; font-weight: {font_weight}; color: {color};")
57
+ except:
58
+ raise TypeError("font-size format:integer, font-weight format:'bold',Makes the text bold, Color format:1.Specific color like:red, blue, lightblue. 2.Hex Code like: #FF5733. 3. RGB like: rgb(255, 87, 51)." )
59
+ return title
60
+
61
+ def add_subtitle(text, font_weight="bold", color = "black", font_size="15", Alignment="AlignLeft"):
62
+ subtitle = QLabel(text)
63
+ Alignment_dict={
64
+ "AlignLeft":Qt.AlignmentFlag.AlignLeft,
65
+ "AlignRight": Qt.AlignmentFlag.AlignRight,
66
+ "AlignHCenter":Qt.AlignmentFlag.AlignHCenter,
67
+ "AlignJustify":Qt.AlignmentFlag.AlignJustify,
68
+ "AlignTop":Qt.AlignmentFlag.AlignTop,
69
+ "AlignBottom":Qt.AlignmentFlag.AlignBottom,
70
+ "AlignVCenter":Qt.AlignmentFlag.AlignCenter,
71
+ "AlignBaseline":Qt.AlignmentFlag.AlignVCenter
72
+ }
73
+ if Alignment not in Alignment_dict:
74
+ raise TypeError("Have problem in Alignment format, Alignment should be 'AlignLeft, AlignRight, AlignHCenter, AlignJustify, AlignTop, AlignBottom, AlignVCenter, AlignBaseline'" )
75
+ else:
76
+ subtitle.setAlignment(Alignment_dict[Alignment])
77
+ try:
78
+ subtitle.setStyleSheet(f"font-size: {font_size}px; font-weight: {font_weight}; color: {color};")
79
+ except:
80
+ raise TypeError("font-size format:integer, font-weight format:'bold',Makes the text bold, Color format:1.Specific color like:red, blue, lightblue. 2.Hex Code like: #FF5733. 3. RGB like: rgb(255, 87, 51)." )
81
+ return subtitle
82
+
83
+
84
+ #This is a function help build header.It has three inputs. The first input is from QMainWindow(), usually it is "self".
85
+ #The second input is the name for this window. The third input is menu_list. like["File","Documentation"].
86
+ def add_header(window, window_name, menu_list = None):
87
+ window.setWindowTitle(window_name)
88
+ menubar = window.menuBar()
89
+ if menubar is None:
90
+ return
91
+ if menu_list:
92
+ for i in menu_list:
93
+ menubar.addMenu(i)
94
+ return
95
+
96
+ #After use this function,programmer can find exact menu.
97
+ def find_menu_variable(window, target_menu):
98
+ menubar = window.menuBar()
99
+ if menubar is None:
100
+ print("No menu bar found.")
101
+ return
102
+ menus = [child for child in menubar.children() if isinstance(child, QMenu)]
103
+ for menu in menus:
104
+ if menu.title() == target_menu:
105
+ return menu
106
+
107
+ if __name__ == '__main__':
108
+ app = QApplication([])
109
+ window = QMainWindow()
110
+ add_header(window, "My Application", menu_list=["File", "Edit", "View"])
111
+ file_mune = find_menu_variable(window,"File")
112
+ file_mune.addAction("Quit")
113
+ window.show()
114
+ app.exec()
tabs/tab_widget.py ADDED
@@ -0,0 +1,14 @@
1
+ # RCAIDE_GUI/tabs/tab_widget.py
2
+
3
+ # PyQt imports
4
+ from PyQt6.QtWidgets import QWidget
5
+
6
+ # ------------------------------------------------------------------------------
7
+ # Tab Widget
8
+ # ------------------------------------------------------------------------------
9
+ class TabWidget(QWidget):
10
+ def update_layout(self):
11
+ pass
12
+
13
+ def load_from_values(self):
14
+ pass
@@ -0,0 +1,4 @@
1
+ # RCAIDE_GUI/tabs/visualize_geometry/__init__.py
2
+ from .core_3d_viewer import *
3
+ from .visualize_geometry import *
4
+ from . import features
@@ -0,0 +1,253 @@
1
+ # RCAIDE_GUI/tabs/visualize_geometry/core_3d_viewer.py
2
+
3
+ # RCAIDE imports
4
+ import RCAIDE
5
+ from RCAIDE.Framework.Core import Units
6
+ from RCAIDE.Library.Components.Airfoils import Airfoil
7
+ from RCAIDE.Library.Plots.Geometry.generate_3d_wing_points import *
8
+ from RCAIDE.Library.Plots.Geometry.generate_3d_fuselage_points import *
9
+ from RCAIDE.Library.Plots.Geometry.plot_3d_rotor import generate_3d_blade_points
10
+ from RCAIDE.Library.Plots.Geometry.generate_3d_nacelle_points import *
11
+ from RCAIDE.Library.Methods.Geometry.Planform import fuselage_planform, wing_planform
12
+
13
+ # RCAIDE-GUI imports
14
+ from tabs import TabWidget
15
+ from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
16
+ from tabs.visualize_geometry import geometry_helper_functions
17
+
18
+ # PyQt imports
19
+ from PyQt6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QTreeWidget, QPushButton, QTreeWidgetItem, QHeaderView, QLabel, QToolBar, QColorDialog, QSpacerItem, QSizePolicy, QFrame, QLineEdit
20
+ from PyQt6.QtCore import Qt
21
+ from PyQt6.QtGui import QIcon
22
+
23
+ # python imports
24
+ import matplotlib.colors as mcolors
25
+ import vtk
26
+ import rcaide_io
27
+ import os
28
+ from copy import deepcopy
29
+
30
+ # ------------------------------------------------------------------------------
31
+ # make_object
32
+ # ------------------------------------------------------------------------------
33
+ def make_object(renderer, actor_group, GEOM, rgb_color, opacity):
34
+
35
+ actor = geometry_helper_functions.generate_vtk_object(GEOM.PTS)
36
+
37
+ # Set color of fuselage
38
+ mapper = actor.GetMapper()
39
+ mapper.ScalarVisibilityOff()
40
+ prop = actor.GetProperty()
41
+ prop.SetColor(rgb_color[0] * 1.2, rgb_color[1] * 1.2, rgb_color[2] * 1.2) # slightly brighter
42
+ prop.SetDiffuse(0.8)
43
+ prop.SetAmbient(0.4) # adds base light even in dark areas
44
+ prop.SetSpecular(0.3) # gives a soft highlight
45
+ prop.SetSpecularPower(20)
46
+ prop.SetOpacity(opacity)
47
+ renderer.AddActor(actor)
48
+ actor_group.append(actor)
49
+ return
50
+
51
+ def make_actuator_disc(renderer, inner_radius, outer_radius, origin, rot_x,rot_y,rot_z, rgb_color, opacity):
52
+
53
+ disk_source = vtk.vtkDiskSource()
54
+ disk_source.SetInnerRadius(inner_radius)
55
+ disk_source.SetOuterRadius(outer_radius)
56
+ disk_source.SetRadialResolution(50)
57
+ disk_source.SetCircumferentialResolution(50)
58
+
59
+ transform = vtk.vtkTransform()
60
+ transform.RotateX(rot_x/Units.degrees)
61
+ transform.RotateY(rot_y/Units.degrees)
62
+ transform.RotateZ(rot_z/Units.degrees)
63
+ transformFilter = vtk.vtkTransformPolyDataFilter()
64
+ transformFilter.SetTransform(transform)
65
+ transformFilter.SetInputConnection(disk_source.GetOutputPort())
66
+
67
+ mapper = vtk.vtkPolyDataMapper()
68
+ mapper.SetInputConnection(transformFilter.GetOutputPort())
69
+
70
+ actor = vtk.vtkActor()
71
+ actor.SetMapper(mapper)
72
+ actor.GetProperty().SetColor(rgb_color[0], rgb_color[1], rgb_color[2])
73
+ actor.GetProperty().SetDiffuse(1.0)
74
+ actor.GetProperty().SetSpecular(0.0)
75
+ actor.GetProperty().SetOpacity(opacity)
76
+ actor.SetPosition( origin[0][0], origin[0][1], origin[0][2])
77
+ renderer.AddActor(actor)
78
+ return
79
+
80
+ class Core3DViewer(QWidget):
81
+ def __init__(self, parent=None):
82
+ super().__init__(parent)
83
+
84
+ # Setup Layout
85
+ layout = QVBoxLayout(self)
86
+ layout.setContentsMargins(0, 0, 0, 0)
87
+
88
+ # Setup VTK Window
89
+ self.vtkWidget = QVTKRenderWindowInteractor(self)
90
+ layout.addWidget(self.vtkWidget)
91
+
92
+ self.renderer = vtk.vtkRenderer()
93
+ self.vtkWidget.GetRenderWindow().AddRenderer(self.renderer)
94
+ self.render_window_interactor = self.vtkWidget.GetRenderWindow().GetInteractor()
95
+ style = vtk.vtkInteractorStyleTrackballCamera()
96
+ self.render_window_interactor.SetInteractorStyle(style)
97
+ self.wing_actors = []
98
+ self.fuselage_actors = []
99
+ self.boom_actors = []
100
+ self.nacelle_actors = []
101
+ self.rotor_actors = []
102
+ self.fuel_tank_actors = []
103
+ self.part_actors = [] # General list if needed
104
+
105
+ self.render_window_interactor.Initialize()
106
+
107
+ def clear_scene(self):
108
+ """Wipes the 3D canvas clean before drawing a new airplane."""
109
+ self.renderer.RemoveAllViewProps()
110
+ self.wing_actors.clear()
111
+ self.fuselage_actors.clear()
112
+ self.boom_actors.clear()
113
+ self.nacelle_actors.clear()
114
+ self.rotor_actors.clear()
115
+ self.fuel_tank_actors.clear()
116
+ self.part_actors.clear()
117
+
118
+ def run_solve(self):
119
+ """Calculates and draws the geometry based on rcaide_io.vehicle"""
120
+
121
+ self.clear_scene()
122
+
123
+ wing_color = 'grey'
124
+ fuselage_color = 'grey'
125
+ nacelle_color = 'grey'
126
+ boom_color = 'grey'
127
+ rotor_color = 'black'
128
+ wing_opacity = 0.5
129
+ fuselage_opacity = 0.5
130
+ nacelle_opacity = 1.0
131
+ rotor_opacity = 0.6
132
+ number_of_airfoil_points = 101
133
+ tessellation = 96
134
+ boom_opacity = 1.0
135
+ camera_eye_x = -1
136
+ camera_eye_y = -1
137
+ camera_eye_z = 0.35
138
+
139
+ wing_rgb_color = mcolors.to_rgb(wing_color)
140
+ fuselage_rgb_color = mcolors.to_rgb(fuselage_color)
141
+ nacelle_rgb_color = mcolors.to_rgb(nacelle_color)
142
+ rotor_rgb_color = mcolors.to_rgb(rotor_color)
143
+ boom_rgb_color = mcolors.to_rgb(boom_color)
144
+
145
+ if not rcaide_io.vehicle:
146
+ return # Don't try to draw if there's no vehicle
147
+
148
+ geometry = deepcopy(rcaide_io.vehicle)
149
+
150
+
151
+ for wing in geometry.wings:
152
+ try: wing_planform(wing)
153
+ except: pass # Bypass if not fully defined yet in GUI
154
+
155
+ for fuselage in geometry.fuselages:
156
+ try:
157
+ fuselage_planform(fuselage)
158
+ except: pass
159
+
160
+ for wing in geometry.wings:
161
+ n_segments = len(wing.segments)
162
+ dim = n_segments if n_segments > 0 else 2
163
+ try:
164
+ GEOM = generate_3d_wing_points(wing, number_of_airfoil_points, dim)
165
+ make_object(self.renderer, self.wing_actors, GEOM, wing_rgb_color, wing_opacity)
166
+ if wing.yz_plane_symmetric:
167
+ GEOM.PTS[:, :, 0] = -GEOM.PTS[:, :, 0]
168
+ make_object(self.renderer, self.wing_actors,GEOM,wing_rgb_color,wing_opacity)
169
+ if wing.xz_plane_symmetric:
170
+ GEOM.PTS[:, :, 1] = -GEOM.PTS[:, :, 1]
171
+ make_object(self.renderer,self.wing_actors, GEOM,wing_rgb_color,wing_opacity)
172
+ if wing.xy_plane_symmetric:
173
+ GEOM.PTS[:, :, 2] = -GEOM.PTS[:, :, 2]
174
+ make_object(self.renderer,self.wing_actors, GEOM,wing_rgb_color,wing_opacity)
175
+ except Exception as e:
176
+ print(f"Skipping a wing due to incomplete data: {e}")
177
+
178
+
179
+ for fuselage in geometry.fuselages:
180
+ try:
181
+ GEOM = generate_3d_fuselage_points(fuselage, tessellation)
182
+ make_object(self.renderer, self.fuselage_actors,GEOM, fuselage_rgb_color,fuselage_opacity)
183
+ except: pass
184
+
185
+ for boom in geometry.booms:
186
+ try:
187
+ GEOM = generate_3d_fuselage_points(boom, tessellation)
188
+ make_object(self.renderer, self.boom_actors, GEOM, boom_rgb_color,boom_opacity)
189
+ except: pass
190
+
191
+ for nacelle in geometry.nacelles:
192
+ try:
193
+ GEOM = generate_3d_BOR_nacelle_points(nacelle, tessellation=tessellation, number_of_airfoil_points=number_of_airfoil_points)
194
+ make_object(self.renderer, self.nacelle_actors, GEOM, nacelle_rgb_color, nacelle_opacity)
195
+ except: pass
196
+
197
+ for network in geometry.networks:
198
+ for propulsor in network.propulsors:
199
+ if 'nacelle' in propulsor and propulsor.nacelle != None:
200
+ try:
201
+ if type(propulsor.nacelle) == RCAIDE.Library.Components.Nacelles.Stack_Nacelle:
202
+ GEOM = generate_3d_stack_nacelle_points(propulsor.nacelle,tessellation = tessellation,number_of_airfoil_points = number_of_airfoil_points)
203
+ elif type(propulsor.nacelle) == RCAIDE.Library.Components.Nacelles.Body_of_Revolution_Nacelle:
204
+ GEOM = generate_3d_BOR_nacelle_points(propulsor.nacelle,tessellation = tessellation,number_of_airfoil_points = number_of_airfoil_points)
205
+ else:
206
+ GEOM= generate_3d_basic_nacelle_points(propulsor.nacelle,tessellation = tessellation,number_of_airfoil_points = number_of_airfoil_points)
207
+ make_object(self.renderer,self.nacelle_actors, GEOM, nacelle_rgb_color,nacelle_opacity)
208
+ except: pass
209
+
210
+ if 'rotor' in propulsor:
211
+ try:
212
+ rot = propulsor.rotor
213
+ rot_x = rot.orientation_euler_angles[0]
214
+ rot_y = rot.orientation_euler_angles[1]
215
+ rot_z = rot.orientation_euler_angles[2]
216
+ num_B = int(rot.number_of_blades)
217
+ if rot.radius_distribution is None:
218
+ make_actuator_disc(self.renderer, rot.hub_radius, rot.tip_radius, rot.origin, rot_x,rot_y,rot_z, rotor_rgb_color,rotor_opacity)
219
+ else:
220
+ dim = len(rot.radius_distribution)
221
+ for i in range(num_B):
222
+ GEOM = generate_3d_blade_points(rot,number_of_airfoil_points,dim,i)
223
+ make_object(self.renderer,self.rotor_actors, GEOM, rotor_rgb_color,rotor_opacity)
224
+ except: pass
225
+
226
+ if 'propeller' in propulsor:
227
+ try:
228
+ prop = propulsor.propeller
229
+ rot_x = prop.orientation_euler_angles[0]
230
+ rot_y = np.pi / 2 + prop.orientation_euler_angles[1]
231
+ rot_z = prop.orientation_euler_angles[2]
232
+ num_B = int(prop.number_of_blades)
233
+ if prop.radius_distribution is None:
234
+ make_actuator_disc(self.renderer, prop.hub_radius, prop.tip_radius, prop.origin, rot_x,rot_y,rot_z,rotor_rgb_color,rotor_opacity)
235
+ else:
236
+ dim = len(prop.radius_distribution)
237
+ for i in range(num_B):
238
+ GEOM = generate_3d_blade_points(prop,number_of_airfoil_points,dim,i)
239
+ make_object(self.renderer,self.rotor_actors, GEOM, rotor_rgb_color,rotor_opacity)
240
+ except: pass
241
+
242
+
243
+ # Set camera and background
244
+ camera = vtk.vtkCamera()
245
+ camera.SetPosition(camera_eye_x, camera_eye_y, camera_eye_z)
246
+ camera.SetFocalPoint(0, 0, 0)
247
+ camera.SetViewUp(0, 0, 1)
248
+
249
+ self.renderer.SetActiveCamera(camera)
250
+ self.renderer.ResetCamera()
251
+ self.renderer.SetBackground(0.1, 0.1, 0.15) # A nice dark theme background
252
+
253
+ self.vtkWidget.GetRenderWindow().Render()
@@ -0,0 +1,10 @@
1
+ # RCAIDE_GUI/tabs/visualize_geometry/features/__init__.py
2
+
3
+ from .axes_gizmo import *
4
+ from .background import *
5
+ from .blueprint import *
6
+ from .camera import *
7
+ from .drag_aircraft import *
8
+ from .grid import *
9
+ from .measurement import *
10
+ from .screenshot import *
@@ -0,0 +1,173 @@
1
+ # RCAIDE_GUI/tabs/visualize_geometry/features/axes_gizmo.py
2
+
3
+ # Created: M Clarke, LEADS, 2024
4
+ # RCAIDE-GUI imports
5
+ # RCAIDE-GUI Imports
6
+ from tabs.visualize_geometry.visualize_geometry import VisualizeGeometryWidget
7
+
8
+ # Python imports
9
+ import vtkmodules.all as vtk
10
+ # ------------------------------------------------------------------------------
11
+ # Gizmo functions
12
+ # ------------------------------------------------------------------------------
13
+ def _bind_gizmo_renderer(widget, renderer):
14
+ # VTK uses different method names depending on the version
15
+ try:
16
+ widget.SetCurrentRenderer(renderer)
17
+ except Exception:
18
+ try:
19
+ widget.SetDefaultRenderer(renderer)
20
+ except Exception:
21
+ pass
22
+
23
+ def add_orientation_gizmo(self):
24
+ # Need renderer + render window + interactor
25
+ r = getattr(self, "renderer", None)
26
+ if r is None:
27
+ return
28
+ try:
29
+ rw = self.vtkWidget.GetRenderWindow()
30
+ except Exception:
31
+ return
32
+ if rw is None:
33
+ return
34
+ iren = rw.GetInteractor()
35
+ if iren is None:
36
+ return
37
+
38
+ # Reuse existing widget so we don’t create duplicates
39
+ widget = getattr(self, "_axes_widget", None) or getattr(iren, "_axes_widget", None)
40
+
41
+ if widget is None:
42
+ # Axes + label styling
43
+ axes = vtk.vtkAxesActor()
44
+ for cap in (
45
+ axes.GetXAxisCaptionActor2D(),
46
+ axes.GetYAxisCaptionActor2D(),
47
+ axes.GetZAxisCaptionActor2D(),
48
+ ):
49
+ tp = cap.GetTextActor().GetTextProperty()
50
+ tp.SetFontSize(14)
51
+ tp.SetColor(1.0, 1.0, 1.0)
52
+
53
+ # Light wireframe border around the gizmo
54
+ border = vtk.vtkCubeSource()
55
+ border.SetXLength(1.2)
56
+ border.SetYLength(1.2)
57
+ border.SetZLength(1.2)
58
+
59
+ border_mapper = vtk.vtkPolyDataMapper()
60
+ border_mapper.SetInputConnection(border.GetOutputPort())
61
+
62
+ border_actor = vtk.vtkActor()
63
+ border_actor.SetMapper(border_mapper)
64
+ p = border_actor.GetProperty()
65
+ p.SetColor(0.8, 0.8, 0.8)
66
+ p.SetOpacity(0.1)
67
+ p.SetRepresentationToWireframe()
68
+ p.LightingOff()
69
+
70
+ # Combine axes + border into one marker
71
+ assembly = vtk.vtkPropAssembly()
72
+ assembly.AddPart(border_actor)
73
+ assembly.AddPart(axes)
74
+
75
+ # Small overlay widget in the corner
76
+ widget = vtk.vtkOrientationMarkerWidget()
77
+ widget.SetOrientationMarker(assembly)
78
+ widget.SetViewport(0.0, 0.0, 0.18, 0.28)
79
+ widget.SetInteractor(iren)
80
+
81
+ # Attach to the current renderer (important if renderer was rebuilt)
82
+ _bind_gizmo_renderer(widget, r)
83
+
84
+ # Some VTK builds need this before enabling the widget
85
+ try:
86
+ if not iren.GetInitialized():
87
+ iren.Initialize()
88
+ except Exception:
89
+ pass
90
+
91
+ # Keep it visible but not clickable
92
+ widget.EnabledOn()
93
+ widget.InteractiveOff()
94
+
95
+ # Store in both places so it survives tab switches
96
+ self._axes_widget = widget
97
+ iren._axes_widget = widget
98
+
99
+ # Redraw now
100
+ try:
101
+ rw.Render()
102
+ except Exception:
103
+ pass
104
+
105
+ def _rebind_axes_gizmo_on_show(self):
106
+ # Tab switching can recreate the renderer/interactor, so we reattach the gizmo
107
+ r = getattr(self, "renderer", None)
108
+ if r is None:
109
+ return
110
+ try:
111
+ rw = self.vtkWidget.GetRenderWindow()
112
+ except Exception:
113
+ return
114
+ if rw is None:
115
+ return
116
+ iren = rw.GetInteractor()
117
+ if iren is None:
118
+ return
119
+
120
+ # Find existing gizmo widget
121
+ w = getattr(self, "_axes_widget", None) or getattr(iren, "_axes_widget", None)
122
+ if w is None:
123
+ return
124
+
125
+ # Reattach to the current interactor/renderer
126
+ try:
127
+ w.SetInteractor(iren)
128
+ except Exception:
129
+ pass
130
+ _bind_gizmo_renderer(w, r)
131
+
132
+ # Turn it back on and redraw
133
+ try:
134
+ w.EnabledOn()
135
+ w.InteractiveOff()
136
+ rw.Render()
137
+ except Exception:
138
+ pass
139
+
140
+ # Activate Patch
141
+ def _patch_run_solve_for_axes_gizmo():
142
+ # After run_solve builds the scene, make sure the gizmo exists
143
+ if getattr(VisualizeGeometryWidget, "_axes_gizmo_patched", False):
144
+ return
145
+ base = VisualizeGeometryWidget.run_solve
146
+
147
+ def wrapped(self, *args, **kwargs):
148
+ base(self, *args, **kwargs)
149
+ add_orientation_gizmo(self)
150
+
151
+ VisualizeGeometryWidget.run_solve = wrapped
152
+ VisualizeGeometryWidget._axes_gizmo_patched = True
153
+
154
+ # Activate Patch
155
+ def _patch_showEvent_for_axes_gizmo_rebind():
156
+ # When the widget becomes visible again, rebind the gizmo
157
+ if getattr(VisualizeGeometryWidget, "_axes_showevent_patched", False):
158
+ return
159
+ old = getattr(VisualizeGeometryWidget, "showEvent", None)
160
+
161
+ def wrapped(self, event):
162
+ if old is not None:
163
+ try:
164
+ old(self, event)
165
+ except Exception:
166
+ pass
167
+ _rebind_axes_gizmo_on_show(self)
168
+
169
+ VisualizeGeometryWidget.showEvent = wrapped
170
+ VisualizeGeometryWidget._axes_showevent_patched = True
171
+
172
+ _patch_run_solve_for_axes_gizmo()
173
+ _patch_showEvent_for_axes_gizmo_rebind()