🐛 fix(data_test.py): change variable name from teta_diffs to theta_diffs to improve semantics
✨ feat(data_processing.py): add function to calculate morphological indicators from discrete data The variable name teta_diffs was changed to theta_diffs to improve semantics. A new function was added to calculate morphological indicators from discrete data. The function calculates Tortuosity, Volume, Surface, Mean radius, Standard deviation of radius, Sigma r tot, MI_l, and MI_p. 🔥 refactor(input.py): remove unused result_file_path parameter from ScannedObject constructor and from_xyz_file method ✨ feat(input.py): add encoding parameter to open method in from_obj_file and from_xyz_file methods The result_file_path parameter was not being used in the ScannedObject constructor and from_xyz_file method, so it was removed to simplify the code. The encoding parameter was added to the open method in the from_obj_file and from_xyz_file methods to ensure that the files are opened with the correct encoding. 🐛 fix(output.py): add utf-8 encoding when writing to output file ✨ feat(output.py): remove unused import and function argument, improve code readability The fix adds the utf-8 encoding when writing to the output file to avoid encoding issues. The feat removes the unused import and function argument to improve code readability. The function format_data now only takes the necessary arguments and the unused import is removed. 🐛 fix(main_window.py): fix typo in function name ✨ feat(main_window.py): add persistence to pre-processed data The fix corrects a typo in the function name get_true_theta_from_x_y. The feat adds persistence to the pre-processed data by storing the raw data, discrete data, and advanced data in the main window. This avoids re-computation of the data when switching between tabs. 🎨 style(MainWindow.ui): add export_advanced_metrics button to the UI 🎨 style(UI_MainWindow.py): add export_advanced_metrics button to the UI 🎨 style(ressources_rc.py): update the resource file 🐛 fix(data_extraction.py): fix typo in function name get_mean_teta to get_mean_theta The changes add a new button to the UI named "export_advanced_metrics" which allows the user to export variables. The resource file is updated to reflect the changes. The typo in the function name get_mean_teta is fixed to get_mean_theta.
This commit is contained in:
parent
4402085620
commit
fb2bb6e9ce
@ -104,7 +104,7 @@ def check_raw_data(expected_file, actual_file, ndigits=7, eps=0.00001, silent: b
|
|||||||
minimal_output_data = ""
|
minimal_output_data = ""
|
||||||
output_data = ""
|
output_data = ""
|
||||||
expected = []
|
expected = []
|
||||||
list_x_diffs, list_y_diffs, list_z_diffs, list_teta_diffs, list_radius_diffs, list_xmoy_diffs, list_ymoy_diffs = [], [], [], [], [], [], []
|
list_x_diffs, list_y_diffs, list_z_diffs, list_theta_diffs, list_radius_diffs, list_xmoy_diffs, list_ymoy_diffs = [], [], [], [], [], [], []
|
||||||
|
|
||||||
with open(expected_file, "r") as f:
|
with open(expected_file, "r") as f:
|
||||||
expected = f.readlines()[1:]
|
expected = f.readlines()[1:]
|
||||||
@ -124,7 +124,7 @@ def check_raw_data(expected_file, actual_file, ndigits=7, eps=0.00001, silent: b
|
|||||||
list_x_diffs.append(x_diff)
|
list_x_diffs.append(x_diff)
|
||||||
list_y_diffs.append(y_diff)
|
list_y_diffs.append(y_diff)
|
||||||
list_z_diffs.append(z_diff)
|
list_z_diffs.append(z_diff)
|
||||||
list_teta_diffs.append(t_diff)
|
list_theta_diffs.append(t_diff)
|
||||||
list_radius_diffs.append(r_diff)
|
list_radius_diffs.append(r_diff)
|
||||||
list_xmoy_diffs.append(xmoy_diff)
|
list_xmoy_diffs.append(xmoy_diff)
|
||||||
list_ymoy_diffs.append(ymoy_diff)
|
list_ymoy_diffs.append(ymoy_diff)
|
||||||
@ -141,7 +141,7 @@ def check_raw_data(expected_file, actual_file, ndigits=7, eps=0.00001, silent: b
|
|||||||
if z_diff > eps:
|
if z_diff > eps:
|
||||||
minimal_output_data += f"{i} : Z diff {z_diff}\n"
|
minimal_output_data += f"{i} : Z diff {z_diff}\n"
|
||||||
if t_diff > eps:
|
if t_diff > eps:
|
||||||
minimal_output_data += f"{i} : teta diff{ t_diff}\n"
|
minimal_output_data += f"{i} : theta diff{ t_diff}\n"
|
||||||
if r_diff > eps:
|
if r_diff > eps:
|
||||||
minimal_output_data += f"{i} : R diff {r_diff}\n"
|
minimal_output_data += f"{i} : R diff {r_diff}\n"
|
||||||
if xmoy_diff > eps:
|
if xmoy_diff > eps:
|
||||||
@ -158,7 +158,7 @@ def check_raw_data(expected_file, actual_file, ndigits=7, eps=0.00001, silent: b
|
|||||||
sum_x_diff = round(sum(list_x_diffs)/len(expected), ndigits)
|
sum_x_diff = round(sum(list_x_diffs)/len(expected), ndigits)
|
||||||
sum_y_diff = round(sum(list_y_diffs)/len(expected), ndigits)
|
sum_y_diff = round(sum(list_y_diffs)/len(expected), ndigits)
|
||||||
sum_z_diff = round(sum(list_z_diffs)/len(expected), ndigits)
|
sum_z_diff = round(sum(list_z_diffs)/len(expected), ndigits)
|
||||||
sum_teta_diff = round(sum(list_teta_diffs)/len(expected), ndigits)
|
sum_theta_diff = round(sum(list_theta_diffs)/len(expected), ndigits)
|
||||||
sum_radius_diff = round(sum(list_radius_diffs)/len(expected), ndigits)
|
sum_radius_diff = round(sum(list_radius_diffs)/len(expected), ndigits)
|
||||||
sum_xmoy_diff = round(sum(list_xmoy_diffs)/len(expected), ndigits)
|
sum_xmoy_diff = round(sum(list_xmoy_diffs)/len(expected), ndigits)
|
||||||
sum_ymoy_diffs = round(sum(list_ymoy_diffs)/len(expected), ndigits)
|
sum_ymoy_diffs = round(sum(list_ymoy_diffs)/len(expected), ndigits)
|
||||||
@ -167,12 +167,12 @@ def check_raw_data(expected_file, actual_file, ndigits=7, eps=0.00001, silent: b
|
|||||||
print(f"diff moyenne de x : {sum_x_diff}")
|
print(f"diff moyenne de x : {sum_x_diff}")
|
||||||
print(f"diff moyenne de y : {sum_y_diff}")
|
print(f"diff moyenne de y : {sum_y_diff}")
|
||||||
print(f"diff moyenne de z : {sum_z_diff}")
|
print(f"diff moyenne de z : {sum_z_diff}")
|
||||||
print(f"diff moyenne de t : {sum_teta_diff}")
|
print(f"diff moyenne de t : {sum_theta_diff}")
|
||||||
print(f"diff moyenne de r : {sum_radius_diff}")
|
print(f"diff moyenne de r : {sum_radius_diff}")
|
||||||
print(f"diff moyenne de xmoy : {sum_xmoy_diff}")
|
print(f"diff moyenne de xmoy : {sum_xmoy_diff}")
|
||||||
print(f"diff moyenne de ymoy : {sum_ymoy_diffs}")
|
print(f"diff moyenne de ymoy : {sum_ymoy_diffs}")
|
||||||
print(
|
print(
|
||||||
f"diff gloabale des fichiers : {(sum_x_diff+sum_y_diff+sum_z_diff+sum_teta_diff+sum_radius_diff+sum_xmoy_diff+sum_ymoy_diffs)/7}")
|
f"diff gloabale des fichiers : {(sum_x_diff+sum_y_diff+sum_z_diff+sum_theta_diff+sum_radius_diff+sum_xmoy_diff+sum_ymoy_diffs)/7}")
|
||||||
print(f"Voir {output_file} pour plus de détails")
|
print(f"Voir {output_file} pour plus de détails")
|
||||||
print(f"Voir {minimal_output_file} pour les différences significatives")
|
print(f"Voir {minimal_output_file} pour les différences significatives")
|
||||||
print("_"*80)
|
print("_"*80)
|
||||||
@ -180,7 +180,7 @@ def check_raw_data(expected_file, actual_file, ndigits=7, eps=0.00001, silent: b
|
|||||||
return {"x": list_x_diffs,
|
return {"x": list_x_diffs,
|
||||||
"y": list_y_diffs,
|
"y": list_y_diffs,
|
||||||
"z": list_z_diffs,
|
"z": list_z_diffs,
|
||||||
"teta": list_teta_diffs,
|
"theta": list_theta_diffs,
|
||||||
"radius": list_radius_diffs,
|
"radius": list_radius_diffs,
|
||||||
"xmoy": list_xmoy_diffs,
|
"xmoy": list_xmoy_diffs,
|
||||||
"ymoy": list_ymoy_diffs}
|
"ymoy": list_ymoy_diffs}
|
||||||
@ -236,7 +236,7 @@ def test_get_raw_data(obj: ScannedObject, expected_file: str, ndigits: int = 6,
|
|||||||
print()
|
print()
|
||||||
print("Time to calculate raw data: ", time.time() - now)
|
print("Time to calculate raw data: ", time.time() - now)
|
||||||
output.save_output_file('tmp_analyse_brute.txt', output.format_data(data, '\t', [
|
output.save_output_file('tmp_analyse_brute.txt', output.format_data(data, '\t', [
|
||||||
"X (en mm)", "Y (en mm)", "Z (en mm)", "teta (en rad)", "rayon (en mm)", "Xi-Xmoy", "Yi-Ymoy"]))
|
"X (en mm)", "Y (en mm)", "Z (en mm)", "theta (en rad)", "rayon (en mm)", "Xi-Xmoy", "Yi-Ymoy"]))
|
||||||
check_raw_data(expected_file, 'tmp_analyse_brute.txt', ndigits, eps)
|
check_raw_data(expected_file, 'tmp_analyse_brute.txt', ndigits, eps)
|
||||||
os.remove('tmp_analyse_brute.txt')
|
os.remove('tmp_analyse_brute.txt')
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,6 @@ Created on Mon Apr 24 2023
|
|||||||
from utils.math import data_extraction as de
|
from utils.math import data_extraction as de
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from utils.files.input import ScannedObject
|
from utils.files.input import ScannedObject
|
||||||
from utils.settings.SettingManager import SettingManager
|
|
||||||
|
|
||||||
def progressbar_placeholder(percent:int):
|
def progressbar_placeholder(percent:int):
|
||||||
"""
|
"""
|
||||||
@ -27,14 +26,20 @@ def get_raw_data(obj:ScannedObject, ndigits:int,delta_z:float=1,update_progress_
|
|||||||
- X (en mm) : list of x values
|
- X (en mm) : list of x values
|
||||||
- Y (en mm) : list of y values
|
- Y (en mm) : list of y values
|
||||||
- Z (en mm) : list of z values
|
- Z (en mm) : list of z values
|
||||||
- teta (en rad) : list of teta values
|
- theta (en rad) : list of theta values
|
||||||
- rayon (en mm) : list of radius values
|
- rayon (en mm) : list of radius values
|
||||||
- Xi-Xmoy : list of Xi-Xmoy values
|
- Xi-Xmoy : list of Xi-Xmoy values
|
||||||
- Yi-Ymoy : list of Yi-Ymoy values
|
- Yi-Ymoy : list of Yi-Ymoy values
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Create the data dict
|
# Create the data dict
|
||||||
colones = ["X (en mm)", "Y (en mm)", "Z (en mm)", "teta (en rad)", "rayon (en mm)","Xi-Xmoy","Yi-Ymoy"]
|
colones = ["X (en mm)",
|
||||||
|
"Y (en mm)",
|
||||||
|
"Z (en mm)",
|
||||||
|
"theta (en rad)",
|
||||||
|
"rayon (en mm)",
|
||||||
|
"Xi-Xmoy",
|
||||||
|
"Yi-Ymoy"]
|
||||||
data = {}
|
data = {}
|
||||||
for colone in colones:
|
for colone in colones:
|
||||||
data[colone] = []
|
data[colone] = []
|
||||||
@ -50,7 +55,7 @@ def get_raw_data(obj:ScannedObject, ndigits:int,delta_z:float=1,update_progress_
|
|||||||
data["X (en mm)"].append(round(x, ndigits))
|
data["X (en mm)"].append(round(x, ndigits))
|
||||||
data["Y (en mm)"].append(round(y, ndigits))
|
data["Y (en mm)"].append(round(y, ndigits))
|
||||||
data["Z (en mm)"].append(round(z, ndigits))
|
data["Z (en mm)"].append(round(z, ndigits))
|
||||||
data["teta (en rad)"].append(round(de.get_teta_from_x_y(x,y,mean_x,mean_y), ndigits))
|
data["theta (en rad)"].append(round(de.get_theta_from_x_y(x,y,mean_x,mean_y), ndigits))
|
||||||
data["rayon (en mm)"].append(round(de.get_radius_from_x_y(x,y,mean_x,mean_y), ndigits))
|
data["rayon (en mm)"].append(round(de.get_radius_from_x_y(x,y,mean_x,mean_y), ndigits))
|
||||||
data["Xi-Xmoy"].append(round(x-mean_x, ndigits))
|
data["Xi-Xmoy"].append(round(x-mean_x, ndigits))
|
||||||
data["Yi-Ymoy"].append(round(y-mean_y, ndigits))
|
data["Yi-Ymoy"].append(round(y-mean_y, ndigits))
|
||||||
@ -73,8 +78,14 @@ def get_discrete_data(obj:ScannedObject, ndigits:int, delta_z:float=1, update_pr
|
|||||||
- Rayon moyen (en mm) : list of mean radius values
|
- Rayon moyen (en mm) : list of mean radius values
|
||||||
- Rayon ecart type (en mm) : list of radius standard deviation values
|
- Rayon ecart type (en mm) : list of radius standard deviation values
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Create the data dict
|
# Create the data dict
|
||||||
colones = ["X moy (en mm)", "Y moy (en mm)", "Z moy (en mm)","Discretisation(en mm)","Rayon moyen (en mm)","Rayon ecart type (en mm)"]
|
colones = ["X moy (en mm)",
|
||||||
|
"Y moy (en mm)",
|
||||||
|
"Z moy (en mm)",
|
||||||
|
"Discretisation(en mm)",
|
||||||
|
"Rayon moyen (en mm)",
|
||||||
|
"Rayon ecart type (en mm)"]
|
||||||
data = {}
|
data = {}
|
||||||
for colone in colones:
|
for colone in colones:
|
||||||
data[colone] = []
|
data[colone] = []
|
||||||
@ -96,25 +107,49 @@ def get_discrete_data(obj:ScannedObject, ndigits:int, delta_z:float=1, update_pr
|
|||||||
progress += 1
|
progress += 1
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_advanced_data(discrete_data:dict, update_progress_bar= progressbar_placeholder):
|
def get_advanced_data(discrete_data:dict, update_progress_bar= progressbar_placeholder)->dict:
|
||||||
"""
|
"""
|
||||||
|
Calculates morphological indicators from the given discrete data
|
||||||
|
|
||||||
|
:param discrete_data: dict(str:list) with the following keys:
|
||||||
|
- X moy (en mm) : list of x mean values
|
||||||
|
- Y moy (en mm) : list of y mean values
|
||||||
|
- Z moy (en mm) : list of z mean values
|
||||||
|
- Rayon moyen (en mm) : list of mean radius values
|
||||||
|
- Rayon ecart type (en mm) : list of radius standard deviation values
|
||||||
|
:param update_progress_bar: Function to update the progress bar
|
||||||
|
:return: dict with the following keys:
|
||||||
|
- Tortuosite
|
||||||
|
- Volume
|
||||||
|
- Surface
|
||||||
|
- Moyenne des rayons moyens
|
||||||
|
- Ecart-type des rayons moyens
|
||||||
|
- Sigma r tot
|
||||||
|
- MI_l
|
||||||
|
- MI_p
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Tortusity
|
# Tortusity
|
||||||
l = 0
|
l = 0
|
||||||
L = 0
|
L = 0
|
||||||
vertices = list(zip(discrete_data["X moy (en mm)"], discrete_data["Y moy (en mm)"], discrete_data["Z moy (en mm)"]))
|
vertices = list(zip(discrete_data["X moy (en mm)"],
|
||||||
for i in range(len(vertices)-1):
|
discrete_data["Y moy (en mm)"],
|
||||||
l += de.get_distance_between_two_vertices(vertices[i], vertices[i+1])
|
discrete_data["Z moy (en mm)"]))
|
||||||
|
|
||||||
|
for index in range(len(vertices)-1):
|
||||||
|
l += de.get_distance_between_two_vertices(vertices[index], vertices[index+1])
|
||||||
L = de.get_distance_between_two_vertices(vertices[0], vertices[-1])
|
L = de.get_distance_between_two_vertices(vertices[0], vertices[-1])
|
||||||
T = l/L
|
T = l/L
|
||||||
update_progress_bar(10)
|
update_progress_bar(10)
|
||||||
|
|
||||||
# Volume and surface
|
# Volume and surface
|
||||||
H = discrete_data["Z moy (en mm)"][-1] - discrete_data["Z moy (en mm)"][0]
|
H = discrete_data["Z moy (en mm)"][-1] - discrete_data["Z moy (en mm)"][0]
|
||||||
R = de.get_mean([np.power(r,2) for r in discrete_data["Rayon moyen (en mm)"]])
|
R = de.get_mean([np.power(r,2) for r in discrete_data["Rayon moyen (en mm)"]])
|
||||||
V = np.pi * R * H
|
V = np.pi * R * H
|
||||||
S = 2 * np.pi * R * H
|
S = 2 * np.pi * R * H
|
||||||
update_progress_bar(30)
|
update_progress_bar(30)
|
||||||
#
|
|
||||||
|
# Morphological indicators
|
||||||
R_mean = de.get_mean(discrete_data["Rayon moyen (en mm)"])
|
R_mean = de.get_mean(discrete_data["Rayon moyen (en mm)"])
|
||||||
R_mean_std = de.get_standard_deviation(discrete_data["Rayon moyen (en mm)"])
|
R_mean_std = de.get_standard_deviation(discrete_data["Rayon moyen (en mm)"])
|
||||||
mean_sigma_r_squared = de.get_mean([np.power(r,2) for r in discrete_data["Rayon ecart type (en mm)"]])
|
mean_sigma_r_squared = de.get_mean([np.power(r,2) for r in discrete_data["Rayon ecart type (en mm)"]])
|
||||||
|
|||||||
@ -26,11 +26,9 @@ class ScannedObject:
|
|||||||
|
|
||||||
:param vertices: List of verticesm Ndarray of shape (n,2)
|
:param vertices: List of verticesm Ndarray of shape (n,2)
|
||||||
:param faces: List of faces, Ndarray of shape (n,2)
|
:param faces: List of faces, Ndarray of shape (n,2)
|
||||||
:param result_file_path: Path to the result file (deprecated, used for the bruteforce discretization)
|
|
||||||
|
|
||||||
:ivar vertices: List of vertices, Ndarray of shape (n,2)
|
:ivar vertices: List of vertices, Ndarray of shape (n,2)
|
||||||
:ivar faces: List of faces, Ndarray of shape (n,2)
|
:ivar faces: List of faces, Ndarray of shape (n,2)
|
||||||
:ivar result_file_path: Path to the result file (deprecated, used for the bruteforce discretization)
|
|
||||||
:ivar x: List of x values of the vertices
|
:ivar x: List of x values of the vertices
|
||||||
:ivar y: List of y values of the vertices
|
:ivar y: List of y values of the vertices
|
||||||
:ivar z: List of z values of the vertices
|
:ivar z: List of z values of the vertices
|
||||||
@ -46,31 +44,10 @@ class ScannedObject:
|
|||||||
:method get_data(): Returns the data
|
:method get_data(): Returns the data
|
||||||
:method export: Exports the data to a file
|
:method export: Exports the data to a file
|
||||||
|
|
||||||
|
|
||||||
:raises FacesNotGiven: If no faces was given
|
:raises FacesNotGiven: If no faces was given
|
||||||
:raises ResultFileNotGiven: If no result file was given
|
:raises ResultFileNotGiven: If no result file was given
|
||||||
|
|
||||||
|
|
||||||
:Example:
|
|
||||||
|
|
||||||
>>> from utils.files.input import ScannedObject
|
|
||||||
>>> vertices = [(0,0,0), (1,0,0), (1,1,0), (0,1,0), (0,0,1), (1,0,1), (1,1,1), (0,1,1)]
|
|
||||||
>>> faces = [(0,1,2), (0,2,3), (4,5,6), (4,6,7), (0,1,5), (0,4,5), (1,2,6), (1,5,6), (2,3,7), (2,6,7), (3,0,4), (3,7,4)]
|
|
||||||
>>> obj = ScannedObject(vertices, faces)
|
|
||||||
>>> obj.get_x()
|
|
||||||
[0, 1, 1, 0, 0, 1, 1, 0]
|
|
||||||
>>> obj.get_y()
|
|
||||||
[0, 0, 1, 1, 0, 0, 1, 1]
|
|
||||||
>>> obj.get_z()
|
|
||||||
[0, 0, 0, 0, 1, 1, 1, 1]
|
|
||||||
>>> obj.get_vertices()
|
|
||||||
[(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]
|
|
||||||
>>> obj.get_faces()
|
|
||||||
[(0, 1, 2), (0, 2, 3), (4, 5, 6), (4, 6, 7), (0, 1, 5), (0, 4, 5), (1, 2, 6), (1, 5, 6), (2, 3, 7), (2, 6, 7), (3, 0, 4), (3, 7, 4)]
|
|
||||||
>>> obj.get_discrete_vertices()
|
|
||||||
[[(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]],[ (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]]
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, vertices, faces=None, result_file_path=None):
|
def __init__(self, vertices, faces=None):
|
||||||
self.vertices = np.asarray(vertices)
|
self.vertices = np.asarray(vertices)
|
||||||
self.faces = np.asarray(faces)
|
self.faces = np.asarray(faces)
|
||||||
self.old_delta = None
|
self.old_delta = None
|
||||||
@ -82,17 +59,16 @@ class ScannedObject:
|
|||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_obj_file(file_path:str, result_file_path:str = None, ratio:float = 1,normalised:str = '')->'ScannedObject':
|
def from_obj_file(file_path:str, ratio:float = 1,normalised:str = '')->'ScannedObject':
|
||||||
"""
|
"""
|
||||||
Create an Object from an OBJ file.
|
Create an Object from an OBJ file.
|
||||||
|
|
||||||
:param file_path: Path to the OBJ file
|
:param file_path: Path to the OBJ file
|
||||||
:param result_file_path: Path to the result file
|
|
||||||
:param ratio: Ratio to apply to the vertices
|
:param ratio: Ratio to apply to the vertices
|
||||||
:param normalised: the axis to normalise
|
:param normalised: the axis to normalise
|
||||||
:return: A ScannedObject
|
:return: A ScannedObject
|
||||||
"""
|
"""
|
||||||
with open(file_path, 'r') as f:
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
x, y, z = [], [], []
|
x, y, z = [], [], []
|
||||||
triangles = []
|
triangles = []
|
||||||
data = f.readlines()
|
data = f.readlines()
|
||||||
@ -105,6 +81,10 @@ class ScannedObject:
|
|||||||
triangles.append([int(line.split()[1].split("/")[0])-1, int(line.split()[2].split("/")[0])-1, int(line.split()[3].split("/")[0])-1])
|
triangles.append([int(line.split()[1].split("/")[0])-1, int(line.split()[2].split("/")[0])-1, int(line.split()[3].split("/")[0])-1])
|
||||||
else:
|
else:
|
||||||
triangles.append([int(line.split()[1])-1, int(line.split()[2])-1, int(line.split()[3])-1])
|
triangles.append([int(line.split()[1])-1, int(line.split()[2])-1, int(line.split()[3])-1])
|
||||||
|
|
||||||
|
# if it is a vertex, the line starts with a 'v ',
|
||||||
|
# taking only 'v' would cause to take the textures coordinates('vt'),
|
||||||
|
# vertex normals ('vn') and space vertices ('vp')
|
||||||
elif line.startswith('v '):
|
elif line.startswith('v '):
|
||||||
x.append(float(line.split()[1]) * ratio)
|
x.append(float(line.split()[1]) * ratio)
|
||||||
y.append(float(line.split()[2]) * ratio)
|
y.append(float(line.split()[2]) * ratio)
|
||||||
@ -112,55 +92,54 @@ class ScannedObject:
|
|||||||
|
|
||||||
if 'x' in normalised:
|
if 'x' in normalised:
|
||||||
xmin = min(x)
|
xmin = min(x)
|
||||||
for count, value in enumerate(x):
|
for count,_ in enumerate(x):
|
||||||
x[count] -= xmin
|
x[count] -= xmin
|
||||||
|
|
||||||
if 'y' in normalised:
|
if 'y' in normalised:
|
||||||
ymin = min(y)
|
ymin = min(y)
|
||||||
for count, value in enumerate(y):
|
for count,_ in enumerate(y):
|
||||||
y[count] -= ymin
|
y[count] -= ymin
|
||||||
|
|
||||||
if 'z' in normalised:
|
if 'z' in normalised:
|
||||||
zmin = min(z)
|
zmin = min(z)
|
||||||
for count, value in enumerate(z):
|
for count,_ in enumerate(z):
|
||||||
z[count] -= zmin
|
z[count] -= zmin
|
||||||
|
|
||||||
return ScannedObject(list(zip(x,y,z)), triangles, result_file_path)
|
return ScannedObject(list(zip(x,y,z)), triangles, )
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_xyz_file(file_path: str, result_file_path:str = None, delimiter: str = ' ',normalised:str = '')->'ScannedObject':
|
def from_xyz_file(file_path:str, delimiter:str = ' ', normalised:str = '')->'ScannedObject':
|
||||||
"""
|
"""
|
||||||
Create an Object from an XYZ file.
|
Create an Object from an XYZ file.
|
||||||
|
|
||||||
:param file_path: Path to the XYZ file
|
:param file_path: Path to the XYZ file
|
||||||
:param result_file_path: Path to the result file
|
|
||||||
:param delimiter: The delimiter used in the xyz file.
|
:param delimiter: The delimiter used in the xyz file.
|
||||||
:param normalised: the axis to normalise
|
:param normalised: the axis to normalise
|
||||||
:return: A ScannedObject
|
:return: A ScannedObject
|
||||||
"""
|
"""
|
||||||
x , y , z = [], [], []
|
x , y , z = [], [], []
|
||||||
with open(file_path, 'r') as f:
|
with open(file_path, 'r',encoding='utf-8') as f:
|
||||||
data = f.readlines()
|
data = f.readlines()
|
||||||
for line in data:
|
for line in data:
|
||||||
x.append(float(line.split(delimiter)[0]))
|
x.append(float(line.split(delimiter)[0]))
|
||||||
y.append(float(line.split(delimiter)[1]))
|
y.append(float(line.split(delimiter)[1]))
|
||||||
z.append(float(line.split(delimiter)[2]))
|
z.append(float(line.split(delimiter)[2]))
|
||||||
|
|
||||||
if 'x' in normalised:
|
if 'x' in normalised:
|
||||||
xmin = min(x)
|
xmin = min(x)
|
||||||
for count, value in enumerate(x):
|
for count,_ in enumerate(x):
|
||||||
x[count] -= xmin
|
x[count] -= xmin
|
||||||
|
|
||||||
if 'y' in normalised:
|
if 'y' in normalised:
|
||||||
ymin = min(y)
|
ymin = min(y)
|
||||||
for count, value in enumerate(y):
|
for count,_ in enumerate(y):
|
||||||
y[count] -= ymin
|
y[count] -= ymin
|
||||||
|
|
||||||
if 'z' in normalised:
|
if 'z' in normalised:
|
||||||
zmin = min(z)
|
zmin = min(z)
|
||||||
for count, value in enumerate(z):
|
for count,_ in enumerate(z):
|
||||||
z[count] -= zmin
|
z[count] -= zmin
|
||||||
return ScannedObject(list(zip(x,y,z)), result_file_path=result_file_path)
|
return ScannedObject(list(zip(x,y,z)))
|
||||||
|
|
||||||
def get_x(self)->list:
|
def get_x(self)->list:
|
||||||
"""
|
"""
|
||||||
@ -194,6 +173,12 @@ class ScannedObject:
|
|||||||
return vertices
|
return vertices
|
||||||
|
|
||||||
def get_discrete_vertices(self, step:float = 1)->list:
|
def get_discrete_vertices(self, step:float = 1)->list:
|
||||||
|
"""
|
||||||
|
Discretize the vertices using the method specified in the settings.
|
||||||
|
|
||||||
|
:param step: Step of the discretization
|
||||||
|
:return: Discretized vertices
|
||||||
|
"""
|
||||||
if SettingManager.get_instance().get_setting("discretisation_method") == "Z0-Zi < DeltaZ":
|
if SettingManager.get_instance().get_setting("discretisation_method") == "Z0-Zi < DeltaZ":
|
||||||
return self.get_discrete_vertices_1(step)
|
return self.get_discrete_vertices_1(step)
|
||||||
return self.get_discrete_vertices_2(step)
|
return self.get_discrete_vertices_2(step)
|
||||||
@ -206,10 +191,14 @@ class ScannedObject:
|
|||||||
:param step: Step of the discretization
|
:param step: Step of the discretization
|
||||||
:return: Discretized vertices
|
:return: Discretized vertices
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# if it has already been calculated with the same method and parametters
|
||||||
|
# dont do it again
|
||||||
if self.old_delta == step and self.old_discrete_type == 0:
|
if self.old_delta == step and self.old_discrete_type == 0:
|
||||||
return self.old_discrete
|
return self.old_discrete
|
||||||
self.old_delta = step
|
self.old_delta = step
|
||||||
self.old_discrete_type = 0
|
self.old_discrete_type = 0
|
||||||
|
|
||||||
current_interval = int(min(self.get_z()))
|
current_interval = int(min(self.get_z()))
|
||||||
splitted_data = [[]]
|
splitted_data = [[]]
|
||||||
for line in self.get_vertices(sort=True):
|
for line in self.get_vertices(sort=True):
|
||||||
@ -223,43 +212,53 @@ class ScannedObject:
|
|||||||
|
|
||||||
def get_discrete_vertices_2(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.
|
Discretize the vertices of the object using a length method.
|
||||||
This implementation will split the object when difference between the
|
This implementation will split the object when difference between the
|
||||||
first and last point of a slice is greater or equal then the step interval.
|
first and last point of a slice is greater or equal then the step interval.
|
||||||
|
|
||||||
:param step: Step of the discretization
|
:param step: Step of the discretization
|
||||||
:return: Discretized vertices
|
:return: Discretized vertices
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# if it has already been calculated with the same method and parametters
|
||||||
|
# dont do it again
|
||||||
if self.old_delta == step and self.old_discrete_type == 1:
|
if self.old_delta == step and self.old_discrete_type == 1:
|
||||||
return self.old_discrete
|
return self.old_discrete
|
||||||
self.old_delta = step
|
self.old_delta = step
|
||||||
self.old_discrete_type = 1
|
self.old_discrete_type = 1
|
||||||
L = [[]]
|
|
||||||
|
splitted_data = [[]]
|
||||||
z = min(self.get_z())
|
z = min(self.get_z())
|
||||||
sorted_vertices = self.get_vertices(sort=True)
|
sorted_vertices = self.get_vertices(sort=True)
|
||||||
for index,_ in enumerate(sorted_vertices):
|
for index,_ in enumerate(sorted_vertices):
|
||||||
L[-1].append(sorted_vertices[index])
|
splitted_data[-1].append(sorted_vertices[index])
|
||||||
if sorted_vertices[index][2] - z > step:
|
if sorted_vertices[index][2] - z > step:
|
||||||
z = sorted_vertices[index+1][2]
|
z = sorted_vertices[index+1][2]
|
||||||
L.append([])
|
splitted_data.append([])
|
||||||
self.old_discrete = L
|
self.old_discrete = splitted_data
|
||||||
return L
|
return splitted_data
|
||||||
|
|
||||||
def get_faces(self,resolved:bool = False)->list:
|
def get_faces(self,resolved:bool = False)->list:
|
||||||
"""
|
"""
|
||||||
Get the faces of the object.
|
Get the faces of the object.
|
||||||
|
If the faces are not resolved, the faces will be returned as a list of
|
||||||
|
indices of the vertices. else, the faces will be returned as a list of
|
||||||
|
vertices.
|
||||||
|
|
||||||
|
:param resolved: If the faces should be resolved
|
||||||
:return: faces
|
:return: faces
|
||||||
"""
|
"""
|
||||||
if self.faces is None:
|
if self.faces is None:
|
||||||
raise FacesNotGiven('No faces was given')
|
raise FacesNotGiven('No faces were given')
|
||||||
if resolved:
|
if resolved:
|
||||||
return self.vertices[self.faces]
|
return self.vertices[self.faces]
|
||||||
return self.faces
|
return self.faces
|
||||||
|
|
||||||
def update_from_faces(self,faces:list):
|
def update_from_faces(self,faces:list):
|
||||||
"""
|
"""
|
||||||
Update the object from the faces.
|
Update the object from the faces. This will reconstruct the vertices
|
||||||
|
from the faces, it is assumed that the faces are given as a list of
|
||||||
|
vertices.
|
||||||
|
|
||||||
:param faces: Faces to update the object from
|
:param faces: Faces to update the object from
|
||||||
"""
|
"""
|
||||||
@ -304,7 +303,12 @@ class ScannedObject:
|
|||||||
|
|
||||||
:return: Data of the object
|
:return: Data of the object
|
||||||
"""
|
"""
|
||||||
return {'verticies': self.vertices, 'faces': self.faces, 'x': self.x, 'y': self.y, 'z': self.z}
|
return {'verticies': self.vertices,
|
||||||
|
'faces': self.faces,
|
||||||
|
'x': self.x,
|
||||||
|
'y': self.y,
|
||||||
|
'z': self.z
|
||||||
|
}
|
||||||
|
|
||||||
def export_xyz(self, file_path:str,separator:str="\t"):
|
def export_xyz(self, file_path:str,separator:str="\t"):
|
||||||
"""
|
"""
|
||||||
@ -314,12 +318,11 @@ class ScannedObject:
|
|||||||
:param separator: chars used to separate the values
|
:param separator: chars used to separate the values
|
||||||
"""
|
"""
|
||||||
string = ''
|
string = ''
|
||||||
with open(file_path, "w") as f:
|
for vertex in self.get_vertices(sort=True):
|
||||||
for vertex in self.get_vertices(sort=True):
|
x = round(vertex[0], 6)
|
||||||
x = round(vertex[0], 6)
|
y = round(vertex[1], 6)
|
||||||
y = round(vertex[1], 6)
|
z = round(vertex[2], 6)
|
||||||
z = round(vertex[2], 6)
|
string+=f"{x}{separator}{y}{separator}{z}\n"
|
||||||
string+=f"{x}{separator}{y}{separator}{z}\n"
|
|
||||||
save_output_file(file_path,string)
|
save_output_file(file_path,string)
|
||||||
|
|
||||||
def export_obj(self,file_path):
|
def export_obj(self,file_path):
|
||||||
@ -329,17 +332,16 @@ class ScannedObject:
|
|||||||
:param file_path: Path of the file
|
:param file_path: Path of the file
|
||||||
"""
|
"""
|
||||||
string = ''
|
string = ''
|
||||||
with open(file_path, "w") as f:
|
for vertex in self.get_vertices():
|
||||||
for vertex in self.get_vertices():
|
x = round(vertex[0], 6)
|
||||||
x = round(vertex[0], 6)
|
y = round(vertex[1], 6)
|
||||||
y = round(vertex[1], 6)
|
z = round(vertex[2], 6)
|
||||||
z = round(vertex[2], 6)
|
string+=f"v {x} {y} {z}\n"
|
||||||
string+=f"v {x} {y} {z}\n"
|
for face in self.get_faces():
|
||||||
for face in self.get_faces():
|
string+="f "
|
||||||
string+="f "
|
for vertex in face:
|
||||||
for vertex in face:
|
string+=f"{vertex+1} "
|
||||||
string+=f"{vertex+1} "
|
string+="\n"
|
||||||
string+="\n"
|
|
||||||
save_output_file(file_path,string)
|
save_output_file(file_path,string)
|
||||||
|
|
||||||
|
|
||||||
@ -347,7 +349,7 @@ def parse_result_file(file_path: str, separator: str = "\t")-> tuple:
|
|||||||
"""
|
"""
|
||||||
This functions parses the discretised output file to retreive the first
|
This functions parses the discretised output file to retreive the first
|
||||||
three colunms. It is used to extract the means of x y z for the consistency
|
three colunms. It is used to extract the means of x y z for the consistency
|
||||||
check and the bruteforce_discretisation
|
check.
|
||||||
|
|
||||||
:param file_path: Path of the file
|
:param file_path: Path of the file
|
||||||
:param separator: chars used to separate the values
|
:param separator: chars used to separate the values
|
||||||
|
|||||||
@ -5,10 +5,8 @@ Created on Mon Apr 17 2023
|
|||||||
@auth: Djalim Simaila
|
@auth: Djalim Simaila
|
||||||
@e-mail: djalim.simaila@inrae.fr
|
@e-mail: djalim.simaila@inrae.fr
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from utils.settings.SettingManager import SettingManager
|
from utils.settings.SettingManager import SettingManager
|
||||||
|
|
||||||
|
|
||||||
def format_data(data:dict, separator:str, selected_columns:list = None) -> str:
|
def format_data(data:dict, separator:str, selected_columns:list = None) -> str:
|
||||||
"""
|
"""
|
||||||
Format the data to be saved in the output file.
|
Format the data to be saved in the output file.
|
||||||
@ -41,7 +39,7 @@ def format_data(data:dict, separator:str, selected_columns:list = None) -> str:
|
|||||||
# Write the columns headers
|
# Write the columns headers
|
||||||
for column_name in selected_columns:
|
for column_name in selected_columns:
|
||||||
if is_prettier:
|
if is_prettier:
|
||||||
output += column_name.ljust(len(column_name) if len(column_name) > 8 else 9 ) + separator
|
output += column_name.ljust(len(column_name) if len(column_name) > 8 else 9 ) + separator
|
||||||
else:
|
else:
|
||||||
output += column_name + separator
|
output += column_name + separator
|
||||||
output += '\n'
|
output += '\n'
|
||||||
@ -62,5 +60,5 @@ def save_output_file(output_file:str, content:str):
|
|||||||
:param output_file: Path to the output file
|
:param output_file: Path to the output file
|
||||||
:param content: Content of the output file
|
:param content: Content of the output file
|
||||||
"""
|
"""
|
||||||
with open(output_file, 'w') as f:
|
with open(output_file, 'w',encoding='utf-8') as file:
|
||||||
f.write(content)
|
file.write(content)
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
Created on Fri Apr 21 2023
|
Created on Fri Apr 21 2023
|
||||||
@name: mpl_render.py
|
@name: mpl_render.py
|
||||||
@desc: A module to render a 2D data using matplotlib
|
@desc: A module to render a 2D data using matplotlib,
|
||||||
|
thoses functions cant be integrated into pyqt gui yet
|
||||||
@auth: Djalim Simaila
|
@auth: Djalim Simaila
|
||||||
@e-mail: djalim.simaila@inrae.fr
|
@e-mail: djalim.simaila@inrae.fr
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -16,7 +16,7 @@ def render2D(values:list,title:str,xlabel="",ylabel="",show:bool=True)->vp.Fig|N
|
|||||||
:param title: Title of the plot
|
:param title: Title of the plot
|
||||||
:param xlabel: Label of the x axis
|
:param xlabel: Label of the x axis
|
||||||
:param ylabel: Label of the y axis
|
:param ylabel: Label of the y axis
|
||||||
:param show: If True, show the plot, else return the canvas
|
:param show: If True, show the plot in its own window, else return the canvas
|
||||||
"""
|
"""
|
||||||
fig = vp.Fig(size=(600, 500), show=False)
|
fig = vp.Fig(size=(600, 500), show=False)
|
||||||
plotwidget = fig[0, 0]
|
plotwidget = fig[0, 0]
|
||||||
@ -41,7 +41,7 @@ def cross_section(x_values:list, y_values:list,title:str,xlabel="",ylabel="",sho
|
|||||||
:param title: Title of the plot
|
:param title: Title of the plot
|
||||||
:param xlabel: Label of the x axis
|
:param xlabel: Label of the x axis
|
||||||
:param ylabel: Label of the y axis
|
:param ylabel: Label of the y axis
|
||||||
:param show: If True, show the plot, else return the canvas
|
:param show: If True, show the plot in its own window, else return the canvas
|
||||||
"""
|
"""
|
||||||
color = (0.3, 0.5, 0.8,.8)
|
color = (0.3, 0.5, 0.8,.8)
|
||||||
fig = vp.Fig(show=False)
|
fig = vp.Fig(show=False)
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
Created on Fri Apr 21 2023
|
Created on Fri Apr 21 2023
|
||||||
@name: mpl_render.py
|
@name: mpl_render.py
|
||||||
@desc: A module to render a 3D data using matplotlib
|
@desc: A module to render a 3D data using matplotlib,
|
||||||
|
thoses functions cant be integrated into pyqt gui yet
|
||||||
@auth: Djalim Simaila
|
@auth: Djalim Simaila
|
||||||
@e-mail: djalim.simaila@inrae.fr
|
@e-mail: djalim.simaila@inrae.fr
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -10,6 +10,8 @@ from utils.gui.pyqt.about.UI_AboutThis import Ui_AboutThis
|
|||||||
from utils.settings.SettingManager import SettingManager
|
from utils.settings.SettingManager import SettingManager
|
||||||
|
|
||||||
class AboutThis(QtWidgets.QMainWindow,Ui_AboutThis):
|
class AboutThis(QtWidgets.QMainWindow,Ui_AboutThis):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
def __init__(self, parent=None) -> None:
|
def __init__(self, parent=None) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|||||||
@ -30,8 +30,12 @@ class ErrorPopup(object):
|
|||||||
self.button_callback = button_callback
|
self.button_callback = button_callback
|
||||||
self.details = details
|
self.details = details
|
||||||
|
|
||||||
|
|
||||||
def show_popup(self):
|
def show_popup(self):
|
||||||
|
"""
|
||||||
|
Show the error popup with the specified error message.
|
||||||
|
If a button label and a button callback are specified, the popup will
|
||||||
|
have a button with the specified label and the specified callback.
|
||||||
|
"""
|
||||||
msg = QMessageBox()
|
msg = QMessageBox()
|
||||||
msg.setWindowTitle("Erreur")
|
msg.setWindowTitle("Erreur")
|
||||||
msg.setText("Erreur: " + self.error_text)
|
msg.setText("Erreur: " + self.error_text)
|
||||||
|
|||||||
@ -13,7 +13,7 @@ from utils.files.input import ScannedObject
|
|||||||
from utils.gui.pyqt.main_window.Workers.AdvancedDataWorker import AdvancedDataWorker
|
from utils.gui.pyqt.main_window.Workers.AdvancedDataWorker import AdvancedDataWorker
|
||||||
from utils.gui.pyqt.settings.Settings import Settings
|
from utils.gui.pyqt.settings.Settings import Settings
|
||||||
from utils.gui.pyqt.about.AboutThis import AboutThis
|
from utils.gui.pyqt.about.AboutThis import AboutThis
|
||||||
from utils.math.data_extraction import get_mean, get_radius_from_x_y, get_true_teta_from_x_y
|
from utils.math.data_extraction import get_mean, get_radius_from_x_y, get_true_theta_from_x_y
|
||||||
from utils.settings.SettingManager import SettingManager
|
from utils.settings.SettingManager import SettingManager
|
||||||
from utils.graph2D.visplot_render import cross_section, render2D
|
from utils.graph2D.visplot_render import cross_section, render2D
|
||||||
from utils.graph3D.visplot_render import render3D
|
from utils.graph3D.visplot_render import render3D
|
||||||
@ -25,11 +25,17 @@ from utils.gui.pyqt.error_popup.ErrorPopup import ErrorPopup
|
|||||||
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||||
"""
|
"""
|
||||||
Main window of the application, it contains all the UI elements
|
Main window of the application, it contains all the UI elements
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super(MainWindow, self).__init__(parent)
|
super(MainWindow, self).__init__(parent)
|
||||||
|
|
||||||
|
# Persist variable to avoid re-computation
|
||||||
|
self.obj = None
|
||||||
|
self.raw_data= None
|
||||||
|
self.discrete_data = None
|
||||||
|
self.advanced_data = None
|
||||||
|
|
||||||
# Retrieve the UI
|
# Retrieve the UI
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
# Setup buttons listeners
|
# Setup buttons listeners
|
||||||
@ -42,10 +48,12 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
self.actionQuitter.triggered.connect(self.close)
|
self.actionQuitter.triggered.connect(self.close)
|
||||||
self.actionQ_propos_de_ce_logiciel.triggered.connect(self.show_about)
|
self.actionQ_propos_de_ce_logiciel.triggered.connect(self.show_about)
|
||||||
|
|
||||||
|
# add default layer combobox value and setup the listenerr when index change
|
||||||
self.layer_ComboBox.addItems(['Aucune couche'])
|
self.layer_ComboBox.addItems(['Aucune couche'])
|
||||||
self.layer_ComboBox.currentIndexChanged.connect(self.layer_changed)
|
self.layer_ComboBox.currentIndexChanged.connect(self.layer_changed)
|
||||||
|
|
||||||
self.graphType = [
|
# Prepare available graph type list for the slots combobox
|
||||||
|
self.graph_type = [
|
||||||
"Aucun",
|
"Aucun",
|
||||||
"Mesh3D",
|
"Mesh3D",
|
||||||
"Coupe XZ",
|
"Coupe XZ",
|
||||||
@ -55,15 +63,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
"Coupe de la couche",
|
"Coupe de la couche",
|
||||||
"Difference entre le rayon de chaque points et le rayon moyen de la couche"
|
"Difference entre le rayon de chaque points et le rayon moyen de la couche"
|
||||||
]
|
]
|
||||||
|
|
||||||
self.obj = None
|
|
||||||
self.raw_data= None
|
|
||||||
self.discrete_data = None
|
|
||||||
self.advanced_data = None
|
|
||||||
|
|
||||||
self.completed_tasks = 0
|
|
||||||
self.total_tasks = 3
|
|
||||||
|
|
||||||
|
# put all slots combo boxes in a list for conveniance
|
||||||
self.combo_boxes = [
|
self.combo_boxes = [
|
||||||
self.slot0ComboBox,
|
self.slot0ComboBox,
|
||||||
self.slot1ComboBox,
|
self.slot1ComboBox,
|
||||||
@ -78,10 +79,12 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
self.slot10ComboBox
|
self.slot10ComboBox
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Setup all combo boxes with values and listener
|
||||||
for combo_box in self.combo_boxes:
|
for combo_box in self.combo_boxes:
|
||||||
combo_box.addItems(self.graphType)
|
combo_box.addItems(self.graph_type)
|
||||||
combo_box.currentIndexChanged.connect(self.graph_type_changed)
|
combo_box.currentIndexChanged.connect(self.graph_type_changed)
|
||||||
|
|
||||||
|
# put all slots in a list for conveniance and store their state in a string
|
||||||
self.slots = [
|
self.slots = [
|
||||||
[self.slot0,"Aucun"],
|
[self.slot0,"Aucun"],
|
||||||
[self.slot1,"Aucun"],
|
[self.slot1,"Aucun"],
|
||||||
@ -96,19 +99,32 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
[self.slot10,"Aucun"]
|
[self.slot10,"Aucun"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Retrieve the slot previous value from the settings
|
||||||
for slot_nb,slot in enumerate(self.slots):
|
for slot_nb,slot in enumerate(self.slots):
|
||||||
slot[1] = SettingManager.get_instance().get_last_graph(slot_nb)
|
slot[1] = SettingManager.get_instance().get_last_graph(slot_nb)
|
||||||
self.combo_boxes[slot_nb].setCurrentText(slot[1])
|
self.combo_boxes[slot_nb].setCurrentText(slot[1])
|
||||||
|
|
||||||
|
# Graph number indicator
|
||||||
self.graph_nb =0
|
self.graph_nb =0
|
||||||
self.graph_type_changed()
|
self.graph_type_changed()
|
||||||
|
|
||||||
|
# Construct sub windows
|
||||||
self.settings_window = Settings()
|
self.settings_window = Settings()
|
||||||
self.about_window = AboutThis()
|
self.about_window = AboutThis()
|
||||||
|
|
||||||
|
# Variable to check if parametters has changed to avoid re-computation
|
||||||
self.has_changed = True
|
self.has_changed = True
|
||||||
self.old_discretisation_value = None
|
self.old_discretisation_value = None
|
||||||
|
|
||||||
|
# Task counter and task number, used to know when the analysis
|
||||||
|
# is considered finished
|
||||||
|
# Current tasks are
|
||||||
|
# - process raw data
|
||||||
|
# - process discrete data
|
||||||
|
# - process morphological indicators (advanced data)
|
||||||
|
self.completed_tasks = 0
|
||||||
|
self.total_tasks = 3
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# #
|
# #
|
||||||
# #
|
# #
|
||||||
@ -118,7 +134,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
def select_file(self):
|
def select_file(self):
|
||||||
"""
|
"""
|
||||||
Open a file dialog to select the input file
|
Open a file dialog to select the input file,
|
||||||
|
if the folder_path is empty, it fills it with the files folder.
|
||||||
"""
|
"""
|
||||||
file = QFileDialog.getOpenFileName()[0]
|
file = QFileDialog.getOpenFileName()[0]
|
||||||
self.input_file_path.setPlainText(file)
|
self.input_file_path.setPlainText(file)
|
||||||
@ -136,19 +153,30 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
def check_input_file(self):
|
def check_input_file(self):
|
||||||
"""
|
"""
|
||||||
Check if the input file is valid
|
Check if the input file is valid by checking:
|
||||||
|
- if it exists
|
||||||
|
- if its extension is .obj
|
||||||
"""
|
"""
|
||||||
if not os.path.isfile(self.input_file_path.toPlainText()):
|
if not os.path.isfile(self.input_file_path.toPlainText()) :
|
||||||
ErrorPopup("Fichier d'entrée invalide",button_label="Choisir un fichier d'entrée",button_callback=self.select_file).show_popup()
|
ErrorPopup("Fichier d'entrée invalide: Aucun fichier selectionné, ou le fichier n'existe pas",
|
||||||
|
button_label="Choisir un fichier d'entrée",
|
||||||
|
button_callback=self.select_file).show_popup()
|
||||||
|
return False
|
||||||
|
if os.path.splitext(self.input_file_path.toPlainText())[1].lower() != ".obj":
|
||||||
|
ErrorPopup("Fichier d'entrée invalide: l'extension du fichier est incorrecte ",
|
||||||
|
button_label="Choisir un fichier d'entrée",
|
||||||
|
button_callback=self.select_file).show_popup()
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def check_output_folder(self):
|
def check_output_folder(self):
|
||||||
"""
|
"""
|
||||||
Check if the output folder is valid
|
Check if the output folder is valid by cheking if it exists.
|
||||||
"""
|
"""
|
||||||
if not os.path.isdir(self.output_folder_path.toPlainText()):
|
if not os.path.isdir(self.output_folder_path.toPlainText()):
|
||||||
ErrorPopup("Dossier de sortie invalide",button_label="Choisir un dossier de sortie",button_callback=self.select_folder).show_popup()
|
ErrorPopup("Dossier de sortie invalide",
|
||||||
|
button_label="Choisir un dossier de sortie",
|
||||||
|
button_callback=self.select_folder).show_popup()
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -284,13 +312,13 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
def set_obj(self,obj:ScannedObject):
|
def set_obj(self,obj:ScannedObject):
|
||||||
"""
|
"""
|
||||||
Set the obj to the main window
|
Persists the pre-processed obj
|
||||||
"""
|
"""
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
|
|
||||||
def set_discrete_data(self,discrete_data:dict):
|
def set_discrete_data(self,discrete_data:dict):
|
||||||
"""
|
"""
|
||||||
Set the discrete data to the main window
|
Persists the calculated discrete data
|
||||||
"""
|
"""
|
||||||
self.discrete_data = discrete_data
|
self.discrete_data = discrete_data
|
||||||
layer = [str(i) for i in range(len(discrete_data["X moy (en mm)"]))]
|
layer = [str(i) for i in range(len(discrete_data["X moy (en mm)"]))]
|
||||||
@ -302,13 +330,13 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
def set_raw_data(self,raw_data:dict):
|
def set_raw_data(self,raw_data:dict):
|
||||||
"""
|
"""
|
||||||
Set the raw data to the main window
|
Persists the calculated raw data
|
||||||
"""
|
"""
|
||||||
self.raw_data = raw_data
|
self.raw_data = raw_data
|
||||||
|
|
||||||
def set_advanced_data(self,advanced_data:dict):
|
def set_advanced_data(self,advanced_data:dict):
|
||||||
"""
|
"""
|
||||||
Set the advanced data to the main window
|
Persists the calculated raw data and show the values
|
||||||
"""
|
"""
|
||||||
self.advanced_data = advanced_data
|
self.advanced_data = advanced_data
|
||||||
self.tortuosity.setValue(advanced_data["Tortuosité"])
|
self.tortuosity.setValue(advanced_data["Tortuosité"])
|
||||||
@ -433,15 +461,15 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
y_mean = discrete_data["Y 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]
|
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]
|
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]
|
θs = [get_true_theta_from_x_y(x,y,x_mean,y_mean) for x,y,z in vertices]
|
||||||
min_θ = min(θs)
|
min_θ = min(θs)
|
||||||
current_slot.addWidget(cross_section([θ-min_θ for θ in θs],
|
current_slot.addWidget(cross_section([θ-min_θ for θ in θs],
|
||||||
[r-r_mean for r in rs],
|
[r-r_mean for r in rs],
|
||||||
"Difference entre le rayon de chaque points et le rayon moyen de la couche",
|
"Difference entre le rayon de chaque points et le rayon moyen de la couche",
|
||||||
"Theta en rad",
|
"Theta en rad",
|
||||||
"R - <R>",
|
"r - <r> en mm",
|
||||||
False).native)
|
False).native)
|
||||||
self.set_status("Graphs rendered!")
|
self.set_status("Graphs rendered!")
|
||||||
|
|
||||||
|
|
||||||
def clear_slot(self,slot):
|
def clear_slot(self,slot):
|
||||||
|
|||||||
@ -567,6 +567,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="16" column="0">
|
||||||
|
<widget class="QPushButton" name="export_advanced_metrics">
|
||||||
|
<property name="text">
|
||||||
|
<string>Exporter les variables</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
|||||||
@ -277,6 +277,9 @@ class Ui_MainWindow(object):
|
|||||||
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
self.line.setObjectName("line")
|
self.line.setObjectName("line")
|
||||||
self.formLayout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.line)
|
self.formLayout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.line)
|
||||||
|
self.export_advanced_metrics = QtWidgets.QPushButton(self.tab_6)
|
||||||
|
self.export_advanced_metrics.setObjectName("export_advanced_metrics")
|
||||||
|
self.formLayout.setWidget(16, QtWidgets.QFormLayout.LabelRole, self.export_advanced_metrics)
|
||||||
self.gridLayout_15.addLayout(self.formLayout, 0, 0, 1, 1)
|
self.gridLayout_15.addLayout(self.formLayout, 0, 0, 1, 1)
|
||||||
self.SettingsTab.addTab(self.tab_6, "")
|
self.SettingsTab.addTab(self.tab_6, "")
|
||||||
self.gridLayout_2.addWidget(self.SettingsTab, 1, 0, 1, 1)
|
self.gridLayout_2.addWidget(self.SettingsTab, 1, 0, 1, 1)
|
||||||
@ -519,6 +522,7 @@ class Ui_MainWindow(object):
|
|||||||
self.sigma_r_tot_label.setText(_translate("MainWindow", "σ<R>tot"))
|
self.sigma_r_tot_label.setText(_translate("MainWindow", "σ<R>tot"))
|
||||||
self.MI_p_label.setText(_translate("MainWindow", "MI_p"))
|
self.MI_p_label.setText(_translate("MainWindow", "MI_p"))
|
||||||
self.MI_l_label.setText(_translate("MainWindow", "MI_l"))
|
self.MI_l_label.setText(_translate("MainWindow", "MI_l"))
|
||||||
|
self.export_advanced_metrics.setText(_translate("MainWindow", "Exporter les variables"))
|
||||||
self.SettingsTab.setTabText(self.SettingsTab.indexOf(self.tab_6), _translate("MainWindow", "Valeurs"))
|
self.SettingsTab.setTabText(self.SettingsTab.indexOf(self.tab_6), _translate("MainWindow", "Valeurs"))
|
||||||
self.GraphTabs.setTabText(self.GraphTabs.indexOf(self.tab), _translate("MainWindow", "1"))
|
self.GraphTabs.setTabText(self.GraphTabs.indexOf(self.tab), _translate("MainWindow", "1"))
|
||||||
self.GraphTabs.setTabText(self.GraphTabs.indexOf(self.tab_2), _translate("MainWindow", "2"))
|
self.GraphTabs.setTabText(self.GraphTabs.indexOf(self.tab_2), _translate("MainWindow", "2"))
|
||||||
|
|||||||
@ -751,7 +751,7 @@ qt_resource_struct_v2 = b"\
|
|||||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
|
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
|
||||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||||
\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
||||||
\x00\x00\x01\x87\xdb\xa2\x85\x9e\
|
\x00\x00\x01\x87\xdc\x71\xcf\xb8\
|
||||||
"
|
"
|
||||||
|
|
||||||
qt_version = [int(v) for v in QtCore.qVersion().split('.')]
|
qt_version = [int(v) for v in QtCore.qVersion().split('.')]
|
||||||
|
|||||||
@ -101,51 +101,51 @@ def get_radius_std(discrete_values:list):
|
|||||||
radius.append(get_radius_from_x_y(x,y,x_mean,y_mean))
|
radius.append(get_radius_from_x_y(x,y,x_mean,y_mean))
|
||||||
return get_standard_deviation(radius)
|
return get_standard_deviation(radius)
|
||||||
|
|
||||||
def get_mean_teta(discrete_values:list):
|
def get_mean_theta(discrete_values:list):
|
||||||
"""
|
"""
|
||||||
Get the mean of the teta in the discrete range.
|
Get the mean of the theta in the discrete range.
|
||||||
|
|
||||||
:param discrete_values: discrete values
|
:param discrete_values: discrete values
|
||||||
:return: mean of the teta in the discrete range
|
:return: mean of the theta in the discrete range
|
||||||
|
|
||||||
:Example:
|
:Example:
|
||||||
>>> get_mean_teta([(1,2,3),(4,5,6),(7,8,9)])
|
>>> get_mean_theta([(1,2,3),(4,5,6),(7,8,9)])
|
||||||
0.7853981633974483
|
0.7853981633974483
|
||||||
"""
|
"""
|
||||||
x_mean, y_mean, z_mean = get_x_y_z_mean(discrete_values)
|
x_mean, y_mean, z_mean = get_x_y_z_mean(discrete_values)
|
||||||
teta = []
|
theta = []
|
||||||
for x,y,z in discrete_values:
|
for x,y,z in discrete_values:
|
||||||
teta.append(get_teta_from_x_y(x,y,x_mean,y_mean))
|
theta.append(get_theta_from_x_y(x,y,x_mean,y_mean))
|
||||||
return get_mean(teta)
|
return get_mean(theta)
|
||||||
|
|
||||||
def get_teta_from_x_y(xi:float, yi:float, x_mean:float, y_mean:float):
|
def get_theta_from_x_y(xi:float, yi:float, x_mean:float, y_mean:float):
|
||||||
"""
|
"""
|
||||||
Get the teta from the x and y coordinates.
|
Get the theta from the x and y coordinates.
|
||||||
|
|
||||||
:param xi: x coordinate
|
:param xi: x coordinate
|
||||||
:param yi: y coordinate
|
:param yi: y coordinate
|
||||||
:param x_mean: mean of x coordinates in the discrete range
|
:param x_mean: mean of x coordinates in the discrete range
|
||||||
:param y_mean: mean of y coordinates in the discrete range
|
:param y_mean: mean of y coordinates in the discrete range
|
||||||
:return: teta for this point
|
:return: theta for this point
|
||||||
|
|
||||||
:Example:
|
:Example:
|
||||||
>>> get_teta_from_x_y(1,2,3,4)
|
>>> get_theta_from_x_y(1,2,3,4)
|
||||||
0.7853981633974483
|
0.7853981633974483
|
||||||
"""
|
"""
|
||||||
return math.atan((yi - y_mean)/(xi-x_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):
|
def get_true_theta_from_x_y(xi:float, yi:float, x_mean:float, y_mean:float):
|
||||||
"""
|
"""
|
||||||
Get the teta from the x and y coordinates.
|
Get the theta from the x and y coordinates.
|
||||||
|
|
||||||
:param xi: x coordinate
|
:param xi: x coordinate
|
||||||
:param yi: y coordinate
|
:param yi: y coordinate
|
||||||
:param x_mean: mean of x coordinates in the discrete range
|
:param x_mean: mean of x coordinates in the discrete range
|
||||||
:param y_mean: mean of y coordinates in the discrete range
|
:param y_mean: mean of y coordinates in the discrete range
|
||||||
:return: teta for this point
|
:return: theta for this point
|
||||||
|
|
||||||
:Example:
|
:Example:
|
||||||
>>> get_true_teta_from_x_y(1,2,3,4)
|
>>> get_true_theta_from_x_y(1,2,3,4)
|
||||||
0.7853981633974483
|
0.7853981633974483
|
||||||
"""
|
"""
|
||||||
return math.atan2((xi-x_mean),(yi-y_mean))
|
return math.atan2((xi-x_mean),(yi-y_mean))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user