🐛 fix(.gitignore): add test.py to the list of ignored files

 feat(data_processing.py): remove deprecated get_discrete_vertices2 method and rename get_discrete_vertices3 to get_discrete_vertices. Simplify get_discrete_vertices method by removing the selection of the discretisation method from the settings and always using the Z0-Zi < DeltaZ method. This improves code readability and maintainability.
 feat(input.py): remove deprecated result_file_path and bruteforce_discretization_result attributes from ScannedObject class. Add old_delta, old_discrete, and old_discrete_type attributes to cache the results of the get_discrete_vertices method. This improves performance by avoiding unnecessary recomputations of the discretized vertices.
 feat(MainWindow.py): add support for selecting a layer to display discrete graphs for. Add two new graph types: "Coupe de la couche" and "Difference entre le rayon de chaque points

🔧 fix(ui): change tab index to display the correct tab on startup
 feat(ui): add label and combobox to select layer to display
🔧 fix(worker): add discretisation_value parameter to PreProcessWorker constructor
 feat(math): add get_true_teta_from_x_y, get_difference_from_mean_value, and get_distance_between_two_vertices functions
The UI fix changes the tab index to display the correct tab on startup. The new label and combobox allow the user to select the layer to display. The worker fix adds a discretisation_value parameter to the PreProcessWorker constructor. The new math functions are get_true_teta_from_x_y, get_difference_from_mean_value, and get_distance_between_two_vertices. These functions are useful for calculating teta, differences from mean values, and distances between vertices.
This commit is contained in:
Djalim Simaila 2023-05-03 17:26:11 +02:00
parent 78ab03e9d8
commit 8175561250
8 changed files with 182 additions and 87 deletions

1
.gitignore vendored
View File

@ -163,3 +163,4 @@ cython_debug/
*.DS_Store
.vscode
test.py

View File

