Skip to content

Commit

Permalink
Added buttons for each type of geometry and constraint
Browse files Browse the repository at this point in the history
  • Loading branch information
mlauer154 committed Feb 5, 2024
1 parent 04d8c93 commit fe71433
Show file tree
Hide file tree
Showing 26 changed files with 159 additions and 14 deletions.
6 changes: 4 additions & 2 deletions docs/source/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ Planned feature additions
- Add variable constraint GUI object positioning (add keyword arguments that are stored in the `.jmea` files)
- Add error handling for over-constrained/Jax shape/no solution in GCS
- Add an undo/redo framework to the GUI
- Graphical highlighting of constraint parameters and constraints
- Graphical highlighting of constraint parameters and constraints - make hoverEnter detection on constraint canvas items
- Tie parameter hover to associated constraint hover events
- Add unit selection ComboBox
- Add renaming to parameters
- Make the "Analysis" tab focused by default after an aerodynamics analysis (possibly implement a user option to
override this behavior)
- Write the XFOIL/MSES analysis code using the same `CPUBoundProcess` architecture used by optimization
Expand All @@ -40,6 +40,8 @@ Bug fixes
- Remove wave/viscous drag from XFOIL drag history plots (optimization)
- Fix symmetry constraint having switched target/tool points (perhaps automatically create the mirror point?)
- Toggle grid affects all the dock widgets
- Apply theme to status bar widgets immediately on theme change
- Correct dimensions having default colors before switching themes

Testing
-------
Expand Down
8 changes: 4 additions & 4 deletions pymead/core/gcs2.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,10 @@ def solve(self, source: GeoCon):
d_angle = new_angle - old_angle
rotation_point = source.p2

if edge_data_p21 and edge_data_p21["angle"] is source:
if edge_data_p21 and "angle" in edge_data_p21 and edge_data_p21["angle"] is source:
start = source.p1
# d_angle *= -1
elif edge_data_p23 and edge_data_p23["angle"] is source:
elif edge_data_p23 and "angle" in edge_data_p23 and edge_data_p23["angle"] is source:
start = source.p3
d_angle *= -1
else:
Expand Down Expand Up @@ -279,9 +279,9 @@ def solve(self, source: GeoCon):
d_angle = new_angle - old_angle
rotation_point = source.vertex

if edge_data_p21 and edge_data_p21["angle"] is source:
if edge_data_p21 and "angle" in edge_data_p21 and edge_data_p21["angle"] is source:
pass
elif edge_data_p23 and edge_data_p23["angle"] is source:
elif edge_data_p23 and "angle" in edge_data_p23 and edge_data_p23["angle"] is source:
pass
d_angle *= -1
else:
Expand Down
64 changes: 60 additions & 4 deletions pymead/gui/airfoil_canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import typing
import sys
from copy import deepcopy
from functools import partial

import numpy as np
import pyqtgraph as pg
Expand Down Expand Up @@ -206,7 +207,7 @@ def addPymeadCanvasItem(self, pymead_obj: PymeadObj):
pymead_obj.canvas_item.sigPolyExit.connect(self.airfoil_exited)

@staticmethod
def runSelectionEventLoop(drawing_object: str, starting_message: str):
def runSelectionEventLoop(drawing_object: str, starting_message: str, enter_callback: typing.Callable = None):
drawing_object = drawing_object
starting_message = starting_message

Expand All @@ -215,7 +216,11 @@ def wrapped(self, *args, **kwargs):
self.drawing_object = drawing_object
self.sigStatusBarUpdate.emit(starting_message, 0)
loop = QEventLoop()
self.sigEnterPressed.connect(loop.quit)
connection = None
if enter_callback:
connection = self.sigEnterPressed.connect(partial(enter_callback, self))
else:
self.sigEnterPressed.connect(loop.quit)
self.sigEscapePressed.connect(loop.quit)
loop.exec()
# if len(self.geo_col.selected_objects["points"]) > 0:
Expand All @@ -226,6 +231,8 @@ def wrapped(self, *args, **kwargs):
# self.clearSelectedObjects()
self.drawing_object = None
self.sigStatusBarUpdate.emit("", 0)
if enter_callback:
self.sigEnterPressed.disconnect(connection)
return wrapped
return decorator

Expand All @@ -234,6 +241,18 @@ def wrapped(self, *args, **kwargs):
def drawPoints(self):
pass

def drawBezierNoEvent(self):
if len(self.geo_col.selected_objects["points"]) < 2:
msg = f"Choose at least 2 points to define a curve"
self.sigStatusBarUpdate.emit(msg, 2000)
return

point_sequence = PointSequence([pt for pt in self.geo_col.selected_objects["points"]])
self.geo_col.add_bezier(point_sequence=point_sequence)

self.clearSelectedObjects()
self.sigStatusBarUpdate.emit("Select the first Bezier control point of the next curve", 0)

