diff --git a/Gui.py b/Gui.py index 941b27f..3ac61e0 100644 --- a/Gui.py +++ b/Gui.py @@ -419,6 +419,7 @@ class Ui_fluencyCAD(object): self.pb_con_perp = QPushButton(self.groupBox_3) self.pb_con_perp.setObjectName(u"pb_con_perp") self.pb_con_perp.setCheckable(True) + self.pb_con_perp.setAutoExclusive(False) self.gridLayout_4.addWidget(self.pb_con_perp, 1, 1, 1, 1) @@ -454,6 +455,7 @@ class Ui_fluencyCAD(object): self.pb_con_mid = QPushButton(self.groupBox_3) self.pb_con_mid.setObjectName(u"pb_con_mid") self.pb_con_mid.setCheckable(True) + self.pb_con_mid.setAutoExclusive(False) self.gridLayout_4.addWidget(self.pb_con_mid, 1, 0, 1, 1) @@ -469,11 +471,11 @@ class Ui_fluencyCAD(object): self.tabWidget.setSizePolicy(sizePolicy5) self.tabWidget.setMaximumSize(QSize(200, 16777215)) self.tabWidget.setTabPosition(QTabWidget.South) - self.widget = QWidget() - self.widget.setObjectName(u"widget") - self.verticalLayout_3 = QVBoxLayout(self.widget) + self.snaps = QWidget() + self.snaps.setObjectName(u"snaps") + self.verticalLayout_3 = QVBoxLayout(self.snaps) self.verticalLayout_3.setObjectName(u"verticalLayout_3") - self.groupBox_5 = QGroupBox(self.widget) + self.groupBox_5 = QGroupBox(self.snaps) self.groupBox_5.setObjectName(u"groupBox_5") sizePolicy6 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) sizePolicy6.setHorizontalStretch(0) @@ -491,6 +493,7 @@ class Ui_fluencyCAD(object): self.pb_snap_vert = QPushButton(self.groupBox_5) self.pb_snap_vert.setObjectName(u"pb_snap_vert") self.pb_snap_vert.setCheckable(True) + self.pb_snap_vert.setAutoExclusive(False) self.gridLayout_11.addWidget(self.pb_snap_vert, 2, 1, 1, 1) @@ -516,12 +519,14 @@ class Ui_fluencyCAD(object): self.pushButton_7 = QPushButton(self.groupBox_5) self.pushButton_7.setObjectName(u"pushButton_7") self.pushButton_7.setCheckable(True) + self.pushButton_7.setAutoExclusive(False) self.gridLayout_11.addWidget(self.pushButton_7, 3, 0, 1, 1) self.pb_snap_horiz = QPushButton(self.groupBox_5) self.pb_snap_horiz.setObjectName(u"pb_snap_horiz") self.pb_snap_horiz.setCheckable(True) + self.pb_snap_horiz.setAutoExclusive(False) self.gridLayout_11.addWidget(self.pb_snap_horiz, 2, 0, 1, 1) @@ -534,28 +539,32 @@ class Ui_fluencyCAD(object): self.pushButton_8 = QPushButton(self.groupBox_5) self.pushButton_8.setObjectName(u"pushButton_8") + self.pushButton_8.setCheckable(True) + self.pushButton_8.setAutoExclusive(False) self.gridLayout_11.addWidget(self.pushButton_8, 0, 0, 1, 1) self.pb_snap_midp = QPushButton(self.groupBox_5) self.pb_snap_midp.setObjectName(u"pb_snap_midp") self.pb_snap_midp.setCheckable(True) + self.pb_snap_midp.setAutoExclusive(False) self.gridLayout_11.addWidget(self.pb_snap_midp, 0, 1, 1, 1) self.pb_snap_angle = QPushButton(self.groupBox_5) self.pb_snap_angle.setObjectName(u"pb_snap_angle") self.pb_snap_angle.setCheckable(True) + self.pb_snap_angle.setAutoExclusive(False) self.gridLayout_11.addWidget(self.pb_snap_angle, 3, 1, 1, 1) self.verticalLayout_3.addWidget(self.groupBox_5) - self.tabWidget.addTab(self.widget, "") - self.widget1 = QWidget() - self.widget1.setObjectName(u"widget1") - self.tabWidget.addTab(self.widget1, "") + self.tabWidget.addTab(self.snaps, "") + self.settings = QWidget() + self.settings.setObjectName(u"settings") + self.tabWidget.addTab(self.settings, "") self.gridLayout.addWidget(self.tabWidget, 3, 0, 1, 1) @@ -582,7 +591,7 @@ class Ui_fluencyCAD(object): self.retranslateUi(fluencyCAD) self.InputTab.setCurrentIndex(0) - self.tabWidget.setCurrentIndex(1) + self.tabWidget.setCurrentIndex(0) QMetaObject.connectSlotsByName(fluencyCAD) @@ -710,8 +719,8 @@ class Ui_fluencyCAD(object): self.pushButton_8.setText(QCoreApplication.translate("fluencyCAD", u"Pnt", None)) self.pb_snap_midp.setText(QCoreApplication.translate("fluencyCAD", u"MidP", None)) self.pb_snap_angle.setText(QCoreApplication.translate("fluencyCAD", u"Angles", None)) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.widget), QCoreApplication.translate("fluencyCAD", u"Setg 1", None)) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.widget1), QCoreApplication.translate("fluencyCAD", u"Setg 2", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.snaps), QCoreApplication.translate("fluencyCAD", u"Setg 1", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.settings), QCoreApplication.translate("fluencyCAD", u"Setg 2", None)) self.menuFile.setTitle(QCoreApplication.translate("fluencyCAD", u"File", None)) self.menuSettings.setTitle(QCoreApplication.translate("fluencyCAD", u"Settings", None)) # retranslateUi diff --git a/doc/flow.md b/doc/flow.md index b7b9690..613ac32 100644 --- a/doc/flow.md +++ b/doc/flow.md @@ -32,4 +32,4 @@ So far these are the elements: - Code: A special type that directly builds bodys from sdfCAD code. - Body: The 3D meshed result from sdfCAD - Sketch: The base to draw new entities. -- Interactor: A special component mesh that is used to manipulate the bodys in 3d view. \ No newline at end of file +- Interactor (edges): A special component mesh that is used to manipulate the bodys in 3d view. \ No newline at end of file diff --git a/drawing_modules/draw_widget_solve.py b/drawing_modules/draw_widget_solve.py index e7429fb..c2d8c49 100644 --- a/drawing_modules/draw_widget_solve.py +++ b/drawing_modules/draw_widget_solve.py @@ -24,15 +24,60 @@ class SketchWidget(QWidget): self.hovered_point = None self.selected_line = None + ### Display Settings self.snapping_range = 20 # Range in pixels for snapping self.zoom = 1 + # Mouse Input self.setMouseTracking(True) self.mouse_mode = False + self.is_construct = False + + self.snap_mode = { + "point": False, + "mpoint": False, + "horiz": False, + "vert":False, + "grid": False, + "angle": False + } + + # Solver self.solv = SolverSystem() self.sketch = Sketch2d() + def act_line_mode(self, checked): + if checked: + self.mouse_mode = 'line' + print(self.mouse_mode) + else: + self.mouse_mode = None + + def act_constrain_pt_pt_mode(self): + self.mouse_mode = 'pt_pt' + + def act_constrain_pt_line_mode(self): + self.mouse_mode = 'pt_line' + + def act_constrain_horiz_line_mode(self): + self.mouse_mode = 'horiz' + + def act_constrain_vert_line_mode(self): + self.mouse_mode = 'vert' + + def act_constrain_distance_mode(self): + self.mouse_mode = 'distance' + + def act_constrain_mid_point_mode(self): + self.mouse_mode = 'pb_con_mid' + + def on_snap_mode_change(self, helper_type: str, value: bool): + self.snap_mode[helper_type] = value + + def on_construct_change(self, checked): + self.is_construct = checked + def create_sketch(self, sketch_in ): self.sketch = Sketch2d() self.sketch.id = sketch_in.id @@ -46,6 +91,7 @@ class SketchWidget(QWidget): return self.sketch def reset_buffers(self): + self.mouse_mode = None self.line_draw_buffer = [None, None] self.drag_buffer = [None, None] self.main_buffer = [None, None] @@ -235,11 +281,12 @@ class SketchWidget(QWidget): Go through solversystem and check points2d for changes in position after solving :return: List with points that now have a different position """ + old_points_ui = [] new_points_ui = [] - for old_point_ui in self.sketch.points: - old_points_ui.append(old_point_ui.ui_point) + for index, old_point_ui in enumerate(self.sketch.points): + old_points_ui.append((index, old_point_ui.ui_point)) for i in range(self.sketch.entity_len()): # Iterate though full length because mixed list from SS @@ -255,9 +302,10 @@ class SketchWidget(QWidget): if len(old_points_ui) != len(new_points_ui): print(f"Length mismatch {len(old_points_ui)} - {len(new_points_ui)}") - for index, (old_point, new_point) in enumerate(zip(old_points_ui, new_points_ui)): + for (old_index, old_point), new_point in zip(old_points_ui, new_points_ui): if old_point != new_point: - differences.append((index, old_point, new_point)) + #print(old_point) + differences.append((old_index, old_point, new_point)) return differences @@ -266,13 +314,20 @@ class SketchWidget(QWidget): # print("Initial slv_points_main:", self.slv_points_main) print("Change list:", point_list) - if len(point_list) > 0: + """for point in point_list: + new = Point2D(point.x(), point.y()) + self.sketch.points.append(new)""" + + if len(point_list) > 1: for tbu_points_idx in point_list: # Each tbu_points_idx is a tuple: (index, old_point, new_point) index, old_point, new_point = tbu_points_idx # Update the point in slv_points_main self.sketch.points[index].ui_point = new_point + + """for points in self.sketch.points: + print(points.ui_point)""" # Print updated state # print("Updated slv_points_main:", self.slv_points_main) @@ -291,7 +346,7 @@ class SketchWidget(QWidget): if event.button() == Qt.LeftButton and not self.mouse_mode: self.drag_buffer[1] = local_event_pos - print("Le main buffer", self.drag_buffer) + #print("Le main buffer", self.drag_buffer) if not None in self.main_buffer and len(self.main_buffer) == 2: entry = self.drag_buffer[0] @@ -300,9 +355,9 @@ class SketchWidget(QWidget): self.sketch.solve() - points_need_update = self.check_all_points() - self.update_ui_points(points_need_update) - self.check_all_lines_and_update(points_need_update) + #points_need_update = self.check_all_points() + #self.update_ui_points(points_need_update) + #self.check_all_lines_and_update(points_need_update) self.update() self.drag_buffer = [None, None] @@ -315,6 +370,7 @@ class SketchWidget(QWidget): self.drag_buffer[0] = self.get_handle_from_ui_point(self.hovered_point) if event.button() == Qt.RightButton and self.mouse_mode: + self.constrain_done.emit() self.reset_buffers() if event.button() == Qt.LeftButton and self.mouse_mode == "line": @@ -329,6 +385,10 @@ class SketchWidget(QWidget): v = clicked_pos.y() point = Point2D(u,v) + print("construct", self.is_construct ) + # Construction mode + point.is_helper = self.is_construct + self.sketch.add_point(point) self.line_draw_buffer[0] = point @@ -337,17 +397,25 @@ class SketchWidget(QWidget): u = clicked_pos.x() v = clicked_pos.y() - + print("construct", self.is_construct) point = Point2D(u, v) + + # Construction mode + point.is_helper = self.is_construct + self.sketch.add_point(point) self.line_draw_buffer[1] = point - print("Buffer state", self.line_draw_buffer) + #print("Buffer state", self.line_draw_buffer) if self.line_draw_buffer[0] and self.line_draw_buffer[1]: line = Line2D(self.line_draw_buffer[0], self.line_draw_buffer[1]) + + # Construction mode + line.is_helper = self.is_construct + self.sketch.add_line(line) # Reset the buffer for the next line segment @@ -367,7 +435,7 @@ class SketchWidget(QWidget): self.main_buffer[1] = self.get_handle_from_ui_point(self.hovered_point) if self.main_buffer[0] and self.main_buffer[1]: - print("buf", self.main_buffer) + # print("buf", self.main_buffer) self.sketch.coincident(self.main_buffer[0], self.main_buffer[1], self.sketch.wp) @@ -387,7 +455,7 @@ class SketchWidget(QWidget): self.main_buffer = [None, None] if event.button() == Qt.LeftButton and self.mouse_mode == "pt_line": - print("ptline") + #print("ptline") line_selected = None if self.hovered_point and not self.main_buffer[1]: @@ -418,9 +486,10 @@ class SketchWidget(QWidget): self.main_buffer = [None, None] if event.button() == Qt.LeftButton and self.mouse_mode == "pb_con_mid": - print("ptline") + #print("ptline") line_selected = None + if self.hovered_point and not self.main_buffer[1]: self.main_buffer[0] = self.get_handle_from_ui_point(self.hovered_point) @@ -433,6 +502,12 @@ class SketchWidget(QWidget): if self.sketch.solve() == ResultFlag.OKAY: print("Fuck yeah") + for idx, line in enumerate(self.sketch.lines): + # print(line.crd1.ui_point) + if self.is_point_on_line(local_event_pos, line.crd1.ui_point, line.crd2.ui_point): + self.sketch.lines[idx].constraints.append("mid") + print(self.sketch.lines[idx].constraints) + elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE: print("Solve_failed - Converge") @@ -450,12 +525,21 @@ class SketchWidget(QWidget): line_selected = self.get_line_handle_from_ui_point(local_event_pos) + + if line_selected: self.sketch.horizontal(line_selected, self.sketch.wp) if self.sketch.solve() == ResultFlag.OKAY: print("Fuck yeah") + # Add succesful constraint to constrain draw list so it gets drawn in paint function + for idx, line in enumerate(self.sketch.lines): + #print(line.crd1.ui_point) + if self.is_point_on_line(local_event_pos, line.crd1.ui_point, line.crd2.ui_point): + self.sketch.lines[idx].constraints.append("hrz") + #print(self.sketch.lines[idx].constraints) + elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE: print("Solve_failed - Converge") @@ -473,6 +557,11 @@ class SketchWidget(QWidget): if self.sketch.solve() == ResultFlag.OKAY: print("Fuck yeah") + for idx, line in enumerate(self.sketch.lines): + # print(line.crd1.ui_point) + if self.is_point_on_line(local_event_pos, line.crd1.ui_point, line.crd2.ui_point): + self.sketch.lines[idx].constraints.append("vrt") + # print(self.sketch.lines[idx].constraints) elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE: print("Solve_failed - Converge") @@ -490,7 +579,7 @@ class SketchWidget(QWidget): e2 = None if self.hovered_point: - print("buf point") + # print("buf point") # Get the point as UI point as buffer self.main_buffer[0] = self.hovered_point @@ -514,6 +603,11 @@ class SketchWidget(QWidget): if self.sketch.solve() == ResultFlag.OKAY: print("Fuck yeah") + for idx, line in enumerate(self.sketch.lines): + # print(line.crd1.ui_point) + if self.is_point_on_line(local_event_pos, line.crd1.ui_point, line.crd2.ui_point): + if "dist" not in self.sketch.lines[idx].constraints: + self.sketch.lines[idx].constraints.append("dst") elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE: print("Solve_failed - Converge") @@ -532,6 +626,8 @@ class SketchWidget(QWidget): self.update_ui_points(points_need_update) self.check_all_lines_and_update(points_need_update) + + self.update() def mouseMoveEvent(self, event): @@ -540,7 +636,7 @@ class SketchWidget(QWidget): closest_point = None min_distance = float('inf') - threshold = 10 # Distance threshold for highlighting + threshold = 15 # Distance threshold for highlighting if self.mouse_mode == "line" and self.line_draw_buffer[0]: # Update the current cursor position as the second point @@ -562,7 +658,7 @@ class SketchWidget(QWidget): if closest_point != self.hovered_point: self.hovered_point = closest_point - print(self.hovered_point) + #print(self.hovered_point) for line in self.sketch.lines: p1 = line.crd1.ui_point @@ -571,11 +667,14 @@ class SketchWidget(QWidget): if self.is_point_on_line(local_event_pos, p1, p2): self.selected_line = p1, p2 - # Midpointsnap only in drawer not solver - mid = self.calculate_midpoint(p1, p2) - distance = (local_event_pos - mid).manhattanLength() - if distance < threshold and distance < min_distance: - self.hovered_point = mid + if self.snap_mode.get("mpoint"): + # Midpointsnap only in drawer not solver + mid = self.calculate_midpoint(p1, p2) + distance = (local_event_pos - mid).manhattanLength() + if distance < threshold and distance < min_distance: + self.hovered_point = mid + break + break else: self.selected_line = None @@ -678,6 +777,103 @@ class SketchWidget(QWidget): widget_y = -point.y() return QPoint(int(widget_x), int(widget_y)) + def draw_measurement(self,painter, start_point, end_point): + + pen_normal = QPen(Qt.gray) + pen_normal.setWidthF(2 / self.zoom) + + pen_planned = QPen(Qt.gray) + pen_planned.setStyle(Qt.PenStyle.DotLine) + pen_planned.setWidthF(2 / self.zoom) + + pen_construct = QPen(Qt.cyan) + pen_construct.setStyle(Qt.PenStyle.DotLine) + pen_construct.setWidthF(1 / self.zoom) + + pen_solver = QPen(Qt.green) + pen_solver.setWidthF(2 / self.zoom) + + pen_text = QPen(Qt.white) + pen_text.setWidthF(1 / self.zoom) + + # Calculate the direction of the line + dx = end_point.x() - start_point.x() + dy = end_point.y() - start_point.y() + + # Swap and negate to get a perpendicular vector + perp_dx = -dy + perp_dy = dx + + # Normalize the perpendicular vector + length = (perp_dx ** 2 + perp_dy ** 2) ** 0.5 + if length == 0: # Prevent division by zero + return + perp_dx /= length + perp_dy /= length + + # Fixed length for the perpendicular lines + fixed_length = 40 # Adjust as needed + half_length = fixed_length # fixed_length / 2 + + # Calculate endpoints for the perpendicular line at the start + start_perp_start_x = start_point.x() + perp_dx + start_perp_start_y = start_point.y() + perp_dy + + start_perp_end_x = start_point.x() + perp_dx * half_length * (1 / self.zoom) + start_perp_end_y = start_point.y() + perp_dy * half_length * (1 / self.zoom) + + start_perp_end_x_conn_line = start_point.x() + perp_dx * half_length * 0.75 * (1 / self.zoom) + start_perp_end_y_conn_line = start_point.y() + perp_dy * half_length * 0.75 * (1 / self.zoom) + + # Draw the perpendicular line at the start + painter.setPen(pen_construct) # Different color for the perpendicular line + painter.drawLine( + QPointF(start_perp_start_x, start_perp_start_y), + QPointF(start_perp_end_x, start_perp_end_y) + ) + + # Calculate endpoints for the perpendicular line at the end + end_perp_start_x = end_point.x() + perp_dx + end_perp_start_y = end_point.y() + perp_dy + + end_perp_end_x = end_point.x() + perp_dx * half_length * (1 / self.zoom) + end_perp_end_y = end_point.y() + perp_dy * half_length * (1 / self.zoom) + + end_perp_end_x_conn_line = end_point.x() + perp_dx * half_length * .75 * (1 / self.zoom) + end_perp_end_y_conn_line = end_point.y() + perp_dy * half_length * .75 * (1 / self.zoom) + + # Draw the perpendicular line at the end + painter.drawLine( + QPointF(end_perp_start_x, end_perp_start_y), + QPointF(end_perp_end_x, end_perp_end_y) + ) + + painter.drawLine( + QPointF(end_perp_end_x_conn_line, end_perp_end_y_conn_line), + QPointF(start_perp_end_x_conn_line, start_perp_end_y_conn_line) + ) + + # Save painter state + painter.save() + painter.setPen(pen_text) + + # Calculate the distance and midpoint + dis = self.distance(start_point, end_point) + mid = self.calculate_midpoint(QPointF(start_perp_end_x_conn_line, start_perp_end_y_conn_line), + QPointF(end_perp_end_x_conn_line, end_perp_end_y_conn_line)) + + # mid = self.calculate_midpoint(start_point, end_point) + + # Transform for text + painter.translate(mid.x(), mid.y()) # Move to the midpoint + painter.scale(1, -1) # Flip y-axis back to make text readable + + # Draw the text + painter.drawText(0, 0, str(round(dis, 2))) # Draw text at transformed position + + # Restore painter state + painter.restore() + def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) @@ -700,6 +896,10 @@ class SketchWidget(QWidget): pen_normal = QPen(Qt.gray) pen_normal.setWidthF(2 / self.zoom) + pen_planned = QPen(Qt.gray) + pen_planned.setStyle(Qt.PenStyle.DotLine) + pen_planned.setWidthF(2 / self.zoom) + pen_construct = QPen(Qt.cyan) pen_construct.setStyle(Qt.PenStyle.DotLine) pen_construct.setWidthF(1 / self.zoom) @@ -726,26 +926,10 @@ class SketchWidget(QWidget): if self.mouse_mode == "line" and self.line_draw_buffer[0] and self.dynamic_line_end is not None: start_point = self.line_draw_buffer[0].ui_point end_point = self.dynamic_line_end - painter.setPen(Qt.red) # Use a different color for the dynamic line + painter.setPen(pen_planned) # Use a different color for the dynamic line painter.drawLine(start_point, end_point) - # Save painter state - painter.save() - painter.setPen(pen_text) - - # Calculate the distance and midpoint - dis = self.distance(start_point, end_point) - mid = self.calculate_midpoint(start_point, end_point) - - # Transform for text - painter.translate(mid.x(), mid.y()) # Move to the midpoint - painter.scale(1, -1) # Flip y-axis back to make text readable - - # Draw the text - painter.drawText(0, 0, str(round(dis, 2))) # Draw text at transformed position - - # Restore painter state - painter.restore() + self.draw_measurement(painter, start_point, end_point) for line in self.sketch.lines: if line.is_helper: @@ -759,6 +943,17 @@ class SketchWidget(QWidget): p2 = line.crd2.ui_point painter.drawLine(p1, p2) + if not self.selected_line: + + painter.save() + midp = self.calculate_midpoint(p1, p2) + painter.translate(midp) + painter.scale(1, -1) + + for i, text in enumerate(line.constraints): + painter.drawText(0, i * 15, f"> {text} <") + painter.restore() + # Draw all solver points if self.sketch.entity_len(): painter.setPen(pen_solver) @@ -782,6 +977,9 @@ class SketchWidget(QWidget): painter.setPen(QPen(Qt.red, 2 / self.zoom)) painter.drawLine(p1, p2) + self.draw_measurement(painter, p1, p2) + + """for cross in self.sketch.proj_points: self.draw_cross(painter, cross, 10 / self.zoom) @@ -825,6 +1023,9 @@ class Line2D: # Construction Geometry self.is_helper: bool = False + # String list with applied constraints + self.constraints: list = [] + class Sketch2d(SolverSystem): """ diff --git a/gui.ui b/gui.ui index 4cfc2c0..55daca4 100644 --- a/gui.ui +++ b/gui.ui @@ -733,6 +733,9 @@ true + + false + @@ -813,6 +816,9 @@ true + + false + @@ -836,9 +842,9 @@ QTabWidget::South - 1 + 0 - + Setg 1 @@ -882,6 +888,9 @@ true + + false + @@ -919,6 +928,9 @@ true + + false + @@ -929,6 +941,9 @@ true + + false + @@ -949,6 +964,12 @@ Pnt + + true + + + false + @@ -959,6 +980,9 @@ true + + false + @@ -969,6 +993,9 @@ true + + false + @@ -976,7 +1003,7 @@ - + Setg 2 diff --git a/license.md b/license.md new file mode 100644 index 0000000..0bab9db --- /dev/null +++ b/license.md @@ -0,0 +1,16 @@ +Copyright (C) 2025 Thomas Herrmann +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/main.py b/main.py index 2b5588a..edc4842 100644 --- a/main.py +++ b/main.py @@ -120,13 +120,13 @@ class MainWindow(QMainWindow): self.ui.pb_flip_face.pressed.connect(self.on_flip_face) ###Modes - self.ui.pb_linetool.pressed.connect(self.act_line_mode) - self.ui.pb_con_ptpt.pressed.connect(self.act_constrain_pt_pt_mode) - self.ui.pb_con_line.pressed.connect(self.act_constrain_pt_line_mode) - self.ui.pb_con_horiz.pressed.connect(self.act_constrain_horiz_line_mode) - self.ui.pb_con_vert.pressed.connect(self.act_constrain_vert_line_mode) - self.ui.pb_con_dist.pressed.connect(self.act_constrain_distance_mode) - self.ui.pb_con_mid.pressed.connect(self.act_constrain_mid_point_mode) + self.ui.pb_linetool.clicked.connect(self.sketchWidget.act_line_mode) + self.ui.pb_con_ptpt.clicked.connect(self.sketchWidget.act_constrain_pt_pt_mode) + self.ui.pb_con_line.clicked.connect(self.sketchWidget.act_constrain_pt_line_mode) + self.ui.pb_con_horiz.clicked.connect(self.sketchWidget.act_constrain_horiz_line_mode) + self.ui.pb_con_vert.clicked.connect(self.sketchWidget.act_constrain_vert_line_mode) + self.ui.pb_con_dist.clicked.connect(self.sketchWidget.act_constrain_distance_mode) + self.ui.pb_con_mid.clicked.connect(self.sketchWidget.act_constrain_mid_point_mode) ### Operations self.ui.pb_extrdop.pressed.connect(self.send_extrude) @@ -137,12 +137,18 @@ class MainWindow(QMainWindow): self.setFocusPolicy(Qt.StrongFocus) self.send_command.connect(self.custom_3D_Widget.on_receive_command) - self.ui.actionNew_Project.triggered.connect(self.new_project) - + self.ui.pb_enable_construct.clicked.connect(self.sketchWidget.on_construct_change) self.project = Project() self.new_project() + ### SNAPS + + self.ui.pb_snap_midp.toggled.connect(lambda checked: self.sketchWidget.on_snap_mode_change("mpoint", checked)) + self.ui.pb_snap_horiz.toggled.connect(lambda checked: self.sketchWidget.on_snap_mode_change("horiz", checked)) + self.ui.pb_snap_vert.toggled.connect(lambda checked: self.sketchWidget.on_snap_mode_change("vert", checked)) + self.ui.pb_snap_angle.toggled.connect(lambda checked: self.sketchWidget.on_snap_mode_change("angle", checked)) + self.ui.pb_enable_snap.toggled.connect(lambda checked: self.sketchWidget.on_snap_mode_change("point", checked)) ### COMPOS ### COMPOS @@ -156,7 +162,6 @@ class MainWindow(QMainWindow): self.project.timeline = timeline self.new_component() - def new_component(self): print("Creating a new component...") @@ -270,7 +275,7 @@ class MainWindow(QMainWindow): sketch.original_sketch = sketch_from_widget #Get parameters - points = sketch_from_widget.points + points = [point for point in sketch_from_widget.points if hasattr(point, 'is_helper') and not point.is_helper] sketch.convert_points_for_sdf(points) sketch.id = sketch_from_widget.id @@ -280,7 +285,7 @@ class MainWindow(QMainWindow): # Register sketch to timeline ### Add selection compo here compo_id = self.get_activated_compo() - print("newsketch_name", sketch.id) + #print("newsketch_name", sketch.id) self.project.timeline[compo_id].sketches[sketch.id] = sketch # Add Item to slection menu @@ -294,6 +299,7 @@ class MainWindow(QMainWindow): self.ui.sketch_list.setCurrentItem(items) def on_compo_change(self): + '''This function redraws the sdf and helper mesh from available bodies and adds the names back to the list entries''' self.custom_3D_Widget.clear_body_actors() self.custom_3D_Widget.clear_actors_interactor() self.custom_3D_Widget.clear_actors_projection() @@ -303,27 +309,27 @@ class MainWindow(QMainWindow): self.ui.sketch_list.clear() self.ui.body_list.clear() - print("id", compo_id) - print("sketch_registry", self.project.timeline[compo_id].sketches) + #print("id", compo_id) + #print("sketch_registry", self.project.timeline[compo_id].sketches) for sketch in self.project.timeline[compo_id].sketches: - print(sketch) + #print(sketch) self.ui.sketch_list.addItem(sketch) for body in self.project.timeline[compo_id].bodies: self.ui.body_list.addItem(body) - if self.project.timeline[compo_id].bodies: - item = self.ui.body_list.findItems(body , Qt.MatchExactly)[0] - self.ui.body_list.setCurrentItem(item) - self.draw_mesh() + if self.project.timeline[compo_id].bodies: + item = self.ui.body_list.findItems(body , Qt.MatchExactly)[0] + self.ui.body_list.setCurrentItem(item) + self.draw_mesh() - selected = self.ui.body_list.currentItem() - name = selected.text() + selected = self.ui.body_list.currentItem() + name = selected.text() - edges = self.project.timeline[compo_id].bodies[name].interactor.edges - offset_vec = self.project.timeline[compo_id].bodies[name].interactor.offset_vector - self.custom_3D_Widget.load_interactor_mesh(edges, offset_vec) + edges = self.project.timeline[compo_id].bodies[name].interactor.edges + offset_vec = self.project.timeline[compo_id].bodies[name].interactor.offset_vector + self.custom_3D_Widget.load_interactor_mesh(edges, offset_vec) def edit_sketch(self): selected = self.ui.sketch_list.currentItem() @@ -353,49 +359,6 @@ class MainWindow(QMainWindow): def on_flip_face(self): self.send_command.emit("flip") - def act_line_mode(self): - if not self.ui.pb_linetool.isChecked(): - self.sketchWidget.mouse_mode = 'line' - else: - self.sketchWidget.mouse_mode = None - self.sketchWidget.line_draw_buffer = [None, None] - - def act_constrain_pt_pt_mode(self): - if not self.ui.pb_con_ptpt.isChecked(): - self.sketchWidget.mouse_mode = 'pt_pt' - else: - self.sketchWidget.mouse_mode = None - - def act_constrain_pt_line_mode(self): - if not self.ui.pb_con_line.isChecked(): - self.sketchWidget.mouse_mode = 'pt_line' - else: - self.sketchWidget.mouse_mode = None - - def act_constrain_horiz_line_mode(self): - if not self.ui.pb_con_horiz.isChecked(): - self.sketchWidget.mouse_mode = 'horiz' - else: - self.sketchWidget.mouse_mode = None - - def act_constrain_vert_line_mode(self): - if not self.ui.pb_con_vert.isChecked(): - self.sketchWidget.mouse_mode = 'vert' - else: - self.sketchWidget.mouse_mode = None - - def act_constrain_distance_mode(self): - if not self.ui.pb_con_dist.isChecked(): - self.sketchWidget.mouse_mode = 'distance' - else: - self.sketchWidget.mouse_mode = None - - def act_constrain_mid_point_mode(self): - if not self.ui.pb_con_mid.isChecked(): - self.sketchWidget.mouse_mode = 'pb_con_mid' - else: - self.sketchWidget.mouse_mode = None - def draw_op_complete(self): # safely disable the line modes self.ui.pb_linetool.setChecked(False) @@ -420,7 +383,7 @@ class MainWindow(QMainWindow): model_data = vesta.generate_mesh_from_sdf(model, resolution=64, threshold=0) vertices, faces = model_data - vesta.save_mesh_as_stl(vertices, faces, 'test.stl') + #vesta.save_mesh_as_stl(vertices, faces, 'test.stl') self.custom_3D_Widget.render_from_points_direct_with_faces(vertices, faces) def on_item_changed(self, current_item, previous_item): @@ -464,8 +427,9 @@ class MainWindow(QMainWindow): #print(sketch) points = sketch.sdf_points + # detect loop that causes problems in mesh generation if points[-1] == points[0]: - #detect loop that causes problems in mesh generation + print("overlap") del points[-1] dialog = ExtrudeDialog(self) @@ -484,8 +448,8 @@ class MainWindow(QMainWindow): centroid = self.custom_3D_Widget.centroid if centroid is None: centroid = [0, 0, 0] - else: - centroid = list(centroid) + """else: + centroid = list(centroid)""" #print("This centroid ", centroid) sketch.origin = centroid diff --git a/requirements.txt b/requirements.txt index b6000b1..dfbcfbb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,66 +1,60 @@ -certifi==2024.7.4 -cffi==1.16.0 -charset-normalizer==3.3.2 -contourpy==1.2.0 +certifi==2025.1.31 +cffi==1.17.1 +charset-normalizer==3.4.1 +contourpy==1.3.1 cycler==0.12.1 -fonttools==4.47.0 -freetype-py==2.4.0 +flexcache==0.3 +flexparser==0.4 +fonttools==4.56.0 +freetype-py==2.5.1 hsluv==5.0.4 -idna==3.7 -imageio==2.33.1 -kiwisolver==1.4.5 -lazy_loader==0.3 +idna==3.10 +imageio==2.37.0 +kiwisolver==1.4.8 +lazy_loader==0.4 markdown-it-py==3.0.0 -matplotlib==3.8.2 +matplotlib==3.10.1 mdurl==0.1.2 -meshio==5.3.4 +meshio==5.3.5 names==0.3.0 -networkx==3.2.1 -Nuitka==2.2.1 -numpy==1.26.2 -numpy-stl==3.1.1 +networkx==3.4.2 +Nuitka==2.6.9 +numpy==2.2.4 +numpy-stl==3.2.0 ordered-set==4.1.0 -packaging==23.2 -panda3d-gltf==1.2.0 -panda3d-simplepbr==0.12.0 -Pillow==10.1.0 -Pint==0.22 -platformdirs==4.2.2 +packaging==24.2 +panda3d-gltf==1.3.0 +panda3d-simplepbr==0.13.0 +pillow==11.1.0 +Pint==0.24.4 +platformdirs==4.3.7 pooch==1.8.2 -pycparser==2.21 -pygame==2.5.2 -Pygments==2.17.2 -PyOpenGL==3.1.7 -pyparsing==3.1.1 -PyQt6==6.7.0 -PyQt6-3D==6.7.0 -PyQt6-3D-Qt6==6.7.0 -PyQt6-Qt6==6.7.0 -PyQt6-sip==13.6.0 -PySide6==6.6.1 -PySide6-Addons==6.6.1 -PySide6-Essentials==6.6.1 -python-dateutil==2.8.2 +pycparser==2.22 +pygame==2.6.1 +Pygments==2.19.1 +pyparsing==3.2.3 +PySide6_Essentials==6.8.3 +python-dateutil==2.9.0.post0 python-solvespace==3.0.8 -python-utils==3.8.2 -pyvista==0.43.10 -pyvistaqt==0.11.1 -QtPy==2.4.1 +python-utils==3.9.1 +pyvista==0.44.2 +pyvistaqt==0.11.2 +QtPy==2.4.3 requests==2.32.3 -rich==13.7.0 -scikit-image==0.22.0 -scipy==1.11.4 +rich==13.9.4 +scikit-image==0.25.2 +scipy==1.15.2 scooby==0.10.0 sdfcad @ git+https://gitlab.com/nobodyinperson/sdfCAD@9bd4e9021c6ee7e685ee28e8a3a5d2d2c028190c -shapely==2.0.4 -shiboken6==6.6.1 -six==1.16.0 -tifffile==2023.12.9 -trimesh==4.3.2 +shapely==2.1.0rc1 +shiboken6==6.8.3 +six==1.17.0 +tifffile==2025.3.13 +trimesh==4.6.5 tripy==1.0.0 -typing_extensions==4.9.0 -urllib3==2.2.2 -vispy==0.14.2 -vtk==9.3.0 -vulkan==1.3.275.0 -zstandard==0.22.0 +typing_extensions==4.13.0 +urllib3==2.3.0 +vispy==0.14.3 +vtk==9.4.1 +vulkan==1.3.275.1 +zstandard==0.23.0