@ -38,14 +38,8 @@ def get_raw_data(obj:ScannedObject, ndigits:int,delta_z:float=1,update_progress_
for colone in colones:
data[colone] = []
# Select the discretisation method from the settings
if SettingManager.get_instance().get_setting("discretisation_method") == "Z0-Zi < DeltaZ":
get_discrete_vertices = obj.get_discrete_vertices
else:
get_discrete_vertices = obj.get_discrete_vertices3
# Get the discrete vertices
discrete_vertices = get_discrete_vertices(delta_z)
discrete_vertices = obj.get_discrete_vertices(delta_z)
progress = 0
# Calculate the data for each discrete vertex
@ -84,14 +78,8 @@ def get_discrete_data(obj:ScannedObject, ndigits:int, delta_z:float=1, update_pr
for colone in colones:
data[colone] = []
# Select the discretisation method from the settings
if SettingManager.get_instance().get_setting("discretisation_method") == "Z0-Zi < DeltaZ":
get_discrete_vertices = obj.get_discrete_vertices
else:
get_discrete_vertices = obj.get_discrete_vertices3
# Get the discrete vertices
discrete_vertices = get_discrete_vertices(delta_z)
discrete_vertices = obj.get_discrete_vertices(delta_z)
progress = 0
for discrete_values in discrete_vertices:
x,y,z = data_extraction.get_x_y_z_mean(discrete_values)

View File

@ -7,6 +7,7 @@ Created on Thu Apr 20 2023
"""
import numpy as np
from utils.files.output import save_output_file
from utils.settings.SettingManager import SettingManager
class FacesNotGiven(Exception):
@ -72,8 +73,9 @@ class ScannedObject:
def __init__(self, vertices, faces=None, result_file_path=None):
self.vertices = np.asarray(vertices)
self.faces = np.asarray(faces)
self.result_file_path = result_file_path # Deprecated
self.bruteforce_discretization_result = None # Deprecated
self.old_delta = None
self.old_discrete = None
self.old_discrete_type = None
self.x = np.asarray([vertex[0] for vertex in vertices])
self.y = np.asarray([vertex[1] for vertex in vertices])
self.z = np.asarray([vertex[2] for vertex in vertices])
@ -188,11 +190,15 @@ class ScannedObject:
:return: vertices
"""
vertices = self.vertices if not sort else sorted(self.vertices, key=lambda vertex: vertex[2])
return vertices
def get_discrete_vertices(self, step:float = 1)->list:
if SettingManager.get_instance().get_setting("discretisation_method") == "Z0-Zi < DeltaZ":
return self.get_discrete_vertices_1(step)
return self.get_discrete_vertices_2(step)
def get_discrete_vertices_1(self, step:float = 1)->list:
"""
Discretize the vertices of the object using a split method.
This implementation will split the object at every step interval.
@ -200,6 +206,10 @@ class ScannedObject:
:param step: Step of the discretization
:return: Discretized vertices
"""
if self.old_delta == step and self.old_discrete_type == 0:
return self.old_discrete
self.old_delta = step
self.old_discrete_type = 0
current_interval = int(min(self.get_z()))
splitted_data = [[]]
for line in self.get_vertices(sort=True):
@ -208,23 +218,10 @@ class ScannedObject:
splitted_data.append([])
current_interval += step
splitted_data[-1].append(line)
self.old_discrete = splitted_data
return splitted_data
def get_discrete_vertices2(self, step:float = 1):
"""
Deprecated
"""
cpt = 0
L = [[]]
for vertex in self.get_vertices(sort=True):
step = 1
L[-1].append(vertex)
if vertex[2] > cpt + step:
cpt += step
L.append([])
return L
def get_discrete_vertices3(self, step:float = 1)->list:
def get_discrete_vertices_2(self, step:float = 1)->list:
"""
Discretize the vertices of the object using a lenght method.
This implementation will split the object when difference between the
@ -233,7 +230,10 @@ class ScannedObject:
:param step: Step of the discretization
:return: Discretized vertices
"""
cpt = 0
if self.old_delta == step and self.old_discrete_type == 1:
return self.old_discrete
self.old_delta = step
self.old_discrete_type = 1
L = [[]]
z = min(self.get_z())
sorted_vertices = self.get_vertices(sort=True)
@ -242,6 +242,7 @@ class ScannedObject:
if sorted_vertices[index][2] - z > step:
z = sorted_vertices[index+1][2]
L.append([])
self.old_discrete = L
return L
def get_faces(self,resolved:bool = False)->list:
@ -305,41 +306,6 @@ class ScannedObject:
"""
return {'verticies': self.vertices, 'faces': self.faces, 'x': self.x, 'y': self.y, 'z': self.z}
def bruteforce_discretization(self):
"""
Deprecated
TODO Remove this when its not needed anymore
"""
if self.bruteforce_discretization_result:
return self.bruteforce_discretization_result
if self.result_file_path is None:
raise ResultFileNotGiven("No result file was given")
L = []
splitted_data = [[]]
moyx, moyy, moyz = parse_result_file(self.result_file_path)
moy = moyx
verticies = self.get_vertices(sort=True)
position = 0
while position < len(verticies):
print('progression :',position/len(verticies)*100,end="\r")
x = verticies[position][0]
y = verticies[position][1]
z = verticies[position][2]
L.append(x)
splitted_data[-1].append(verticies[position])
m = np.mean(L)
if len(moy) > 0 and abs(m - moy[0]) < 0.000001:
moy.pop(0)
L = []
splitted_data.append([])
copyposition = position
while int(verticies[copyposition][2]) == int(verticies[copyposition-1][2]):
copyposition -= 1
position += 1
print(50*" ")
self.bruteforce_discretization_result = splitted_data
return splitted_data
def export_xyz(self, file_path:str,separator:str="\t"):
"""
Export the object in a file.

View File

@ -12,6 +12,7 @@ from PyQt5.QtWidgets import QFileDialog, QWidget
from utils.files.input import ScannedObject
from utils.gui.pyqt.settings.Settings import Settings
from utils.gui.pyqt.about.AboutThis import AboutThis
from utils.math.data_extraction import get_radius_from_x_y, get_true_teta_from_x_y
from utils.settings.SettingManager import SettingManager
from utils.graph2D.visplot_render import cross_section, render2D
from utils.graph3D.visplot_render import render3D
@ -40,6 +41,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
self.actionQuitter.triggered.connect(self.close)
self.actionQ_propos_de_ce_logiciel.triggered.connect(self.show_about)
self.layer_ComboBox.addItems(['Aucune couche'])
self.layer_ComboBox.currentIndexChanged.connect(self.layer_changed)
self.graphType = [
"Aucun",
@ -47,6 +50,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
"Coupe XZ",
"Coupe YZ",
"Evolution du rayon moyen",
"Coupe de la couche",
"Difference entre le rayon de chaque points et le rayon moyen de la couche"
]
self.obj = None
@ -172,7 +177,9 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
# Create the thread to run the analyse
self.preprocess_thread = QThread()
self.preprocess_worker = PreProcessWorker("PreProcessWorker",self.input_file_path.toPlainText())
self.preprocess_worker = PreProcessWorker("PreProcessWorker",
self.input_file_path.toPlainText(),
self.discretisation_value_selector.value())
self.preprocess_worker.moveToThread(self.preprocess_thread)
# Connect the signals
@ -258,6 +265,12 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
Set the discrete data to the main window
"""
self.discrete_data = discrete_data
layer = [str(i) for i in range(len(discrete_data["X moy (en mm)"]))]
layer.insert(0,"Aucune couche")
self.layer_ComboBox.clear()
self.layer_ComboBox.addItems(layer)
def set_raw_data(self,raw_data:dict):
"""
@ -303,7 +316,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
:param raw_data: The raw data
:param discrete_data: The discrete data
"""
# Clear the graphs
if not self.show_graph_checkbox.isChecked():
return
self.set_status("Rendering graphs... this may take a moment")
@ -334,6 +346,48 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
False).native)
self.set_status("Graphs rendered!")
def renderDiscreteGraphs(self,obj:ScannedObject,raw_data:dict,discrete_data:dict):
"""
"""
self.set_status("Renderingc discretes graphs... this may take a moment")
if self.layer_ComboBox.currentText() == 'Aucune couche':
return
layer_nb = int(self.layer_ComboBox.currentText())
discretisation_value = self.discretisation_value_selector.value()
for slot in self.slots:
current_slot = slot[0]
graph_type = slot[1]
if graph_type == "Coupe de la couche":
self.clear_slot(current_slot)
vertices = obj.get_discrete_vertices(discretisation_value)[layer_nb]
current_slot.addWidget(cross_section([x for x,y,z in vertices],
[y for x,y,z in vertices],
"Coupe de la couche",
"X en mm",
"Y en mm",
False).native)
if graph_type == "Difference entre le rayon de chaque points et le rayon moyen de la couche":
self.clear_slot(current_slot)
vertices = obj.get_discrete_vertices(discretisation_value)[layer_nb]
x_mean = discrete_data["X moy (en mm)"][layer_nb]
y_mean = discrete_data["Y moy (en mm)"][layer_nb]
r_mean = discrete_data["Rayon moyen (en mm)"][layer_nb]
rs = [get_radius_from_x_y(x,y,x_mean,y_mean) for x,y,z in vertices]
θs = [get_true_teta_from_x_y(x,y,x_mean,y_mean) for x,y,z in vertices]
min_θ = min(θs)
current_slot.addWidget(cross_section([θ-min_θ for θ in θs],
[r-r_mean for r in rs],
"Difference entre le rayon de chaque points et le rayon moyen de la couche",
"Theta en rad",
"R - <R>",
False).native)
self.set_status("Graphs rendered!")
def clear_slot(self,slot):
for i in reversed(range(slot.count())):
slot.itemAt(i).widget().setParent(None)
def clear_graphs(self):
"""
Clear the graphs
@ -341,8 +395,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
if not self.show_graph_checkbox.isChecked():
return
for slot,_ in self.slots:
for i in reversed(range(slot.count())):
slot.itemAt(i).widget().setParent(None)
self.clear_slot(slot)
###############################################################################
# #
@ -361,6 +414,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
self.status_text.setText("Done")
self.analyse_progress_bar.setValue(100)
self.renderGraphs(self.obj,self.raw_data,self.discrete_data)
self.renderDiscreteGraphs(self.obj,self.raw_data,self.discrete_data)
self.start_analyse_button.setEnabled(True)
def update_progress_bar(self, value):
@ -400,4 +454,17 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
settings = SettingManager.get_instance()
for count,_ in enumerate(self.slots):
self.slots[count][1] = self.combo_boxes[count].currentText()
settings.set_last_graph(count,self.slots[count][1])
settings.set_last_graph(count,self.slots[count][1])
if self.obj is None or self.discrete_data is None or self.raw_data is None:
return
else:
self.clear_graphs()
self.renderGraphs(self.obj,self.raw_data,self.discrete_data)
self.renderDiscreteGraphs(self.obj,self.raw_data,self.discrete_data)
def layer_changed(self):
if self.obj is None or self.discrete_data is None or self.raw_data is None:
return
else:
self.renderDiscreteGraphs(self.obj,self.raw_data,self.discrete_data)

View File

@ -183,12 +183,22 @@
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="layer_label">
<property name="text">
<string>Numero de la couche a afficher :</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="enabled">
<bool>true</bool>
</property>
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="tab">
<property name="enabled">
@ -277,7 +287,7 @@
<string>2</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_13">
<item row="0" column="1">
<item row="1" column="1">
<widget class="QFrame" name="slot5_frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
@ -295,7 +305,7 @@
</layout>
</widget>
</item>
<item row="0" column="0">
<item row="1" column="0">
<widget class="QFrame" name="slot4_frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
@ -313,7 +323,7 @@
</layout>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QFrame" name="slot6_frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
@ -331,7 +341,7 @@
</layout>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QFrame" name="slot7_frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
@ -421,6 +431,9 @@
</widget>
</widget>
</item>
<item row="1" column="0">
<widget class="QComboBox" name="layer_ComboBox"/>
</item>
</layout>
</widget>
</item>

View File

@ -106,6 +106,10 @@ class Ui_MainWindow(object):
self.Graphs.setObjectName("Graphs")
self.gridLayout_3 = QtWidgets.QGridLayout(self.Graphs)
self.gridLayout_3.setObjectName("gridLayout_3")
self.layer_label = QtWidgets.QLabel(self.Graphs)
self.layer_label.setAlignment(QtCore.Qt.AlignCenter)
self.layer_label.setObjectName("layer_label")
self.gridLayout_3.addWidget(self.layer_label, 0, 0, 1, 1)
self.tabWidget = QtWidgets.QTabWidget(self.Graphs)
self.tabWidget.setEnabled(True)
self.tabWidget.setObjectName("tabWidget")
@ -183,7 +187,7 @@ class Ui_MainWindow(object):
self.slot5ComboBox = QtWidgets.QComboBox(self.slot5_frame)
self.slot5ComboBox.setObjectName("slot5ComboBox")
self.gridLayout_18.addWidget(self.slot5ComboBox, 0, 0, 1, 1)
self.gridLayout_13.addWidget(self.slot5_frame, 0, 1, 1, 1)
self.gridLayout_13.addWidget(self.slot5_frame, 1, 1, 1, 1)
self.slot4_frame = QtWidgets.QFrame(self.tab_2)
self.slot4_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.slot4_frame.setFrameShadow(QtWidgets.QFrame.Raised)
@ -196,7 +200,7 @@ class Ui_MainWindow(object):
self.slot4ComboBox = QtWidgets.QComboBox(self.slot4_frame)
self.slot4ComboBox.setObjectName("slot4ComboBox")
self.gridLayout_17.addWidget(self.slot4ComboBox, 0, 0, 1, 1)
self.gridLayout_13.addWidget(self.slot4_frame, 0, 0, 1, 1)
self.gridLayout_13.addWidget(self.slot4_frame, 1, 0, 1, 1)
self.slot6_frame = QtWidgets.QFrame(self.tab_2)
self.slot6_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.slot6_frame.setFrameShadow(QtWidgets.QFrame.Raised)
@ -209,7 +213,7 @@ class Ui_MainWindow(object):
self.slot6ComboBox = QtWidgets.QComboBox(self.slot6_frame)
self.slot6ComboBox.setObjectName("slot6ComboBox")
self.gridLayout_19.addWidget(self.slot6ComboBox, 0, 0, 1, 1)
self.gridLayout_13.addWidget(self.slot6_frame, 1, 0, 1, 1)
self.gridLayout_13.addWidget(self.slot6_frame, 2, 0, 1, 1)
self.slot7_frame = QtWidgets.QFrame(self.tab_2)
self.slot7_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.slot7_frame.setFrameShadow(QtWidgets.QFrame.Raised)
@ -222,7 +226,7 @@ class Ui_MainWindow(object):
self.slot7ComboBox = QtWidgets.QComboBox(self.slot7_frame)
self.slot7ComboBox.setObjectName("slot7ComboBox")
self.gridLayout_20.addWidget(self.slot7ComboBox, 0, 0, 1, 1)
self.gridLayout_13.addWidget(self.slot7_frame, 1, 1, 1, 1)
self.gridLayout_13.addWidget(self.slot7_frame, 2, 1, 1, 1)
self.tabWidget.addTab(self.tab_2, "")
self.tab_3 = QtWidgets.QWidget()
self.tab_3.setObjectName("tab_3")
@ -273,7 +277,10 @@ class Ui_MainWindow(object):
self.gridLayout_9.addLayout(self.slot10, 1, 0, 1, 1)
self.gridLayout_4.addWidget(self.slot10_frame, 0, 0, 1, 1)
self.tabWidget.addTab(self.tab_4, "")
self.gridLayout_3.addWidget(self.tabWidget, 0, 0, 1, 1)
self.gridLayout_3.addWidget(self.tabWidget, 2, 0, 1, 1)
self.layer_ComboBox = QtWidgets.QComboBox(self.Graphs)
self.layer_ComboBox.setObjectName("layer_ComboBox")
self.gridLayout_3.addWidget(self.layer_ComboBox, 1, 0, 1, 1)
self.horizontalLayout_4.addWidget(self.Graphs)
self.gridLayout.addLayout(self.horizontalLayout_4, 1, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
@ -306,7 +313,7 @@ class Ui_MainWindow(object):
self.menuBar.addAction(self.menuA_propos.menuAction())
self.retranslateUi(MainWindow)
self.tabWidget.setCurrentIndex(0)
self.tabWidget.setCurrentIndex(1)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
@ -321,6 +328,7 @@ class Ui_MainWindow(object):
self.show_graph_checkbox.setText(_translate("MainWindow", "afficher les graphes"))
self.label.setText(_translate("MainWindow", "Nombre de graphes :"))
self.start_analyse_button.setText(_translate("MainWindow", "Analyser le fichier"))
self.layer_label.setText(_translate("MainWindow", "Numero de la couche a afficher :"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "1"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "2"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "3"))

View File

@ -26,9 +26,10 @@ class PreProcessWorker(Worker):
"""
processed_obj = pyqtSignal(ScannedObject)
def __init__(self,name:str, objpath:str):
def __init__(self,name:str, objpath:str,discretisation_value:float):
super().__init__(name)
self.objpath = objpath
self.deltaZ = discretisation_value
self.progress_value = 0
self.progress_weight = 100
@ -52,6 +53,10 @@ class PreProcessWorker(Worker):
obj.normalise()
self.update_progress(5)
self.set_status("Discretising object...")
obj.get_discrete_vertices(self.deltaZ)
self.update_progress(5)
# Emit the processed object
self.processed_obj.emit(obj)
self.finished.emit()

View File

@ -132,9 +132,56 @@ def get_teta_from_x_y(xi:float, yi:float, x_mean:float, y_mean:float):
>>> get_teta_from_x_y(1,2,3,4)
0.7853981633974483
"""
return math.atan((xi - x_mean)/(yi - y_mean))
return math.atan((yi - y_mean)/(xi-x_mean))
def get_true_teta_from_x_y(xi:float, yi:float, x_mean:float, y_mean:float):
"""
Get the teta from the x and y coordinates.
:param xi: x coordinate
:param yi: y coordinate
:param x_mean: mean of x coordinates in the discrete range
:param y_mean: mean of y coordinates in the discrete range
:return: teta for this point
:Example:
>>> get_true_teta_from_x_y(1,2,3,4)
0.7853981633974483
"""
return math.atan2((xi-x_mean),(yi-y_mean))
def get_difference_from_mean_value(values:list, mean_value:float):
"""
Get the difference from the mean value.
:param values: values
:param mean_value: mean value
:return: difference from the mean value
:Example:
>>> get_difference_from_mean_value([1,2,3,4,5], 3)
[-2.0, -1.0, 0.0, 1.0, 2.0]
"""
return [value - mean_value for value in values]
def get_distance_between_two_vertices(vertex_1,vertex_2):
"""
Get the distance between two vertices.
:param vertex_1: vertex 1
:param vertex_2: vertex 2
:return: distance between two vertices
:Example:
>>> get_distance_between_two_vertices((1,2,3),(4,5,6))
5.196152422706632
"""
return np.sqrt(np.power((vertex_1[0] - vertex_2[0]), 2) + np.power((vertex_1[1] - vertex_2[1]), 2) + np.power((vertex_1[2] - vertex_2[2]), 2))
#todo fix examples
if __name__ == "__main__":
import doctest
doctest.testmod()
doctest.testmod()