- pt_line constrain

- Scalable quadrant view
This commit is contained in:
bklronin 2024-06-25 15:22:10 +02:00
parent b197e52cf3
commit ac9176fbd3
5 changed files with 620 additions and 90 deletions

31
Gui.py
View File

@ -242,23 +242,29 @@ class Ui_fluencyCAD(object):
self.gridLayout_2.setObjectName(u"gridLayout_2") self.gridLayout_2.setObjectName(u"gridLayout_2")
self.pb_rectool = QPushButton(self.groupBox_2) self.pb_rectool = QPushButton(self.groupBox_2)
self.pb_rectool.setObjectName(u"pb_rectool") self.pb_rectool.setObjectName(u"pb_rectool")
self.pb_rectool.setCheckable(True)
self.pb_rectool.setAutoExclusive(False)
self.gridLayout_2.addWidget(self.pb_rectool, 1, 1, 1, 1, Qt.AlignTop) self.gridLayout_2.addWidget(self.pb_rectool, 1, 1, 1, 1, Qt.AlignTop)
self.pb_circtool = QPushButton(self.groupBox_2) self.pb_circtool = QPushButton(self.groupBox_2)
self.pb_circtool.setObjectName(u"pb_circtool") self.pb_circtool.setObjectName(u"pb_circtool")
self.pb_circtool.setCheckable(True)
self.pb_circtool.setAutoExclusive(False)
self.gridLayout_2.addWidget(self.pb_circtool, 2, 0, 1, 1, Qt.AlignTop) self.gridLayout_2.addWidget(self.pb_circtool, 2, 0, 1, 1, Qt.AlignTop)
self.pb_slotool = QPushButton(self.groupBox_2) self.pb_slotool = QPushButton(self.groupBox_2)
self.pb_slotool.setObjectName(u"pb_slotool") self.pb_slotool.setObjectName(u"pb_slotool")
self.pb_slotool.setCheckable(True)
self.pb_slotool.setAutoExclusive(False)
self.gridLayout_2.addWidget(self.pb_slotool, 2, 1, 1, 1, Qt.AlignTop) self.gridLayout_2.addWidget(self.pb_slotool, 2, 1, 1, 1, Qt.AlignTop)
self.pb_linetool = QPushButton(self.groupBox_2) self.pb_linetool = QPushButton(self.groupBox_2)
self.pb_linetool.setObjectName(u"pb_linetool") self.pb_linetool.setObjectName(u"pb_linetool")
self.pb_linetool.setCheckable(True) self.pb_linetool.setCheckable(True)
self.pb_linetool.setAutoExclusive(True) self.pb_linetool.setAutoExclusive(False)
self.gridLayout_2.addWidget(self.pb_linetool, 1, 0, 1, 1) self.gridLayout_2.addWidget(self.pb_linetool, 1, 0, 1, 1)
@ -274,25 +280,46 @@ class Ui_fluencyCAD(object):
self.pb_con_line = QPushButton(self.groupBox_3) self.pb_con_line = QPushButton(self.groupBox_3)
self.pb_con_line.setObjectName(u"pb_con_line") self.pb_con_line.setObjectName(u"pb_con_line")
self.pb_con_line.setCheckable(True) self.pb_con_line.setCheckable(True)
self.pb_con_line.setAutoExclusive(False)
self.gridLayout_4.addWidget(self.pb_con_line, 0, 1, 1, 1) self.gridLayout_4.addWidget(self.pb_con_line, 0, 1, 1, 1)
self.pb_con_ptpt = QPushButton(self.groupBox_3) self.pb_con_ptpt = QPushButton(self.groupBox_3)
self.pb_con_ptpt.setObjectName(u"pb_con_ptpt") self.pb_con_ptpt.setObjectName(u"pb_con_ptpt")
self.pb_con_ptpt.setCheckable(True) self.pb_con_ptpt.setCheckable(True)
self.pb_con_ptpt.setAutoExclusive(False)
self.gridLayout_4.addWidget(self.pb_con_ptpt, 0, 0, 1, 1) self.gridLayout_4.addWidget(self.pb_con_ptpt, 0, 0, 1, 1)
self.pb_con_horiz = QPushButton(self.groupBox_3) self.pb_con_horiz = QPushButton(self.groupBox_3)
self.pb_con_horiz.setObjectName(u"pb_con_horiz") self.pb_con_horiz.setObjectName(u"pb_con_horiz")
self.pb_con_horiz.setCheckable(True)
self.pb_con_horiz.setAutoExclusive(False)
self.gridLayout_4.addWidget(self.pb_con_horiz, 1, 0, 1, 1) self.gridLayout_4.addWidget(self.pb_con_horiz, 1, 0, 1, 1)
self.pb_con_vert = QPushButton(self.groupBox_3) self.pb_con_vert = QPushButton(self.groupBox_3)
self.pb_con_vert.setObjectName(u"pb_con_vert") self.pb_con_vert.setObjectName(u"pb_con_vert")
self.pb_con_vert.setCheckable(True)
self.pb_con_vert.setAutoExclusive(False)
self.gridLayout_4.addWidget(self.pb_con_vert, 1, 1, 1, 1) self.gridLayout_4.addWidget(self.pb_con_vert, 1, 1, 1, 1)
self.pb_con_dist = QPushButton(self.groupBox_3)
self.pb_con_dist.setObjectName(u"pb_con_dist")
self.pb_con_dist.setCheckable(True)
self.pb_con_dist.setAutoExclusive(False)
self.pb_con_dist.setAutoRepeatDelay(297)
self.gridLayout_4.addWidget(self.pb_con_dist, 2, 0, 1, 1)
self.pb_con_sym = QPushButton(self.groupBox_3)
self.pb_con_sym.setObjectName(u"pb_con_sym")
self.pb_con_sym.setCheckable(True)
self.pb_con_sym.setAutoExclusive(False)
self.gridLayout_4.addWidget(self.pb_con_sym, 2, 1, 1, 1)
self.gridLayout.addWidget(self.groupBox_3, 2, 0, 1, 1) self.gridLayout.addWidget(self.groupBox_3, 2, 0, 1, 1)
@ -352,5 +379,7 @@ class Ui_fluencyCAD(object):
self.pb_con_ptpt.setText(QCoreApplication.translate("fluencyCAD", u"Pt_Pt", None)) self.pb_con_ptpt.setText(QCoreApplication.translate("fluencyCAD", u"Pt_Pt", None))
self.pb_con_horiz.setText(QCoreApplication.translate("fluencyCAD", u"Horiz", None)) self.pb_con_horiz.setText(QCoreApplication.translate("fluencyCAD", u"Horiz", None))
self.pb_con_vert.setText(QCoreApplication.translate("fluencyCAD", u"Vert", None)) self.pb_con_vert.setText(QCoreApplication.translate("fluencyCAD", u"Vert", None))
self.pb_con_dist.setText(QCoreApplication.translate("fluencyCAD", u"Distnce", None))
self.pb_con_sym.setText(QCoreApplication.translate("fluencyCAD", u"Symetrc", None))
# retranslateUi # retranslateUi

