- Sketch projection partly works again :)
This commit is contained in:
parent
a5202e1630
commit
842799b35f
14
doc/flow.md
14
doc/flow.md
@ -19,3 +19,17 @@
|
|||||||
- Transform to 2D xy
|
- Transform to 2D xy
|
||||||
- Transform to linear space for 2D widget to draw.
|
- Transform to linear space for 2D widget to draw.
|
||||||
- Result into 2D cartesian for body interaction extrude etc
|
- Result into 2D cartesian for body interaction extrude etc
|
||||||
|
|
||||||
|
### Elements
|
||||||
|
|
||||||
|
So far these are the elements:
|
||||||
|
|
||||||
|
- Project: Main File
|
||||||
|
- Timeline : Used to track the steps
|
||||||
|
- Assembly: Uses Components and Connectors to from Assemblies
|
||||||
|
- Component: Container for multiple smaller elements "part"
|
||||||
|
- Connector: Preserves connections between parts even if the part in between is deleted
|
||||||
|
- 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.
|
@ -1,7 +1,7 @@
|
|||||||
import math
|
import math
|
||||||
import re
|
import re
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from typing import Optional
|
import uuid
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from PySide6.QtWidgets import QApplication, QWidget, QMessageBox, QInputDialog
|
from PySide6.QtWidgets import QApplication, QWidget, QMessageBox, QInputDialog
|
||||||
@ -32,6 +32,11 @@ class SketchWidget(QWidget):
|
|||||||
|
|
||||||
self.sketch = Sketch2d()
|
self.sketch = Sketch2d()
|
||||||
|
|
||||||
|
def create_sketch(self, sketch_in ):
|
||||||
|
self.sketch = Sketch2d()
|
||||||
|
self.sketch.id = sketch_in.id
|
||||||
|
self.sketch.origin = sketch_in.origin
|
||||||
|
|
||||||
def get_sketch(self):
|
def get_sketch(self):
|
||||||
return self.sketch
|
return self.sketch
|
||||||
|
|
||||||
@ -45,28 +50,38 @@ class SketchWidget(QWidget):
|
|||||||
#self.update()
|
#self.update()
|
||||||
|
|
||||||
def create_workplane(self):
|
def create_workplane(self):
|
||||||
self.sketch.working_plane = self.solv.create_2d_base()
|
self.sketch.wp = self.sketch.create_2d_base()
|
||||||
|
|
||||||
def create_workplane_projected(self):
|
def create_workplane_projected(self):
|
||||||
self.sketch.working_plane = self.solv.create_2d_base()
|
self.sketch.wp = self.sketch.create_2d_base()
|
||||||
|
|
||||||
def convert_proj_points(self):
|
def convert_proj_points(self, proj_points: list):
|
||||||
|
### This needs to create a proper Point2D class with bool construction enbaled
|
||||||
out_points = []
|
out_points = []
|
||||||
for point in self.sketch.proj_points:
|
for point in proj_points:
|
||||||
x, y = point
|
pnt = Point2D(point[0], point[1])
|
||||||
coord = QPoint(x, y)
|
# Construction
|
||||||
out_points.append(coord)
|
pnt.is_helper = True
|
||||||
|
print(point)
|
||||||
|
self.sketch.add_point(pnt)
|
||||||
|
|
||||||
self.sketch.proj_points = out_points
|
def convert_proj_lines(self, proj_lines: list):
|
||||||
|
### same as for point
|
||||||
def convert_proj_lines(self):
|
|
||||||
out_lines = []
|
out_lines = []
|
||||||
for line in self.sketch.proj_lines:
|
for line in proj_lines:
|
||||||
start = QPoint(line[0][0], line[0][1])
|
start = Point2D(line[0][0], line[0][1])
|
||||||
end = QPoint(line[1][0], line[1][1])
|
end = Point2D(line[1][0], line[1][1])
|
||||||
coord = QLine(start, end)
|
start.is_helper = True
|
||||||
out_lines.append(coord)
|
end.is_helper = True
|
||||||
self.sketch.proj_lines = out_lines
|
|
||||||
|
self.sketch.add_point(start)
|
||||||
|
self.sketch.add_point(end)
|
||||||
|
|
||||||
|
lne = Line2D(start, end)
|
||||||
|
|
||||||
|
#Construction
|
||||||
|
lne.is_helper = True
|
||||||
|
self.sketch.add_line(lne)
|
||||||
|
|
||||||
def find_duplicate_points_2d(self, edges):
|
def find_duplicate_points_2d(self, edges):
|
||||||
points = []
|
points = []
|
||||||
@ -114,9 +129,9 @@ class SketchWidget(QWidget):
|
|||||||
origin_handle = self.get_handle_from_ui_point(origin)
|
origin_handle = self.get_handle_from_ui_point(origin)
|
||||||
qw, qx, qy, qz = self.normal_to_quaternion(normal)
|
qw, qx, qy, qz = self.normal_to_quaternion(normal)
|
||||||
|
|
||||||
slv_normal = self.solv.add_normal_3d(qw, qx, qy, qz)
|
slv_normal = self.sketch.add_normal_3d(qw, qx, qy, qz)
|
||||||
self.sketch.working_plane = self.solv.add_work_plane(origin_handle, slv_normal)
|
self.sketch.wp = self.sketch.add_work_plane(origin_handle, slv_normal)
|
||||||
print(self.sketch.working_plane)
|
print(self.sketch.wp)
|
||||||
|
|
||||||
def get_handle_nr(self, input_str: str) -> int:
|
def get_handle_nr(self, input_str: str) -> int:
|
||||||
# Define the regex pattern to extract the handle number
|
# Define the regex pattern to extract the handle number
|
||||||
@ -160,16 +175,16 @@ class SketchWidget(QWidget):
|
|||||||
def get_line_handle_from_ui_point(self, ui_point: QPoint):
|
def get_line_handle_from_ui_point(self, ui_point: QPoint):
|
||||||
"""Input Qpoint that is on a line and you shall receive the handle of the line!"""
|
"""Input Qpoint that is on a line and you shall receive the handle of the line!"""
|
||||||
for target_line_con in self.sketch.lines:
|
for target_line_con in self.sketch.lines:
|
||||||
if self.is_point_on_line(ui_point, target_line_con['ui_points'][0], target_line_con['ui_points'][1]):
|
if self.is_point_on_line(ui_point, target_line_con.crd1.ui_point, target_line_con.crd2.ui_point):
|
||||||
slv_handle = target_line_con['solv_handle']
|
slv_handle = target_line_con.handle
|
||||||
|
|
||||||
return slv_handle
|
return slv_handle
|
||||||
|
|
||||||
def get_point_line_handles_from_ui_point(self, ui_point: QPoint) -> tuple:
|
def get_point_line_handles_from_ui_point(self, ui_point: QPoint) -> tuple:
|
||||||
"""Input Qpoint that is on a line and you shall receive the handles of the points of the line!"""
|
"""Input Qpoint that is on a line and you shall receive the handles of the points of the line!"""
|
||||||
for target_line_con in self.sketch.slv_lines:
|
for target_line_con in self.sketch.slv_lines:
|
||||||
if self.is_point_on_line(ui_point, target_line_con['ui_points'][0], target_line_con['ui_points'][1]):
|
if self.is_point_on_line(ui_point, target_line_con.crd1.ui_point, target_line_con.crd2.ui_point):
|
||||||
lines_to_cons = target_line_con['solv_entity_points']
|
lines_to_cons = target_line_con.crd1.handle, target_line_con.crd2.handle
|
||||||
|
|
||||||
return lines_to_cons
|
return lines_to_cons
|
||||||
|
|
||||||
@ -210,18 +225,22 @@ class SketchWidget(QWidget):
|
|||||||
def viewport_to_local_coord(self, qt_pos : QPoint) -> QPoint:
|
def viewport_to_local_coord(self, qt_pos : QPoint) -> QPoint:
|
||||||
return QPoint(self.to_quadrant_coords(qt_pos))
|
return QPoint(self.to_quadrant_coords(qt_pos))
|
||||||
|
|
||||||
def check_all_points(self,) -> list:
|
def check_all_points(self) -> list:
|
||||||
|
"""
|
||||||
|
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 = []
|
old_points_ui = []
|
||||||
new_points_ui = []
|
new_points_ui = []
|
||||||
|
|
||||||
for old_point_ui in self.sketch.points:
|
for old_point_ui in self.sketch.points:
|
||||||
old_points_ui.append(old_point_ui.ui_point)
|
old_points_ui.append(old_point_ui.ui_point)
|
||||||
|
|
||||||
for i in range(self.solv.entity_len()):
|
for i in range(self.sketch.entity_len()):
|
||||||
# Iterate though full length because mixed list from SS
|
# Iterate though full length because mixed list from SS
|
||||||
entity = self.solv.entity(i)
|
entity = self.sketch.entity(i)
|
||||||
if entity.is_point_2d() and self.solv.params(entity.params):
|
if entity.is_point_2d() and self.sketch.params(entity.params):
|
||||||
x_tbu, y_tbu = self.solv.params(entity.params)
|
x_tbu, y_tbu = self.sketch.params(entity.params)
|
||||||
point_solved = QPoint(x_tbu, y_tbu)
|
point_solved = QPoint(x_tbu, y_tbu)
|
||||||
new_points_ui.append(point_solved)
|
new_points_ui.append(point_solved)
|
||||||
|
|
||||||
@ -248,7 +267,7 @@ class SketchWidget(QWidget):
|
|||||||
index, old_point, new_point = tbu_points_idx
|
index, old_point, new_point = tbu_points_idx
|
||||||
|
|
||||||
# Update the point in slv_points_main
|
# Update the point in slv_points_main
|
||||||
self.sketch.points[index].point = new_point
|
self.sketch.points[index].ui_point = new_point
|
||||||
# Print updated state
|
# Print updated state
|
||||||
# print("Updated slv_points_main:", self.slv_points_main)
|
# print("Updated slv_points_main:", self.slv_points_main)
|
||||||
|
|
||||||
@ -256,10 +275,10 @@ class SketchWidget(QWidget):
|
|||||||
for tbu_points_idx in changed_points:
|
for tbu_points_idx in changed_points:
|
||||||
index, old_point, new_point = tbu_points_idx
|
index, old_point, new_point = tbu_points_idx
|
||||||
for line_needs_update in self.sketch.lines:
|
for line_needs_update in self.sketch.lines:
|
||||||
if old_point == line_needs_update.points[0]:
|
if old_point == line_needs_update.crd1.ui_point:
|
||||||
line_needs_update['ui_points'][0] = new_point
|
line_needs_update.crd1.ui_point = new_point
|
||||||
elif old_point == line_needs_update.points[1]:
|
elif old_point == line_needs_update.crd2.ui_point:
|
||||||
line_needs_update['ui_points'][1] = new_point
|
line_needs_update.crd2.ui_point = new_point
|
||||||
|
|
||||||
def mouseReleaseEvent(self, event):
|
def mouseReleaseEvent(self, event):
|
||||||
local_event_pos = self.viewport_to_local_coord(event.pos())
|
local_event_pos = self.viewport_to_local_coord(event.pos())
|
||||||
@ -345,19 +364,20 @@ class SketchWidget(QWidget):
|
|||||||
if self.main_buffer[0] and self.main_buffer[1]:
|
if self.main_buffer[0] and self.main_buffer[1]:
|
||||||
print("buf", self.main_buffer)
|
print("buf", self.main_buffer)
|
||||||
|
|
||||||
self.solv.coincident(self.main_buffer[0], self.main_buffer[1], self.sketch.working_plane)
|
self.sketch.coincident(self.main_buffer[0], self.main_buffer[1], self.sketch.wp)
|
||||||
|
|
||||||
if self.solv.solve() == ResultFlag.OKAY:
|
if self.sketch.solve() == ResultFlag.OKAY:
|
||||||
print("Fuck yeah")
|
print("Fuck yeah")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
|
elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE:
|
||||||
print("Solve_failed - Converge")
|
print("Solve_failed - Converge")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
|
elif self.sketch.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
|
||||||
print("Solve_failed - Unknowns")
|
print("Solve_failed - Unknowns")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.INCONSISTENT:
|
elif self.sketch.solve() == ResultFlag.INCONSISTENT:
|
||||||
print("Solve_failed - Incons")
|
print("Solve_failed - Incons")
|
||||||
|
|
||||||
self.constrain_done.emit()
|
self.constrain_done.emit()
|
||||||
self.main_buffer = [None, None]
|
self.main_buffer = [None, None]
|
||||||
|
|
||||||
@ -373,19 +393,19 @@ class SketchWidget(QWidget):
|
|||||||
|
|
||||||
# Contrain point to line
|
# Contrain point to line
|
||||||
if self.main_buffer[1]:
|
if self.main_buffer[1]:
|
||||||
self.solv.coincident(self.main_buffer[0], self.main_buffer[1], self.sketch.working_plane)
|
self.sketch.coincident(self.main_buffer[0], self.main_buffer[1], self.sketch.wp)
|
||||||
|
|
||||||
if self.solv.solve() == ResultFlag.OKAY:
|
if self.sketch.solve() == ResultFlag.OKAY:
|
||||||
print("Fuck yeah")
|
print("Fuck yeah")
|
||||||
self.constrain_done.emit()
|
self.constrain_done.emit()
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
|
elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE:
|
||||||
print("Solve_failed - Converge")
|
print("Solve_failed - Converge")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
|
elif self.sketch.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
|
||||||
print("Solve_failed - Unknowns")
|
print("Solve_failed - Unknowns")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.INCONSISTENT:
|
elif self.sketch.solve() == ResultFlag.INCONSISTENT:
|
||||||
print("Solve_failed - Incons")
|
print("Solve_failed - Incons")
|
||||||
|
|
||||||
self.constrain_done.emit()
|
self.constrain_done.emit()
|
||||||
@ -404,18 +424,18 @@ class SketchWidget(QWidget):
|
|||||||
|
|
||||||
# Contrain point to line
|
# Contrain point to line
|
||||||
if self.main_buffer[1]:
|
if self.main_buffer[1]:
|
||||||
self.solv.midpoint(self.main_buffer[0], self.main_buffer[1], self.sketch.working_plane)
|
self.sketch.midpoint(self.main_buffer[0], self.main_buffer[1], self.sketch.wp)
|
||||||
|
|
||||||
if self.solv.solve() == ResultFlag.OKAY:
|
if self.sketch.solve() == ResultFlag.OKAY:
|
||||||
print("Fuck yeah")
|
print("Fuck yeah")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
|
elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE:
|
||||||
print("Solve_failed - Converge")
|
print("Solve_failed - Converge")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
|
elif self.sketch.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
|
||||||
print("Solve_failed - Unknowns")
|
print("Solve_failed - Unknowns")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.INCONSISTENT:
|
elif self.sketch.solve() == ResultFlag.INCONSISTENT:
|
||||||
print("Solve_failed - Incons")
|
print("Solve_failed - Incons")
|
||||||
self.constrain_done.emit()
|
self.constrain_done.emit()
|
||||||
|
|
||||||
@ -426,36 +446,36 @@ class SketchWidget(QWidget):
|
|||||||
line_selected = self.get_line_handle_from_ui_point(local_event_pos)
|
line_selected = self.get_line_handle_from_ui_point(local_event_pos)
|
||||||
|
|
||||||
if line_selected:
|
if line_selected:
|
||||||
self.solv.horizontal(line_selected, self.sketch.working_plane)
|
self.sketch.horizontal(line_selected, self.sketch.wp)
|
||||||
|
|
||||||
if self.solv.solve() == ResultFlag.OKAY:
|
if self.sketch.solve() == ResultFlag.OKAY:
|
||||||
print("Fuck yeah")
|
print("Fuck yeah")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
|
elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE:
|
||||||
print("Solve_failed - Converge")
|
print("Solve_failed - Converge")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
|
elif self.sketch.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
|
||||||
print("Solve_failed - Unknowns")
|
print("Solve_failed - Unknowns")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.INCONSISTENT:
|
elif self.sketch.solve() == ResultFlag.INCONSISTENT:
|
||||||
print("Solve_failed - Incons")
|
print("Solve_failed - Incons")
|
||||||
|
|
||||||
if event.button() == Qt.LeftButton and self.mouse_mode == "vert":
|
if event.button() == Qt.LeftButton and self.mouse_mode == "vert":
|
||||||
line_selected = self.get_line_handle_from_ui_point(local_event_pos)
|
line_selected = self.get_line_handle_from_ui_point(local_event_pos)
|
||||||
|
|
||||||
if line_selected:
|
if line_selected:
|
||||||
self.solv.vertical(line_selected, self.sketch.working_plane)
|
self.sketch.vertical(line_selected, self.sketch.wp)
|
||||||
|
|
||||||
if self.solv.solve() == ResultFlag.OKAY:
|
if self.sketch.solve() == ResultFlag.OKAY:
|
||||||
print("Fuck yeah")
|
print("Fuck yeah")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
|
elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE:
|
||||||
print("Solve_failed - Converge")
|
print("Solve_failed - Converge")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
|
elif self.sketch.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
|
||||||
print("Solve_failed - Unknowns")
|
print("Solve_failed - Unknowns")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.INCONSISTENT:
|
elif self.sketch.solve() == ResultFlag.INCONSISTENT:
|
||||||
print("Solve_failed - Incons")
|
print("Solve_failed - Incons")
|
||||||
|
|
||||||
if event.button() == Qt.LeftButton and self.mouse_mode == "distance":
|
if event.button() == Qt.LeftButton and self.mouse_mode == "distance":
|
||||||
@ -485,18 +505,18 @@ class SketchWidget(QWidget):
|
|||||||
if e1 and e2:
|
if e1 and e2:
|
||||||
# Ask fo the dimension and solve if both elements are present
|
# Ask fo the dimension and solve if both elements are present
|
||||||
length, ok = QInputDialog.getDouble(self, 'Distance', 'Enter a mm value:', value=100, decimals=2)
|
length, ok = QInputDialog.getDouble(self, 'Distance', 'Enter a mm value:', value=100, decimals=2)
|
||||||
self.solv.distance(e1, e2, length, self.sketch.working_plane)
|
self.sketch.distance(e1, e2, length, self.sketch.wp)
|
||||||
|
|
||||||
if self.solv.solve() == ResultFlag.OKAY:
|
if self.sketch.solve() == ResultFlag.OKAY:
|
||||||
print("Fuck yeah")
|
print("Fuck yeah")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
|
elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE:
|
||||||
print("Solve_failed - Converge")
|
print("Solve_failed - Converge")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
|
elif self.sketch.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
|
||||||
print("Solve_failed - Unknowns")
|
print("Solve_failed - Unknowns")
|
||||||
|
|
||||||
elif self.solv.solve() == ResultFlag.INCONSISTENT:
|
elif self.sketch.solve() == ResultFlag.INCONSISTENT:
|
||||||
print("Solve_failed - Incons")
|
print("Solve_failed - Incons")
|
||||||
|
|
||||||
self.constrain_done.emit()
|
self.constrain_done.emit()
|
||||||
@ -661,33 +681,53 @@ class SketchWidget(QWidget):
|
|||||||
# Set the transform to the painter
|
# Set the transform to the painter
|
||||||
painter.setTransform(transform)
|
painter.setTransform(transform)
|
||||||
|
|
||||||
pen = QPen(Qt.gray)
|
pen_normal = QPen(Qt.gray)
|
||||||
pen.setWidthF(2 / self.zoom)
|
pen_normal.setWidthF(2 / self.zoom)
|
||||||
painter.setPen(pen)
|
|
||||||
|
pen_construct = QPen(Qt.blue)
|
||||||
|
pen_construct.setStyle(Qt.PenStyle.DashLine)
|
||||||
|
pen_construct.setWidthF(2 / self.zoom)
|
||||||
|
|
||||||
|
pen_solver = QPen(Qt.green)
|
||||||
|
pen_solver.setWidthF(2 / self.zoom)
|
||||||
|
|
||||||
# Draw points
|
# Draw points
|
||||||
if self.sketch:
|
if self.sketch:
|
||||||
|
painter.setPen(pen_normal)
|
||||||
for point in self.sketch.points:
|
for point in self.sketch.points:
|
||||||
painter.drawEllipse(point.ui_point, 3 / self.zoom, 3 / self.zoom)
|
if point.is_helper:
|
||||||
|
painter.setPen(pen_construct)
|
||||||
|
painter.drawEllipse(point.ui_point, 10 / self.zoom, 10 / self.zoom)
|
||||||
|
else:
|
||||||
|
#Normal point
|
||||||
|
painter.setPen(pen_normal)
|
||||||
|
painter.drawEllipse(point.ui_point, 3 / self.zoom, 3 / self.zoom)
|
||||||
|
|
||||||
|
|
||||||
for line in self.sketch.lines:
|
for line in self.sketch.lines:
|
||||||
p1 = line.crd1.ui_point
|
if line.is_helper:
|
||||||
p2 = line.crd2.ui_point
|
painter.setPen(pen_construct)
|
||||||
painter.drawLine(p1, p2)
|
p1 = line.crd1.ui_point
|
||||||
|
p2 = line.crd2.ui_point
|
||||||
|
painter.drawLine(p1, p2)
|
||||||
|
else:
|
||||||
|
painter.setPen(pen_normal)
|
||||||
|
p1 = line.crd1.ui_point
|
||||||
|
p2 = line.crd2.ui_point
|
||||||
|
painter.drawLine(p1, p2)
|
||||||
|
|
||||||
dis = self.distance(p1, p2)
|
dis = self.distance(p1, p2)
|
||||||
mid = self.calculate_midpoint(p1, p2)
|
mid = self.calculate_midpoint(p1, p2)
|
||||||
painter.drawText(mid, str(round(dis, 2)))
|
painter.drawText(mid, str(round(dis, 2)))
|
||||||
|
|
||||||
pen = QPen(Qt.green)
|
|
||||||
pen.setWidthF(2 / self.zoom)
|
|
||||||
painter.setPen(pen)
|
|
||||||
|
|
||||||
if self.solv.entity_len():
|
# Draw all solver points
|
||||||
for i in range(self.solv.entity_len()):
|
if self.sketch.entity_len():
|
||||||
entity = self.solv.entity(i)
|
painter.setPen(pen_solver)
|
||||||
if entity.is_point_2d() and self.solv.params(entity.params):
|
for i in range(self.sketch.entity_len()):
|
||||||
x, y = self.solv.params(entity.params)
|
entity = self.sketch.entity(i)
|
||||||
|
if entity.is_point_2d() and self.sketch.params(entity.params):
|
||||||
|
x, y = self.sketch.params(entity.params)
|
||||||
point = QPointF(x, y)
|
point = QPointF(x, y)
|
||||||
painter.drawEllipse(point, 6 / self.zoom, 6 / self.zoom)
|
painter.drawEllipse(point, 6 / self.zoom, 6 / self.zoom)
|
||||||
|
|
||||||
@ -723,149 +763,30 @@ class SketchWidget(QWidget):
|
|||||||
return self.width() / self.height() * (1.0 / abs(self.zoom))
|
return self.width() / self.height() * (1.0 / abs(self.zoom))
|
||||||
|
|
||||||
|
|
||||||
class Point2D_ALT:
|
### GEOMETRY CLASSES
|
||||||
"""Improved oop aaproach?"""
|
|
||||||
def __init__(self):
|
|
||||||
self.ui_point = None
|
|
||||||
self.solve_handle_nr = None
|
|
||||||
self.solve_handle = None
|
|
||||||
self.part_of_entity = None
|
|
||||||
|
|
||||||
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 = center_y - point.y() # Note the change here
|
|
||||||
|
|
||||||
return QPoint(quadrant_x, quadrant_y) / self.zoom
|
|
||||||
|
|
||||||
def from_quadrant_coords(self, point: QPoint):
|
|
||||||
"""Translate quadrant coordinates to linear coordinates."""
|
|
||||||
center_x = self.width() // 2
|
|
||||||
center_y = self.height() // 2
|
|
||||||
widget_x = center_x + point.x() * self.zoom
|
|
||||||
widget_y = center_y - point.y() * self.zoom # Note the subtraction here
|
|
||||||
|
|
||||||
return QPoint(int(widget_x), int(widget_y))
|
|
||||||
|
|
||||||
def from_quadrant_coords_no_center(self, point):
|
|
||||||
"""Invert Y Coordinate for mesh"""
|
|
||||||
center_x = 0
|
|
||||||
center_y = 0
|
|
||||||
widget_x = point.x()
|
|
||||||
widget_y = -point.y()
|
|
||||||
|
|
||||||
return QPoint(int(widget_x), int(widget_y))
|
|
||||||
|
|
||||||
def get_handle_nr(self, input_str: str) -> int:
|
|
||||||
# Define the regex pattern to extract the handle number
|
|
||||||
pattern = r"handle=(\d+)"
|
|
||||||
|
|
||||||
# Use re.search to find the handle number in the string
|
|
||||||
match = re.search(pattern, input_str)
|
|
||||||
|
|
||||||
if match:
|
|
||||||
handle_number = int(match.group(1))
|
|
||||||
print(f"Handle number: {handle_number}")
|
|
||||||
return int(handle_number)
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("Handle number not found.")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def get_keys(self, d: dict, target: QPoint) -> list:
|
|
||||||
result = []
|
|
||||||
path = []
|
|
||||||
print(d)
|
|
||||||
print(target)
|
|
||||||
for k, v in d.items():
|
|
||||||
path.append(k)
|
|
||||||
if isinstance(v, dict):
|
|
||||||
self.get_keys(v, target)
|
|
||||||
if v == target:
|
|
||||||
result.append(copy(path))
|
|
||||||
path.pop()
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def get_handle_from_ui_point(self, ui_point: QPoint):
|
|
||||||
"""Input QPoint and you shall reveive a slvs entity handle!"""
|
|
||||||
for point in self.sketch.slv_points:
|
|
||||||
if ui_point == point['ui_point']:
|
|
||||||
slv_handle = point['solv_handle']
|
|
||||||
|
|
||||||
return slv_handle
|
|
||||||
|
|
||||||
def get_line_handle_from_ui_point(self, ui_point: QPoint):
|
|
||||||
"""Input Qpoint that is on a line and you shall receive the handle of the line!"""
|
|
||||||
for target_line_con in self.sketch.slv_lines:
|
|
||||||
if self.is_point_on_line(ui_point, target_line_con['ui_points'][0], target_line_con['ui_points'][1]):
|
|
||||||
slv_handle = target_line_con['solv_handle']
|
|
||||||
|
|
||||||
return slv_handle
|
|
||||||
|
|
||||||
def get_point_line_handles_from_ui_point(self, ui_point: QPoint) -> tuple:
|
|
||||||
"""Input Qpoint that is on a line and you shall receive the handles of the points of the line!"""
|
|
||||||
for target_line_con in self.sketch.slv_lines:
|
|
||||||
if self.is_point_on_line(ui_point, target_line_con['ui_points'][0], target_line_con['ui_points'][1]):
|
|
||||||
lines_to_cons = target_line_con['solv_entity_points']
|
|
||||||
|
|
||||||
return lines_to_cons
|
|
||||||
|
|
||||||
def distance(self, p1, p2):
|
|
||||||
return math.sqrt((p1.x() - p2.x())**2 + (p1.y() - p2.y())**2)
|
|
||||||
|
|
||||||
def calculate_midpoint(self, point1, point2):
|
|
||||||
mx = (point1.x() + point2.x()) // 2
|
|
||||||
my = (point1.y() + point2.y()) // 2
|
|
||||||
return QPoint(mx, my)
|
|
||||||
|
|
||||||
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:
|
|
||||||
return QPoint(self.to_quadrant_coords(qt_pos))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Point2D:
|
class Point2D:
|
||||||
def __init__(self, x, y):
|
def __init__(self, x, y):
|
||||||
|
self.id = None
|
||||||
self.ui_x: int = x
|
self.ui_x: int = x
|
||||||
self.ui_y: int = y
|
self.ui_y: int = y
|
||||||
self.ui_point = QPoint(self.ui_x, self.ui_y)
|
self.ui_point = QPoint(self.ui_x, self.ui_y)
|
||||||
self.handle = None
|
self.handle = None
|
||||||
self.handle_nr: int = None
|
self.handle_nr: int = None
|
||||||
|
|
||||||
|
# Construction Geometry
|
||||||
|
self.is_helper: bool = False
|
||||||
|
|
||||||
class Line2D:
|
class Line2D:
|
||||||
def __init__(self, point_s: Point2D, point_e: Point2D):
|
def __init__(self, point_s: Point2D, point_e: Point2D):
|
||||||
|
self.id = None
|
||||||
|
|
||||||
self.crd1: Point2D = point_s
|
self.crd1: Point2D = point_s
|
||||||
self.crd2: Point2D = point_e
|
self.crd2: Point2D = point_e
|
||||||
self.handle = None
|
self.handle = None
|
||||||
self.handle_nr = None
|
self.handle_nr: int = None
|
||||||
|
|
||||||
|
# Construction Geometry
|
||||||
|
self.is_helper: bool = False
|
||||||
|
|
||||||
class Sketch2d(SolverSystem):
|
class Sketch2d(SolverSystem):
|
||||||
"""
|
"""
|
||||||
@ -873,13 +794,16 @@ class Sketch2d(SolverSystem):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.id = uuid.uuid1()
|
||||||
|
|
||||||
self.wp = self.create_2d_base()
|
self.wp = self.create_2d_base()
|
||||||
self.points = []
|
self.points = []
|
||||||
self.lines = []
|
self.lines = []
|
||||||
|
self.origin = [0,0,0]
|
||||||
|
|
||||||
def add_point(self, point: Point2D):
|
def add_point(self, point: Point2D):
|
||||||
"""
|
"""
|
||||||
Adds a point into the solversystem and reurns the handle.
|
Adds a point into the solversystem and returns the handle.
|
||||||
Appends the added point to the points list.
|
Appends the added point to the points list.
|
||||||
:param point: 2D point in Point2D class format
|
:param point: 2D point in Point2D class format
|
||||||
:return:
|
:return:
|
||||||
@ -887,6 +811,7 @@ class Sketch2d(SolverSystem):
|
|||||||
|
|
||||||
point.handle = self.add_point_2d(point.ui_x, point.ui_y, self.wp)
|
point.handle = self.add_point_2d(point.ui_x, point.ui_y, self.wp)
|
||||||
point.handle_nr = self.get_handle_nr(str(point.handle))
|
point.handle_nr = self.get_handle_nr(str(point.handle))
|
||||||
|
point.id = uuid.uuid1()
|
||||||
|
|
||||||
self.points.append(point)
|
self.points.append(point)
|
||||||
|
|
||||||
@ -899,6 +824,8 @@ class Sketch2d(SolverSystem):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
line.id = uuid.uuid1()
|
||||||
|
|
||||||
line.handle = self.add_line_2d(line.crd1.handle, line.crd2.handle, self.wp)
|
line.handle = self.add_line_2d(line.crd1.handle, line.crd2.handle, self.wp)
|
||||||
line.handle_nr = self.get_handle_nr(str(line.handle))
|
line.handle_nr = self.get_handle_nr(str(line.handle))
|
||||||
|
|
||||||
|
69
main.py
69
main.py
@ -49,7 +49,6 @@ class ExtrudeDialog(QDialog):
|
|||||||
self.rounded_checkbox = QCheckBox('Round Edges')
|
self.rounded_checkbox = QCheckBox('Round Edges')
|
||||||
self.seperator = create_hline()
|
self.seperator = create_hline()
|
||||||
|
|
||||||
|
|
||||||
# OK and Cancel buttons
|
# OK and Cancel buttons
|
||||||
button_layout = QHBoxLayout()
|
button_layout = QHBoxLayout()
|
||||||
ok_button = QPushButton('OK')
|
ok_button = QPushButton('OK')
|
||||||
@ -263,14 +262,12 @@ class MainWindow(QMainWindow):
|
|||||||
sketch = Sketch()
|
sketch = Sketch()
|
||||||
sketch.id = name
|
sketch.id = name
|
||||||
sketch.origin = [0,0,0]
|
sketch.origin = [0,0,0]
|
||||||
sketch.slv_points = []
|
|
||||||
sketch.slv_lines = []
|
|
||||||
sketch.proj_points = []
|
|
||||||
sketch.proj_lines = []
|
|
||||||
self.sketchWidget.reset_buffers()
|
self.sketchWidget.reset_buffers()
|
||||||
self.sketchWidget.set_sketch(sketch)
|
self.sketchWidget.create_sketch(sketch)
|
||||||
|
|
||||||
def add_new_sketch_wp(self):
|
def add_new_sketch_wp(self):
|
||||||
|
## Sketch projected from 3d view into 2d
|
||||||
name = f"sketches-{str(names.get_first_name())}"
|
name = f"sketches-{str(names.get_first_name())}"
|
||||||
sketch = Sketch()
|
sketch = Sketch()
|
||||||
sketch.id = name
|
sketch.id = name
|
||||||
@ -280,11 +277,13 @@ class MainWindow(QMainWindow):
|
|||||||
sketch.slv_lines = []
|
sketch.slv_lines = []
|
||||||
sketch.proj_points = self.custom_3D_Widget.project_tosketch_points
|
sketch.proj_points = self.custom_3D_Widget.project_tosketch_points
|
||||||
sketch.proj_lines = self.custom_3D_Widget.project_tosketch_lines
|
sketch.proj_lines = self.custom_3D_Widget.project_tosketch_lines
|
||||||
|
|
||||||
self.sketchWidget.reset_buffers()
|
self.sketchWidget.reset_buffers()
|
||||||
self.sketchWidget.set_sketch(sketch)
|
self.sketchWidget.create_sketch(sketch)
|
||||||
self.sketchWidget.create_workplane_projected()
|
self.sketchWidget.create_workplane_projected()
|
||||||
self.sketchWidget.convert_proj_points()
|
if not sketch.proj_lines:
|
||||||
self.sketchWidget.convert_proj_lines()
|
self.sketchWidget.convert_proj_points(sketch.proj_points)
|
||||||
|
self.sketchWidget.convert_proj_lines(sketch.proj_lines)
|
||||||
self.sketchWidget.update()
|
self.sketchWidget.update()
|
||||||
|
|
||||||
# CLear all selections after it has been projected
|
# CLear all selections after it has been projected
|
||||||
@ -295,15 +294,24 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def add_sketch(self):
|
def add_sketch(self):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
sketch = self.sketchWidget.get_sketch()
|
sketch = Sketch()
|
||||||
sketch.convert_points_for_sdf()
|
sketch_from_widget = self.sketchWidget.get_sketch()
|
||||||
|
points = sketch_from_widget.points
|
||||||
|
|
||||||
|
sketch.convert_points_for_sdf(points)
|
||||||
|
sketch.id = sketch_from_widget.id
|
||||||
|
|
||||||
|
sketch.filter_lines_for_interactor(sketch_from_widget.lines)
|
||||||
|
|
||||||
|
# Register sketch to timeline
|
||||||
self.project.timeline[-1].sketches[sketch.id] = sketch
|
self.project.timeline[-1].sketches[sketch.id] = sketch
|
||||||
|
|
||||||
|
# Add Item to slection menu
|
||||||
self.ui.sketch_list.addItem(sketch.id)
|
self.ui.sketch_list.addItem(sketch.id)
|
||||||
|
|
||||||
|
# Deactivate drawing
|
||||||
self.ui.pb_linetool.setChecked(False)
|
self.ui.pb_linetool.setChecked(False)
|
||||||
self.sketchWidget.line_mode = False
|
self.sketchWidget.line_mode = False
|
||||||
|
|
||||||
@ -387,7 +395,9 @@ class MainWindow(QMainWindow):
|
|||||||
name = selected.text()
|
name = selected.text()
|
||||||
# TODO: add selected element from timeline
|
# TODO: add selected element from timeline
|
||||||
sel_compo = self.project.timeline[-1]
|
sel_compo = self.project.timeline[-1]
|
||||||
|
print(sel_compo)
|
||||||
sketch = sel_compo.sketches[name]
|
sketch = sel_compo.sketches[name]
|
||||||
|
print(sketch)
|
||||||
points = sketch.sdf_points
|
points = sketch.sdf_points
|
||||||
|
|
||||||
if points[-1] == points[0]:
|
if points[-1] == points[0]:
|
||||||
@ -428,7 +438,7 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
### Interactor
|
### Interactor
|
||||||
interactor = Interactor()
|
interactor = Interactor()
|
||||||
interactor.add_lines_for_interactor(sketch.slv_lines)
|
interactor.add_lines_for_interactor(sketch.interactor_lines)
|
||||||
|
|
||||||
if not invert:
|
if not invert:
|
||||||
edges = interactor_mesh.generate_mesh(interactor.lines, 0, length)
|
edges = interactor_mesh.generate_mesh(interactor.lines, 0, length)
|
||||||
@ -564,6 +574,8 @@ class Sketch:
|
|||||||
|
|
||||||
sdf_points: list = None
|
sdf_points: list = None
|
||||||
|
|
||||||
|
interactor_lines: list = None
|
||||||
|
|
||||||
# Points coming back from the 3D-Widget as projection to draw on
|
# Points coming back from the 3D-Widget as projection to draw on
|
||||||
proj_points: list = None
|
proj_points: list = None
|
||||||
proj_lines: list = None
|
proj_lines: list = None
|
||||||
@ -623,13 +635,24 @@ class Sketch:
|
|||||||
print("p2", p2)
|
print("p2", p2)
|
||||||
return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
|
return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
|
||||||
|
|
||||||
def convert_points_for_sdf(self):
|
def convert_points_for_sdf(self, points):
|
||||||
points_for_sdf = []
|
points_for_sdf = []
|
||||||
for point_to_poly in self.slv_points:
|
for point in points:
|
||||||
points_for_sdf.append(self.translate_points_tup(point_to_poly['ui_point']))
|
if point.is_helper is False:
|
||||||
|
print("point", point)
|
||||||
|
points_for_sdf.append(self.translate_points_tup(point.ui_point))
|
||||||
|
|
||||||
self.sdf_points = points_for_sdf
|
self.sdf_points = points_for_sdf
|
||||||
|
|
||||||
|
def filter_lines_for_interactor(self, lines):
|
||||||
|
### Filter lines that are not meant to be drawn for the interactor like contruction lines
|
||||||
|
filtered_lines = []
|
||||||
|
for line in lines:
|
||||||
|
if not line.is_helper:
|
||||||
|
filtered_lines.append(line)
|
||||||
|
|
||||||
|
self.interactor_lines = filtered_lines
|
||||||
|
|
||||||
def extrude(self, height: float, symet: bool = True, invert: bool = False, offset_length: float = None):
|
def extrude(self, height: float, symet: bool = True, invert: bool = False, offset_length: float = None):
|
||||||
"""
|
"""
|
||||||
Extrude a 2D shape into 3D, orient it along the normal, and position it relative to the centroid.
|
Extrude a 2D shape into 3D, orient it along the normal, and position it relative to the centroid.
|
||||||
@ -677,7 +700,6 @@ class Sketch:
|
|||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Interactor:
|
class Interactor:
|
||||||
"""Helper mesh consisting of edges for selection"""
|
"""Helper mesh consisting of edges for selection"""
|
||||||
@ -708,12 +730,13 @@ class Interactor:
|
|||||||
return translation_along_normal
|
return translation_along_normal
|
||||||
|
|
||||||
def add_lines_for_interactor(self, input_lines: list):
|
def add_lines_for_interactor(self, input_lines: list):
|
||||||
"""Expects slvs_lines main list"""
|
"""Takes Line2D objects from the sketch widget and preparesit for interactor mesh.
|
||||||
|
Translates coordinates."""
|
||||||
|
|
||||||
points_for_interact = []
|
points_for_interact = []
|
||||||
for point_to_poly in input_lines:
|
for point_to_poly in input_lines:
|
||||||
start, end = point_to_poly['ui_points']
|
from_coord_start = window.sketchWidget.from_quadrant_coords_no_center(point_to_poly.crd1.ui_point)
|
||||||
from_coord_start = window.sketchWidget.from_quadrant_coords_no_center(start)
|
from_coord_end = window.sketchWidget.from_quadrant_coords_no_center(point_to_poly.crd2.ui_point)
|
||||||
from_coord_end = window.sketchWidget.from_quadrant_coords_no_center(end)
|
|
||||||
start_draw = self.translate_points_tup(from_coord_start)
|
start_draw = self.translate_points_tup(from_coord_start)
|
||||||
end_draw = self.translate_points_tup(from_coord_end)
|
end_draw = self.translate_points_tup(from_coord_end)
|
||||||
line = start_draw, end_draw
|
line = start_draw, end_draw
|
||||||
@ -723,7 +746,6 @@ class Interactor:
|
|||||||
|
|
||||||
self.lines = points_for_interact
|
self.lines = points_for_interact
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Body:
|
class Body:
|
||||||
"""The actual body as sdf3 object"""
|
"""The actual body as sdf3 object"""
|
||||||
@ -743,7 +765,6 @@ class Body:
|
|||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
|
||||||
class Output:
|
class Output:
|
||||||
def export_mesh(self, sdf_object):
|
def export_mesh(self, sdf_object):
|
||||||
"""FINAL EXPORT"""
|
"""FINAL EXPORT"""
|
||||||
@ -763,13 +784,11 @@ class Output:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error executing code:", e)
|
print("Error executing code:", e)
|
||||||
|
|
||||||
|
|
||||||
class Project:
|
class Project:
|
||||||
"""Project -> Timeline -> Component -> Sketch -> Body / Interactor -> Connector -> Assembly -> PB Render"""
|
"""Project -> Timeline -> Component -> Sketch -> Body / Interactor -> Connector -> Assembly -> PB Render"""
|
||||||
timeline: Timeline = None
|
timeline: Timeline = None
|
||||||
assembly: Assembly = None
|
assembly: Assembly = None
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
window = MainWindow()
|
window = MainWindow()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user