@runSelectionEventLoop(drawing_object="Bezier", starting_message="Select the first Bezier control point")
def drawBezier(self):
if len(self.geo_col.selected_objects["points"]) < 2:
Expand All @@ -244,6 +263,14 @@ def drawBezier(self):
point_sequence = PointSequence([pt for pt in self.geo_col.selected_objects["points"]])
self.geo_col.add_bezier(point_sequence=point_sequence)

@runSelectionEventLoop(drawing_object="Beziers", starting_message="Select the first Bezier control point",
enter_callback=drawBezierNoEvent)
def drawBeziers(self):
if len(self.geo_col.selected_objects["points"]) < 2:
msg = f"Choose at least 2 points to define a curve"
self.sigStatusBarUpdate.emit(msg, 2000)
return

@runSelectionEventLoop(drawing_object="LineSegment", starting_message="Select the first line endpoint")
def drawLineSegment(self):
if len(self.geo_col.selected_objects["points"]) < 2:
Expand All @@ -254,15 +281,20 @@ def drawLineSegment(self):
point_sequence = PointSequence([pt for pt in self.geo_col.selected_objects["points"]])
self.geo_col.add_line(point_sequence=point_sequence)

@runSelectionEventLoop(drawing_object="LineSegments", starting_message="Select the first line endpoint")
def drawLines(self):
self.drawLineSegment()
if len(self.geo_col.selected_objects["points"]) < 2:
msg = f"Choose at least 2 points to define a curve"
self.sigStatusBarUpdate.emit(msg, 2000)
return

@runSelectionEventLoop(drawing_object="Airfoil", starting_message="Select the leading edge point")
def generateAirfoil(self):
if len(self.geo_col.selected_objects["points"]) not in [2, 4]:
self.sigStatusBarUpdate.emit(
"Choose either 2 points (sharp trailing edge) or 4 points (blunt trailing edge)"
" to generate an airfoil", 4000)
return

le = self.geo_col.selected_objects["points"][0]
te = self.geo_col.selected_objects["points"][1]
Expand Down Expand Up @@ -291,6 +323,7 @@ def addLengthDimension(self):
self.sigStatusBarUpdate.emit("Choose either 2 points (no length parameter) or 3 points "
"(specified length parameter)"
" to add a length dimension", 4000)
return

tool_point = self.geo_col.selected_objects["points"][0]
target_point = self.geo_col.selected_objects["points"][1]
Expand All @@ -302,6 +335,7 @@ def addLengthDimension(self):
def addDistanceConstraint(self):
if len(self.geo_col.selected_objects["points"]) != 2:
self.sigStatusBarUpdate.emit("Choose exactly two points to define a distance constraint", 4000)
return