View File

@ -1,14 +1,21 @@
import math
import re import re
from copy import copy from copy import copy
from PySide6.QtWidgets import QApplication, QWidget, QMessageBox from PySide6.QtWidgets import QApplication, QWidget, QMessageBox, QInputDialog
from PySide6.QtGui import QPainter, QPen, QColor from PySide6.QtGui import QPainter, QPen, QColor
from PySide6.QtCore import Qt, QPoint from PySide6.QtCore import Qt, QPoint, QPointF, Signal
from python_solvespace import SolverSystem, ResultFlag from python_solvespace import SolverSystem, ResultFlag
class SketchWidget(QWidget): class SketchWidget(QWidget):
constrain_done = Signal()
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.zoom = 1
self.selected_main_idx = None
self.pt_line_buffer = None
self.hovered_point = None self.hovered_point = None
self.line_buffer = None self.line_buffer = None
self.pt_pt_buffer = None self.pt_pt_buffer = None
@ -21,7 +28,12 @@ class SketchWidget(QWidget):
self.wp = None self.wp = None
self.solv = SolverSystem() self.solv = SolverSystem()
self.solventies = [] self.slv_points_main = []
self.slv_lines_main = []
def set_points(self, points: list):
self.points = points
#self.update()
def create_worplane(self): def create_worplane(self):
self.wp = self.solv.create_2d_base() self.wp = self.solv.create_2d_base()
@ -36,12 +48,10 @@ class SketchWidget(QWidget):
if match: if match:
handle_number = int(match.group(1)) handle_number = int(match.group(1))
print(f"Handle number: {handle_number}") print(f"Handle number: {handle_number}")
return int(handle_number) return int(handle_number)
else: else:
print("Handle number not found.") print("Handle number not found.")
return 0 return 0
def get_keys(self, d: dict, target: QPoint) -> list: def get_keys(self, d: dict, target: QPoint) -> list:
@ -59,156 +69,539 @@ class SketchWidget(QWidget):
return result return result
def distance(self, p1, p2):
return math.sqrt((p1.x() - p2.x())**2 + (p1.y() - p2.y())**2)
def is_point_on_line(self, p, p1, p2, tolerance=5):
# Calculate the lengths of the sides of the triangle
a = self.distance(p, p1)
b = self.distance(p, p2)
c = self.distance(p1, p2)
# Calculate the semi-perimeter
s = (a + b + c) / 2
# Calculate the area using Heron's formula
area = math.sqrt(s * (s - a) * (s - b) * (s - c))
# Calculate the height (perpendicular distance from the point to the line)
if c > 0:
height = (2 * area) / c
# Check if the height is within the tolerance distance to the line
if height > tolerance:
return False
# Check if the projection of the point onto the line is within the line segment
dot_product = ((p.x() - p1.x()) * (p2.x() - p1.x()) + (p.y() - p1.y()) * (p2.y() - p1.y())) / (c ** 2)
return 0 <= dot_product <= 1
else:
return None
def viewport_to_local_coord(self, qt_pos : QPoint) -> QPoint:
self.to_quadrant_coords(qt_pos)
return QPoint(self.to_quadrant_coords(qt_pos))
def mousePressEvent(self, event): def mousePressEvent(self, event):
relation = { local_event_pos = self.viewport_to_local_coord(event.pos())
def check_all_points() -> list:
old_points_ui = []
new_points_ui = []
for old_point_ui in self.slv_points_main:
old_points_ui.append(old_point_ui['ui_point'])
for i in range(len(self.slv_points_main) + len(self.slv_lines_main)):
#Iterate though full length because mixed list from SS
entity = self.solv.entity(i)
if entity.is_point_2d() and self.solv.params(entity.params):
x_tbu, y_tbu = self.solv.params(entity.params)
point_solved = QPoint(x_tbu, y_tbu)
new_points_ui.append(point_solved)
# Now we have old_points_ui and new_points_ui, let's compare them
differences = []
if len(old_points_ui) != len(new_points_ui) +1 :
differences.append(f"Length mismatch {len(old_points_ui)} - {len(new_points_ui)}")
return differences
for index, (old_point, new_point) in enumerate(zip(old_points_ui, new_points_ui)):
if old_point != new_point:
differences.append((index, old_point, new_point))
return differences
def update_ui_points(point_list: list):
# Print initial state of slv_points_main
"""print("Initial slv_points_main:", self.slv_points_main)
print("Change list:", point_list)"""
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
"""print("Updating index:", index)
print("Old point:", old_point)
print("New point:", new_point)"""
# Update the point in slv_points_main
self.slv_points_main[index]['ui_point'] = new_point
# Print updated state
#print("Updated slv_points_main:", self.slv_points_main)
"""# UPDATE UI POINTS with solver points
for i in range(len(self.slv_points_main) + len(self.slv_lines_main)):
entity = self.solv.entity(i)
if entity.is_point_2d() and self.solv.params(entity.params):
x_tbu, y_tbu = self.solv.params(entity.params)
point_solved = QPoint(x_tbu, y_tbu)
for point_to_be_updated in self.slv_points_main:
#print("Checking point:", point_to_be_updated)
# if the old ui point is present in the selected constrain line
# update the _ui_point with solver_point
if point_to_be_updated['ui_point'] == target_line_con['ui_points'][0]:
print("that got updated", point_solved)
point_to_be_updated['ui_point'] = point_solved
elif point_to_be_updated['ui_point'] == target_line_con['ui_points'][1]:
print("this got updated", point_solved)
point_to_be_updated['ui_point'] = point_solved
if point_to_be_updated['ui_point'] == target_line_con['ui_points'][0]:
target_line_con['ui_points'][0] = point_solved
elif point_to_be_updated['ui_point'] == target_line_con['ui_points'][1]:
target_line_con['ui_points'][1] = point_solved
#TODO: All points ui and solve into lists and then check before afer for changes
# and update displayed points and then lines"""
def check_all_lines_and_update(changed_points: list):
for tbu_points_idx in changed_points:
index, old_point, new_point = tbu_points_idx
for line_needs_update in self.slv_lines_main:
if old_point == line_needs_update['ui_points'][0]:
line_needs_update['ui_points'][0] = new_point
elif old_point == line_needs_update['ui_points'][1]:
line_needs_update['ui_points'][1] = new_point
relation_point = {
'handle_nr': None, 'handle_nr': None,
'solv_handle': None 'solv_handle': None,
'ui_point': None,
'part_of_entity': None
}
relation_line = {
'handle_nr': None,
'solv_handle': None,
'solv_entity_points': None,
'ui_points': None
} }
if event.button() == Qt.LeftButton and self.mouse_mode == "line": if event.button() == Qt.LeftButton and self.mouse_mode == "line":
clicked_pos = event.pos() clicked_pos = local_event_pos
# Paintline # Paintline
"""self.points.append(clicked_pos) """self.points.append(clicked_pos)
self.update()""" self.update()"""
u = clicked_pos.x() u = clicked_pos.x()
v = clicked_pos.y() v = clicked_pos.y()
point = self.solv.add_point_2d(u, v, self.wp) point = self.solv.add_point_2d(u, v, self.wp)
#print(point)
#self.solv.dragged(point, self.wp) # Track Relationship
# Points
handle_nr = self.get_handle_nr(str(point))
relation_point['handle_nr'] = handle_nr
relation_point['solv_handle'] = point
relation_point['ui_point'] = clicked_pos
# List of points related to the current "figure"
self.points.append(clicked_pos)
# Solverline # Solverline
if self.line_buffer: if self.line_buffer:
line = self.solv.add_line_2d(self.line_buffer, point, self.wp) line = self.solv.add_line_2d(self.line_buffer, point, self.wp)
#print(line) handle_nr_line = self.get_handle_nr(str(line))
relation_line['handle_nr'] = handle_nr_line
relation_line['solv_handle'] = line
relation_line['solv_entity_points'] = (self.line_buffer, point)
relation_line['ui_points'] = [self.points[-2], self.points[-1]]
#track relationship of point in line
relation_point['part_of_entity'] = handle_nr_line
self.slv_lines_main.append(relation_line)
self.slv_points_main.append(relation_point)
self.line_buffer = point self.line_buffer = point
# Track Relationship print("points", self.slv_points_main)
handle_nr = self.get_handle_nr(str(point)) print("lines", self.slv_lines_main)
relation['handle_nr'] = handle_nr
relation['solv_handle'] = point
relation['ui_point'] = clicked_pos
self.solventies.append(relation)
self.points.append(clicked_pos)
print(self.points)
if event.button() == Qt.LeftButton and self.mouse_mode == "pt_pt": if event.button() == Qt.LeftButton and self.mouse_mode == "pt_pt":
#point_solve_now = self.solventies[self.hovered_point]['solv_handle'] point_solve_old = None
point_solve_now = None
for new_id, entry in enumerate(self.solventies):
if self.hovered_point == entry['ui_point']:
point_solve_now = entry['solv_handle']
print(point_solve_now)
target_id = new_id
if self.pt_pt_buffer: if self.pt_pt_buffer:
for old_id, entry in enumerate(self.solventies): for new_id, target_line in enumerate(self.slv_points_main):
if self.pt_pt_buffer == entry['ui_point']: if self.hovered_point == target_line['ui_point']:
point_solve_old = entry['solv_handle'] point_solve_now = target_line['solv_handle']
target_id = new_id
break
else:
point_solve_now = None
for old_id, target_line in enumerate(self.slv_points_main):
if self.pt_pt_buffer == target_line['ui_point']:
point_solve_old = target_line['solv_handle']
move_id = old_id move_id = old_id
break
else:
point_solve_old = None
#point_solve_old = self.solventies[self.pt_pt_buffer]['solv_handle'] if point_solve_old and point_solve_now:
#self.solv.dragged(point_solve_now, self.wp) self.solv.coincident(point_solve_now, point_solve_old, self.wp)
self.solv.coincident(point_solve_now, point_solve_old, self.wp)
if self.solv.solve() == ResultFlag.OKAY: if self.solv.solve() == ResultFlag.OKAY:
# Get the result (unpack from the entity or parameters)
# x and y are actually float type
dof = self.solv.dof()
print(dof)
print(self.solventies[move_id]['ui_point'])
print(self.solventies[target_id]['ui_point'])
x, y = self.solv.params(self.solventies[move_id]['solv_handle'].params)
self.solventies[move_id]['ui_point'] = QPoint(x, y)
print(f"{x}, {y}") """x, y = self.solv.params(self.slv_points_main[move_id]['solv_handle'].params)
print(self.solventies) #after adding the result moved_point = QPoint(x, y)
self.slv_points_main[target_id]['ui_point'] = moved_point
elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE: for ident, lines in enumerate(self.slv_lines_main):
print("Solve_failed - Converge" ) if self.slv_points_main[target_id]['solv_handle'] == lines['solv_entity_points'][0]:
self.slv_lines_main[ident]['ui_points'][0] = moved_point"""
elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS: self.pt_pt_buffer = []
print("Solve_failed - Unknowns" ) self.constrain_done.emit()
#print(dof)
self.update()
elif self.solv.solve() == ResultFlag.INCONSISTENT: elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
print("Solve_failed - Incons" ) print("Solve_failed - Converge" )
self.points = [] elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
for points_ui in self.solventies: print("Solve_failed - Unknowns" )
self.points.append(points_ui['ui_point'])
print(self.points) elif self.solv.solve() == ResultFlag.INCONSISTENT:
print("Solve_failed - Incons" )
self.points = []
#for points_ui in self.slv_points_main:
#self.points.append(points_ui['ui_point'])
print(self.points)
print("Points_all", self.slv_points_main)
self.pt_pt_buffer = self.hovered_point self.pt_pt_buffer = self.hovered_point
self.update() self.update()
if event.button() == Qt.LeftButton and self.mouse_mode == "pt_line":
print("ptline")
line_selected = None
if self.hovered_point:
for nr, entry_point in enumerate(self.slv_points_main):
if self.hovered_point == entry_point['ui_point']:
self.pt_line_buffer = entry_point
self.selected_main_idx = nr
print("Point set", self.pt_line_buffer)
break
if self.pt_line_buffer:
#Line selection target_line to constrain to
for target_line_con in self.slv_lines_main:
if self.is_point_on_line(local_event_pos, target_line_con['ui_points'][0], target_line_con['ui_points'][1]):
line_selected = target_line_con['solv_handle']
print(line_selected.params)
break
#Update UI Line position ot the line of the selected point
for line_nr, move_line in enumerate(self.slv_lines_main):
#Test what point is going to be moved from the line
if move_line['ui_points'][0] == self.pt_line_buffer['ui_point']:
print("On line", line_nr)
idx = 0
break
elif move_line['ui_points'][1] == self.pt_line_buffer['ui_point']:
print("On line", line_nr)
idx = 1
break
# Contrain point to line
if line_selected:
self.solv.coincident(self.pt_line_buffer['solv_handle'], line_selected, self.wp)
print(f"1 : {self.pt_line_buffer['solv_handle']}, 2: {line_selected}")
if self.solv.solve() == ResultFlag.OKAY:
print("Fuck yeah")
x, y = self.solv.params(self.pt_line_buffer['solv_handle'].params)
#Update UI points to returned points from solver
self.slv_points_main[self.selected_main_idx]['ui_point'] = QPoint(x, y)
self.slv_lines_main[line_nr]['ui_points'][idx] = QPoint(x, y)
self.pt_line_buffer = None
self.constrain_done.emit()
elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
print("Solve_failed - Converge")
elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
print("Solve_failed - Unknowns")
elif self.solv.solve() == ResultFlag.INCONSISTENT:
print("Solve_failed - Incons")
if event.button() == Qt.LeftButton and self.mouse_mode == "horiz":
for target_line_con in self.slv_lines_main:
if self.is_point_on_line(local_event_pos, target_line_con['ui_points'][0], target_line_con['ui_points'][1]):
line_selected = target_line_con['solv_handle']
print(line_selected.params)
break
self.solv.horizontal(line_selected, self.wp)
if self.solv.solve() == ResultFlag.OKAY:
print("Fuck yeah")
self.pt_line_buffer = None
self.constrain_done.emit()
elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
print("Solve_failed - Converge")
elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
print("Solve_failed - Unknowns")
elif self.solv.solve() == ResultFlag.INCONSISTENT:
print("Solve_failed - Incons")
if event.button() == Qt.LeftButton and self.mouse_mode == "vert":
for target_line_con in self.slv_lines_main:
if self.is_point_on_line(local_event_pos, target_line_con['ui_points'][0], target_line_con['ui_points'][1]):
line_selected = target_line_con['solv_handle']
print(line_selected.params)
break
self.solv.vertical(line_selected, self.wp)
if self.solv.solve() == ResultFlag.OKAY:
print("Fuck yeah")
self.pt_line_buffer = None
self.constrain_done.emit()
elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
print("Solve_failed - Converge")
elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
print("Solve_failed - Unknowns")
elif self.solv.solve() == ResultFlag.INCONSISTENT:
print("Solve_failed - Incons")
if event.button() == Qt.LeftButton and self.mouse_mode == "distance":
print("distance")
for target_line_con in self.slv_lines_main:
if self.is_point_on_line(local_event_pos, target_line_con['ui_points'][0], target_line_con['ui_points'][1]):
lines_to_cons = target_line_con['solv_entity_points']
break
length, ok = QInputDialog.getDouble(self, 'Distance', 'Enter a mm value:', decimals=2)
e1, e2 = lines_to_cons
self.solv.distance(e1, e2, length, self.wp)
if self.solv.solve() == ResultFlag.OKAY:
print("Fuck yeah")
self.pt_line_buffer = None
self.constrain_done.emit()
elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
print("Solve_failed - Converge")
elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
print("Solve_failed - Unknowns")
elif self.solv.solve() == ResultFlag.INCONSISTENT:
print("Solve_failed - Incons")
points_need_update = check_all_points()
print("This", points_need_update)
update_ui_points(points_need_update)
lines_need_update = check_all_lines_and_update(points_need_update)
print("This", lines_need_update)
dof = self.solv.dof()
print(dof)
self.update()
def mouseMoveEvent(self, event): def mouseMoveEvent(self, event):
local_event_pos = self.viewport_to_local_coord(event.pos())
closest_point = None closest_point = None
min_distance = float('inf') min_distance = float('inf')
threshold = 10 # Distance threshold for highlighting threshold = 10 # Distance threshold for highlighting
for point in self.points: for point in self.slv_points_main:
distance = (event.pos() - point).manhattanLength() distance = (local_event_pos - point['ui_point']).manhattanLength()
if distance < threshold and distance < min_distance: if distance < threshold and distance < min_distance:
closest_point = point closest_point = point['ui_point']
min_distance = distance min_distance = distance
if closest_point != self.hovered_point: if closest_point != self.hovered_point:
self.hovered_point = closest_point self.hovered_point = closest_point
self.update() print(self.hovered_point)
selected_points = []
for dic in self.slv_lines_main:
p1 = dic['ui_points'][0]
p2 = dic['ui_points'][1]
if self.is_point_on_line(local_event_pos, p1, p2):
self.selected_line = p1, p2
break
else:
self.selected_line = None
self.update()
def mouseDoubleClickEvent(self, event): def mouseDoubleClickEvent(self, event):
pass pass
def distance(self, p1, p2): def drawBackgroundGrid(self, painter):
return ((p1.x() - p2.x()) ** 2 + (p1.y() - p2.y()) ** 2) ** 0.5 """Draw a background grid."""
grid_spacing = 50
pen = QPen(QColor(200, 200, 200), 1, Qt.SolidLine)
painter.setPen(pen)
def set_points(self, points: list): # Draw vertical grid lines
self.points = points for x in range(-self.width() // 2, self.width() // 2, grid_spacing):
#self.update() painter.drawLine(x, -self.height() // 2, x, self.height() // 2)
def is_point_on_line(self, p, p1, p2): # Draw horizontal grid lines
distance1 = self.distance(p, p1) for y in range(-self.height() // 2, self.height() // 2, grid_spacing):
distance2 = self.distance(p, p2) painter.drawLine(-self.width() // 2, y, self.width() // 2, y)
total_distance = self.distance(p1, p2)
return abs(distance1 + distance2 - total_distance) < 1 def drawAxes(self, painter):
painter.setRenderHint(QPainter.Antialiasing)
# Set up pen for dashed lines
pen = QPen(Qt.gray, 1, Qt.DashLine)
painter.setPen(pen)
middle_x = self.width() // 2
middle_y = self.height() // 2
# Draw X axis as dashed line
painter.drawLine(0, middle_y, self.width(), middle_y)
# Draw Y axis as dashed line
painter.drawLine(middle_x, 0, middle_x, self.height())
# Draw tick marks
tick_length = 10
tick_spacing = 50
pen = QPen(Qt.gray, 1, Qt.SolidLine)
painter.setPen(pen)
# Draw tick marks on the X axis
for x in range(0, self.width(), tick_spacing):
painter.drawLine(x, middle_y - tick_length // 2, x, middle_y + tick_length // 2)
# Draw tick marks on the Y axis
for y in range(0, self.height(), tick_spacing):
painter.drawLine(middle_x - tick_length // 2, y, middle_x + tick_length // 2, y)
# Draw the origin point in red
painter.setPen(QPen(Qt.red, 4))
painter.drawPoint(middle_x, middle_y)
def to_quadrant_coords(self, point):
"""Translate linear coordinates to quadrant coordinates."""
center_x = self.width() // 2
center_y = self.height() // 2
quadrant_x = point.x() - center_x
quadrant_y = point.y() - center_y
return QPoint(quadrant_x, quadrant_y) / self.zoom
def paintEvent(self, event): def paintEvent(self, event):
painter = QPainter(self) painter = QPainter(self)
#self.drawBackgroundGrid(painter)
self.drawAxes(painter)
# Translate the origin to the center of the widget
center = QPoint(self.width() // 2, self.height() // 2)
painter.translate(center)
# Apply the zoom factor
painter.scale(self.zoom, self.zoom)
# Set the background color # Set the background color
painter.fillRect(self.rect(), QColor('black')) #painter.fillRect(0, self.width(), 0, self.height(), QColor('black'))
# Draw axes
pen = QPen(Qt.gray) pen = QPen(Qt.gray)
pen.setWidth(2) pen.setWidth(2 / self.zoom)
painter.setPen(pen) painter.setPen(pen)
# Draw points # Draw points
for point in self.points: for point in self.slv_points_main:
painter.drawEllipse(point, 3, 3) painter.drawEllipse(point['ui_point'], 3 / self.zoom, 3 / self.zoom)
if len (self.points) > 0: for dic in self.slv_lines_main:
for i in range(len(self.points)-1): painter.drawLine(dic['ui_points'][0] , dic['ui_points'][1])
painter.drawLine(self.points[i], self.points[i + 1])
pen = QPen(Qt.green)
pen.setWidth(2)
painter.setPen(pen)
for i in range(len(self.slv_points_main) + len(self.slv_lines_main)):
entity = self.solv.entity(i)
if entity.is_point_2d() and self.solv.params(entity.params):
x,y = self.solv.params(entity.params)
point = QPoint(x, y)
painter.drawEllipse(point, 6 / self.zoom, 6 / self.zoom)
#Highlight point hovered #Highlight point hovered
if self.hovered_point: if self.hovered_point:
highlight_pen = QPen(QColor(255, 0, 0)) highlight_pen = QPen(QColor(255, 0, 0))
highlight_pen.setWidth(2) highlight_pen.setWidth(2)
painter.setPen(highlight_pen) painter.setPen(highlight_pen)
painter.drawEllipse(self.hovered_point, 5, 5) painter.drawEllipse(self.hovered_point, 5 / self.zoom, 5 / self.zoom)
"""if self.selected_line is not None: if self.selected_line:
p1 = self.points[self.selected_line] p1, p2 = self.selected_line
p2 = self.points[self.selected_line + 1]
painter.setPen(QPen(Qt.red, 2)) painter.setPen(QPen(Qt.red, 2))
painter.drawLine(p1, p2)""" painter.drawLine(p1, p2)
painter.end() painter.end()
def wheelEvent(self, event):
delta = event.angleDelta().y()
self.zoom += (delta / 200) * 0.1
self.update()
def aspect_ratio(self):
return self.width() / self.height() * (1.0 / abs(self.zoom))
def clear_sketch(self): def clear_sketch(self):
self.points = [] self.points = []
self.update() self.update()

View File

@ -28,7 +28,6 @@ class OpenGLWidget(QOpenGLWidget):
return mapped_value return mapped_value
def load_stl(self, filename: str) -> object: def load_stl(self, filename: str) -> object:
try: try:
stl_mesh = mesh.Mesh.from_file(filename) stl_mesh = mesh.Mesh.from_file(filename)
@ -76,12 +75,10 @@ class OpenGLWidget(QOpenGLWidget):
def clear_mesh(self): def clear_mesh(self):
self.mesh_loaded = None self.mesh_loaded = None
def initializeGL(self): def initializeGL(self):
glClearColor(0, 0, 0, 1) glClearColor(0, 0, 0, 1)
glEnable(GL_DEPTH_TEST) glEnable(GL_DEPTH_TEST)
def resizeGL(self, width, height): def resizeGL(self, width, height):
glViewport(0, 0, width, height) glViewport(0, 0, width, height)
glMatrixMode(GL_PROJECTION) glMatrixMode(GL_PROJECTION)

67
gui.ui
View File

@ -331,6 +331,12 @@
<property name="text"> <property name="text">
<string>Rctgl</string> <string>Rctgl</string>
</property> </property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget> </widget>
</item> </item>
<item row="2" column="0" alignment="Qt::AlignTop"> <item row="2" column="0" alignment="Qt::AlignTop">
@ -338,6 +344,12 @@
<property name="text"> <property name="text">
<string>Circle</string> <string>Circle</string>
</property> </property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget> </widget>
</item> </item>
<item row="2" column="1" alignment="Qt::AlignTop"> <item row="2" column="1" alignment="Qt::AlignTop">
@ -345,6 +357,12 @@
<property name="text"> <property name="text">
<string>Slot</string> <string>Slot</string>
</property> </property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
@ -356,7 +374,7 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="autoExclusive"> <property name="autoExclusive">
<bool>true</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
@ -383,6 +401,9 @@
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item row="0" column="0">
@ -393,6 +414,9 @@
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
@ -400,6 +424,12 @@
<property name="text"> <property name="text">
<string>Horiz</string> <string>Horiz</string>
</property> </property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
@ -407,6 +437,41 @@
<property name="text"> <property name="text">
<string>Vert</string> <string>Vert</string>
</property> </property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="pb_con_dist">
<property name="text">
<string>Distnce</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
<property name="autoRepeatDelay">
<number>297</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="pb_con_sym">
<property name="text">
<string>Symetrc</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>

50
main.py
View File

@ -53,12 +53,19 @@ class MainWindow(QMainWindow):
###Modes ###Modes
self.ui.pb_linetool.pressed.connect(self.act_line_mode) 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_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)
### Operations ### Operations
self.ui.pb_extrdop.pressed.connect(self.send_extrude) self.ui.pb_extrdop.pressed.connect(self.send_extrude)
self.ui.pb_cutop.pressed.connect(self.send_cut) self.ui.pb_cutop.pressed.connect(self.send_cut)
self.ui.pb_del_body.pressed.connect(self.del_body) self.ui.pb_del_body.pressed.connect(self.del_body)
self.sketchWidget.constrain_done.connect(self.constrain_finished)
def add_wp_origin(self): def add_wp_origin(self):
#Select orientation #Select orientation
#orientation, ok = Q .getDouble(self, 'Extrude Length', 'Enter a mm value:', decimals=2) #orientation, ok = Q .getDouble(self, 'Extrude Length', 'Enter a mm value:', decimals=2)
@ -70,14 +77,48 @@ class MainWindow(QMainWindow):
#self.sketchWidget.points = [] #self.sketchWidget.points = []
else: else:
self.sketchWidget.mouse_mode = None self.sketchWidget.mouse_mode = None
self.sketchWidget.line_buffer = None
self.sketchWidget.points = []
def act_constrain_pt_pt_mode(self): def act_constrain_pt_pt_mode(self):
if not self.ui.pb_linetool.isChecked(): if not self.ui.pb_linetool.isChecked():
self.sketchWidget.mouse_mode = 'pt_pt' self.sketchWidget.mouse_mode = 'pt_pt'
#self.sketchWidget.points = []
else: else:
self.sketchWidget.mouse_mode = None self.sketchWidget.mouse_mode = None
self.sketchWidget.line_buffer = None
def act_constrain_pt_line_mode(self):
if not self.ui.pb_linetool.isChecked():
self.sketchWidget.mouse_mode = 'pt_line'
else:
self.sketchWidget.mouse_mode = None
self.sketchWidget.line_buffer = 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
self.sketchWidget.line_buffer = 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
self.sketchWidget.line_buffer = None
def act_constrain_distance_mode(self):
if not self.ui.pb_con_vert.isChecked():
self.sketchWidget.mouse_mode = 'distance'
else:
self.sketchWidget.mouse_mode = None
self.sketchWidget.line_buffer = None
def constrain_finished(self):
self.ui.pb_con_ptpt.setChecked(False)
self.ui.pb_con_line.setChecked(False)
self.ui.pb_con_dist.setChecked(False)
def view_update(self): def view_update(self):
print("Update") print("Update")
@ -192,6 +233,9 @@ class MainWindow(QMainWindow):
# UI to mesh # UI to mesh
points = self.translate_points_tup(points) points = self.translate_points_tup(points)
if points[-1] == points[0]:
result = points.pop()
print("removed last point for mesh")
length , ok = QInputDialog.getDouble(self, 'Extrude Length', 'Enter a mm value:', decimals=2) length , ok = QInputDialog.getDouble(self, 'Extrude Length', 'Enter a mm value:', decimals=2)
#TODO : Implement cancel #TODO : Implement cancel
@ -310,4 +354,6 @@ if __name__ == "__main__":
app = QApplication([]) app = QApplication([])
window = MainWindow() window = MainWindow()
window.show() window.show()
app.exec() app.exec()
#pyside6-uic gui.ui > Gui.py -g python