From 878b6093b713d3b90738d3b96684a9443ba6ff0e Mon Sep 17 00:00:00 2001 From: bklronin Date: Thu, 2 Jan 2025 19:28:43 +0100 Subject: [PATCH 1/6] - Added new buttons and settings --- drawing_modules/draw_widget_solve.py | 84 ++++++++++++++++++++++++---- gui.ui | 2 +- 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/drawing_modules/draw_widget_solve.py b/drawing_modules/draw_widget_solve.py index e7429fb..15d145b 100644 --- a/drawing_modules/draw_widget_solve.py +++ b/drawing_modules/draw_widget_solve.py @@ -264,7 +264,7 @@ class SketchWidget(QWidget): def update_ui_points(self, point_list: list): # Print initial state of slv_points_main # print("Initial slv_points_main:", self.slv_points_main) - print("Change list:", point_list) + #print("Change list:", point_list) if len(point_list) > 0: for tbu_points_idx in point_list: @@ -291,7 +291,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] @@ -343,7 +343,7 @@ class SketchWidget(QWidget): 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]: @@ -367,7 +367,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 +387,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,7 +418,7 @@ 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]: @@ -490,7 +490,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 @@ -562,7 +562,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 @@ -700,6 +700,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,16 +730,76 @@ 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) + # 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(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 diff --git a/gui.ui b/gui.ui index 4cfc2c0..2d34b13 100644 --- a/gui.ui +++ b/gui.ui @@ -836,7 +836,7 @@ QTabWidget::South - 1 + 0 From 2a7f718b3e3bab505bfe26694d05dec72b175cf0 Mon Sep 17 00:00:00 2001 From: bklronin Date: Sun, 16 Feb 2025 22:00:59 +0100 Subject: [PATCH 2/6] - Added construction lines switching - Moved callbacks into sketchwidget from main. - Changed reset on right click --- Gui.py | 31 +++++++++----- drawing_modules/draw_widget_solve.py | 49 +++++++++++++++++++++- gui.ui | 31 +++++++++++++- main.py | 63 ++++------------------------ 4 files changed, 106 insertions(+), 68 deletions(-) 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/drawing_modules/draw_widget_solve.py b/drawing_modules/draw_widget_solve.py index 15d145b..fe8fc3a 100644 --- a/drawing_modules/draw_widget_solve.py +++ b/drawing_modules/draw_widget_solve.py @@ -24,15 +24,48 @@ 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 + + # 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.sketchWidget.mouse_mode = 'pb_con_mid' + + 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 +79,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] @@ -315,6 +349,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 +364,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,8 +376,12 @@ 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 @@ -348,6 +391,10 @@ class SketchWidget(QWidget): 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 diff --git a/gui.ui b/gui.ui index 2d34b13..55daca4 100644 --- a/gui.ui +++ b/gui.ui @@ -733,6 +733,9 @@ true + + false + @@ -813,6 +816,9 @@ true + + false + @@ -838,7 +844,7 @@ 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/main.py b/main.py index 2b5588a..6e67dfe 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,9 +137,8 @@ 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() @@ -156,7 +155,6 @@ class MainWindow(QMainWindow): self.project.timeline = timeline self.new_component() - def new_component(self): print("Creating a new component...") @@ -270,7 +268,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 @@ -353,49 +351,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) From f26a59615928a84373d69b540ae63d95136427fd Mon Sep 17 00:00:00 2001 From: bklronin Date: Fri, 28 Mar 2025 21:17:19 +0100 Subject: [PATCH 3/6] - Added contrain displayed next to line - Slight change to point check from solver. --- drawing_modules/draw_widget_solve.py | 247 +++++++++++++++++---------- requirements.txt | 102 ++++++----- 2 files changed, 209 insertions(+), 140 deletions(-) diff --git a/drawing_modules/draw_widget_solve.py b/drawing_modules/draw_widget_solve.py index fe8fc3a..2386485 100644 --- a/drawing_modules/draw_widget_solve.py +++ b/drawing_modules/draw_widget_solve.py @@ -269,11 +269,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 @@ -289,24 +290,32 @@ 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 def update_ui_points(self, point_list: list): # Print initial state of slv_points_main # print("Initial slv_points_main:", self.slv_points_main) - #print("Change list:", point_list) + 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) @@ -334,9 +343,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] @@ -468,6 +477,7 @@ class SketchWidget(QWidget): #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) @@ -480,6 +490,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") @@ -497,12 +513,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") @@ -515,11 +540,18 @@ class SketchWidget(QWidget): if event.button() == Qt.LeftButton and self.mouse_mode == "vert": line_selected = self.get_line_handle_from_ui_point(local_event_pos) + + if line_selected: self.sketch.vertical(line_selected, self.sketch.wp) 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") @@ -536,6 +568,8 @@ class SketchWidget(QWidget): e1 = None e2 = None + + if self.hovered_point: # print("buf point") # Get the point as UI point as buffer @@ -561,6 +595,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") @@ -579,6 +618,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): @@ -725,6 +766,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) @@ -780,83 +918,7 @@ class SketchWidget(QWidget): painter.setPen(pen_planned) # Use a different color for the dynamic line painter.drawLine(start_point, end_point) - # 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() + self.draw_measurement(painter, start_point, end_point) for line in self.sketch.lines: if line.is_helper: @@ -870,6 +932,16 @@ class SketchWidget(QWidget): p2 = line.crd2.ui_point painter.drawLine(p1, p2) + self.draw_measurement(painter, p1, p2) + 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) @@ -936,6 +1008,9 @@ class Line2D: # Construction Geometry self.is_helper: bool = False + # String list with applied constraints + self.constraints: list = [] + class Sketch2d(SolverSystem): """ 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 From 4d7b2cdbad493d692f1e5b837ef5e87388d78096 Mon Sep 17 00:00:00 2001 From: bklronin Date: Sat, 29 Mar 2025 22:36:11 +0100 Subject: [PATCH 4/6] - Added enabling of midpsnap and prepared others - Show dimesnion on hover --- drawing_modules/draw_widget_solve.py | 63 +++++++++++++++++----------- main.py | 7 ++++ 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/drawing_modules/draw_widget_solve.py b/drawing_modules/draw_widget_solve.py index 2386485..c2d8c49 100644 --- a/drawing_modules/draw_widget_solve.py +++ b/drawing_modules/draw_widget_solve.py @@ -33,6 +33,15 @@ class SketchWidget(QWidget): 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() @@ -61,7 +70,10 @@ class SketchWidget(QWidget): self.mouse_mode = 'distance' def act_constrain_mid_point_mode(self): - self.sketchWidget.mouse_mode = 'pb_con_mid' + 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 @@ -292,7 +304,7 @@ class SketchWidget(QWidget): for (old_index, old_point), new_point in zip(old_points_ui, new_points_ui): if old_point != new_point: - print(old_point) + #print(old_point) differences.append((old_index, old_point, new_point)) return differences @@ -314,8 +326,8 @@ class SketchWidget(QWidget): # 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) + """for points in self.sketch.points: + print(points.ui_point)""" # Print updated state # print("Updated slv_points_main:", self.slv_points_main) @@ -523,10 +535,10 @@ class SketchWidget(QWidget): # 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) + #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) + #print(self.sketch.lines[idx].constraints) elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE: print("Solve_failed - Converge") @@ -540,8 +552,6 @@ class SketchWidget(QWidget): if event.button() == Qt.LeftButton and self.mouse_mode == "vert": line_selected = self.get_line_handle_from_ui_point(local_event_pos) - - if line_selected: self.sketch.vertical(line_selected, self.sketch.wp) @@ -568,8 +578,6 @@ class SketchWidget(QWidget): e1 = None e2 = None - - if self.hovered_point: # print("buf point") # Get the point as UI point as buffer @@ -628,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 @@ -659,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 @@ -932,15 +943,16 @@ class SketchWidget(QWidget): p2 = line.crd2.ui_point painter.drawLine(p1, p2) - self.draw_measurement(painter, p1, p2) - painter.save() - midp = self.calculate_midpoint(p1, p2) - painter.translate(midp) - painter.scale(1, -1) + if not self.selected_line: - for i, text in enumerate(line.constraints): - painter.drawText(0, i * 15, f"> {text} <") - painter.restore() + 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(): @@ -965,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) diff --git a/main.py b/main.py index 6e67dfe..b8fcddc 100644 --- a/main.py +++ b/main.py @@ -142,6 +142,13 @@ class MainWindow(QMainWindow): 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 From 6cf70b9ae218ff1625bf9aeb7e0fd8fc13bf366d Mon Sep 17 00:00:00 2001 From: bklronin Date: Sun, 13 Apr 2025 16:40:54 +0200 Subject: [PATCH 5/6] - Fixed redraw when component changed --- doc/flow.md | 2 +- main.py | 36 +++++++++++++++++++----------------- 2 files changed, 20 insertions(+), 18 deletions(-) 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/main.py b/main.py index b8fcddc..edc4842 100644 --- a/main.py +++ b/main.py @@ -285,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 @@ -299,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() @@ -308,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() @@ -382,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): @@ -426,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) @@ -446,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 From c1911e3fac25f5c8cba50fa893f692a4abf82daa Mon Sep 17 00:00:00 2001 From: bklronin Date: Sun, 1 Jun 2025 10:00:12 +0200 Subject: [PATCH 6/6] - added MIT license --- license.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 license.md 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