# p1 = self.geo_col.selected_objects["points"][0]
# p2 = self.geo_col.selected_objects["points"][1]
Expand All @@ -321,6 +355,7 @@ def addAngleDimension(self):
self.sigStatusBarUpdate.emit("Choose either 2 points (no angle parameter) or 3 points "
"(specified angle parameter)"
" to add an angle dimension", 4000)
return
tool_point = self.geo_col.selected_objects["points"][0]
target_point = self.geo_col.selected_objects["points"][1]
angle_param = None if len(self.geo_col.selected_objects["points"]) <= 2 else self.geo_col.selected_objects["points"][2]
Expand Down Expand Up @@ -424,14 +459,35 @@ def pointClicked(self, scatter_item, spot, ev, point_item):
self.geo_col.select_object(point_item.point)
n_ctrl_pts = len(self.geo_col.selected_objects["points"])
degree = n_ctrl_pts - 1
msg = (f"Added control point to curve. Number of control points: {len(self.geo_col.selected_objects['points'])} "
msg = (f"Added control point to curve. Number of control points: "
f"{len(self.geo_col.selected_objects['points'])} "
f"(degree: {degree}). Press 'Enter' to generate the curve.")
self.sigStatusBarUpdate.emit(msg, 0)
elif self.drawing_object == "Beziers":
self.geo_col.select_object(point_item.point)
n_ctrl_pts = len(self.geo_col.selected_objects["points"])
degree = n_ctrl_pts - 1
msg = (f"Added control point to curve. Number of control points: "
f"{len(self.geo_col.selected_objects['points'])} "
f"(degree: {degree}). Press 'Enter' to generate the curve.")
self.sigStatusBarUpdate.emit(msg, 0)
elif self.drawing_object == "LineSegment":
if len(self.geo_col.selected_objects["points"]) < 2:
self.geo_col.select_object(point_item.point)
if len(self.geo_col.selected_objects["points"]) == 1:
self.sigStatusBarUpdate.emit("Next, choose the line's endpoint", 0)
if len(self.geo_col.selected_objects["points"]) == 2:
self.sigEnterPressed.emit() # Complete the line after selecting the second point
elif self.drawing_object == "LineSegments":
if len(self.geo_col.selected_objects["points"]) < 2:
self.geo_col.select_object(point_item.point)
if len(self.geo_col.selected_objects["points"]) == 1:
self.sigStatusBarUpdate.emit("Next, choose the line's endpoint", 0)
if len(self.geo_col.selected_objects["points"]) == 2:
point_sequence = PointSequence([pt for pt in self.geo_col.selected_objects["points"]])
self.geo_col.add_line(point_sequence=point_sequence)
self.clearSelectedObjects()
self.sigStatusBarUpdate.emit("Choose the next line's start point", 0)
elif self.adding_point_to_curve is not None:
if len(self.geo_col.selected_objects["points"]) < 2:
self.geo_col.select_object(point_item.point)
Expand Down
8 changes: 8 additions & 0 deletions pymead/gui/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,14 @@ def set_theme(self, theme_name: str):
for cnstr in self.geo_col.container()["geocon"].values():
cnstr.canvas_item.setStyle(theme)

for button_name, button_setting in self.main_icon_toolbar.button_settings.items():
icon_name = button_setting["icon"]
if "dark" not in icon_name:
continue

image_path = os.path.join(ICON_DIR, icon_name.replace("dark", theme_name))
self.main_icon_toolbar.buttons[button_name]["button"].setIcon(QIcon(image_path))

def set_color_bar_style(self, new_values: dict = None):
if self.cbar is None:
return
Expand Down
46 changes: 44 additions & 2 deletions pymead/gui/gui_settings/buttons.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,59 @@
"function": "change_background_color_button_toggled"
},
"draw-point": {
"icon": "point.svg",
"icon": "point_dark.svg",
"status_tip": "Draw points",
"checkable": false,
"function": "on_draw_points_pressed"
},
"draw-line": {
"icon": "line.svg",
"icon": "line_dark.svg",
"status_tip": "Draw lines",
"checkable": false,
"function": "on_draw_lines_pressed"
},
"draw-bezier": {
"icon": "bezier_dark.svg",
"status_tip": "Draw Bezier curves",
"checkable": false,
"function": "on_draw_bezier_pressed"
},
"add-distance-constraint": {
"icon": "distance_constraint_dark.svg",
"status_tip": "Add a distance constraint",
"checkable": false,
"function": "on_add_distance_constraint_pressed"
},
"add-rel-angle-constraint": {
"icon": "rel_angle_constraint_dark.svg",
"status_tip": "Add a relative angle constraint",
"checkable": false,
"function": "on_add_rel_angle_constraint_pressed"
},
"add-perp-constraint": {
"icon": "perp3_constraint_dark.svg",
"status_tip": "Add a perpendicular constraint",
"checkable": false,
"function": "on_add_perp_constraint_pressed"
},
"add-anti-parallel-constraint": {
"icon": "anti_parallel_constraint_dark.svg",
"status_tip": "Add an anti-parallel constraint",
"checkable": false,
"function": "on_add_anti_parallel_constraint_pressed"
},
"add-symmetry-constraint": {
"icon": "symmetry_constraint_dark.svg",
"status_tip": "Add a symmetry constraint",
"checkable": false,
"function": "on_add_symmetry_constraint_pressed"
},
"add-roc-constraint": {
"icon": "roc_constraint_dark.svg",
"status_tip": "Add a radius of curvature constraint",
"checkable": false,
"function": "on_add_roc_constraint_pressed"
},
"stop-optimization": {
"icon": "stop.png",
"status_tip": "Terminate optimization",
Expand Down
21 changes: 21 additions & 0 deletions pymead/gui/main_icon_toolbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,26 @@ def on_draw_points_pressed(self):
def on_draw_lines_pressed(self):
self.parent.airfoil_canvas.drawLines()

def on_draw_bezier_pressed(self):
self.parent.airfoil_canvas.drawBeziers()

def on_add_distance_constraint_pressed(self):
self.parent.airfoil_canvas.addDistanceConstraint()

def on_add_rel_angle_constraint_pressed(self):
self.parent.airfoil_canvas.addRelAngle3Constraint()

def on_add_perp_constraint_pressed(self):
self.parent.airfoil_canvas.addPerp3Constraint()

def on_add_anti_parallel_constraint_pressed(self):
self.parent.airfoil_canvas.addAntiParallel3Constraint()

def on_add_symmetry_constraint_pressed(self):
self.parent.airfoil_canvas.addSymmetryConstraint()

def on_add_roc_constraint_pressed(self):
self.parent.airfoil_canvas.addROCurvatureConstraint()

def on_help_button_pressed(self):
self.parent.show_help()
1 change: 1 addition & 0 deletions pymead/icons/anti_parallel_constraint_dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit fe71433

Please sign in to comment.