🐛 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 = ""
 | 
			
		||||
    output_data = ""
 | 
			
		||||
    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:
 | 
			
		||||
        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_y_diffs.append(y_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_xmoy_diffs.append(xmoy_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:
 | 
			
		||||
            minimal_output_data += f"{i} : Z diff {z_diff}\n"
 | 
			
		||||
        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:
 | 
			
		||||
            minimal_output_data += f"{i} : R diff {r_diff}\n"
 | 
			
		||||
        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_y_diff = round(sum(list_y_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_xmoy_diff = round(sum(list_xmoy_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 y : {sum_y_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 xmoy : {sum_xmoy_diff}")
 | 
			
		||||
        print(f"diff moyenne de ymoy : {sum_ymoy_diffs}")
 | 
			
		||||
        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 {minimal_output_file} pour les différences significatives")
 | 
			
		||||
        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,
 | 
			
		||||
            "y": list_y_diffs,
 | 
			
		||||
            "z": list_z_diffs,
 | 
			
		||||
            "teta": list_teta_diffs,
 | 
			
		||||
            "theta": list_theta_diffs,
 | 
			
		||||
            "radius": list_radius_diffs,
 | 
			
		||||
            "xmoy": list_xmoy_diffs,
 | 
			
		||||
            "ymoy": list_ymoy_diffs}
 | 
			
		||||
@ -236,7 +236,7 @@ def test_get_raw_data(obj: ScannedObject, expected_file: str, ndigits: int = 6,
 | 
			
		||||
    print()
 | 
			
		||||
    print("Time to calculate raw data: ", time.time() - now)
 | 
			
		||||
    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)
 | 
			
		||||
    os.remove('tmp_analyse_brute.txt')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,6 @@ Created on Mon Apr 24 2023
 | 
			
		||||
from utils.math import data_extraction as de
 | 
			
		||||
import numpy as np
 | 
			
		||||
from utils.files.input import ScannedObject
 | 
			
		||||
from utils.settings.SettingManager import SettingManager
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
        - Y (en mm)     : list of y 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
 | 
			
		||||
        - Xi-Xmoy       : list of Xi-Xmoy values
 | 
			
		||||
        - Yi-Ymoy       : list of Yi-Ymoy values
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    # 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 = {}
 | 
			
		||||
    for colone in colones:
 | 
			
		||||
        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["Y (en mm)"].append(round(y, 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["Xi-Xmoy"].append(round(x-mean_x, 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 ecart type (en mm)  : list of radius standard deviation values
 | 
			
		||||
    """
 | 
			
		||||
    
 | 
			
		||||
    # 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 = {}
 | 
			
		||||
    for colone in colones:
 | 
			
		||||
        data[colone] = []
 | 
			
		||||
@ -96,25 +107,49 @@ def get_discrete_data(obj:ScannedObject, ndigits:int, delta_z:float=1, update_pr
 | 
			
		||||
        progress += 1
 | 
			
		||||
    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
 | 
			
		||||
    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)"]))
 | 
			
		||||
    for i in range(len(vertices)-1):
 | 
			
		||||
        l += de.get_distance_between_two_vertices(vertices[i], vertices[i+1])
 | 
			
		||||
    vertices = list(zip(discrete_data["X moy (en mm)"],
 | 
			
		||||
                        discrete_data["Y moy (en mm)"],
 | 
			
		||||
                        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])
 | 
			
		||||
    T = l/L
 | 
			
		||||
    update_progress_bar(10)
 | 
			
		||||
    
 | 
			
		||||
    # Volume and surface
 | 
			
		||||
    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)"]])
 | 
			
		||||
    V = np.pi * R * H
 | 
			
		||||
    S = 2 * np.pi * R * H
 | 
			
		||||
    update_progress_bar(30)
 | 
			
		||||
    #
 | 
			
		||||
    
 | 
			
		||||
    # Morphological indicators
 | 
			
		||||
    R_mean = de.get_mean(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)"]])
 | 
			
		||||
 | 
			
		||||
@ -26,11 +26,9 @@ class ScannedObject:
 | 
			
		||||
 | 
			
		||||
    :param vertices: List of verticesm 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 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 y: List of y 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 export: Exports the data to a file
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    :raises FacesNotGiven: If no faces 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.faces = np.asarray(faces)
 | 
			
		||||
        self.old_delta = None
 | 
			
		||||
@ -82,17 +59,16 @@ class ScannedObject:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @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.
 | 
			
		||||
 | 
			
		||||
        :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 normalised: the axis to normalise
 | 
			
		||||
        :return: A ScannedObject
 | 
			
		||||
        """
 | 
			
		||||
        with open(file_path, 'r') as f:
 | 
			
		||||
        with open(file_path, 'r', encoding='utf-8') as f:
 | 
			
		||||
            x, y, z = [], [], []
 | 
			
		||||
            triangles = []
 | 
			
		||||
            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])
 | 
			
		||||
                    else:
 | 
			
		||||
                        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 '):
 | 
			
		||||
                    x.append(float(line.split()[1]) * ratio)
 | 
			
		||||
                    y.append(float(line.split()[2]) * ratio)
 | 
			
		||||
@ -112,55 +92,54 @@ class ScannedObject:
 | 
			
		||||
 | 
			
		||||
            if 'x' in normalised: 
 | 
			
		||||
                xmin = min(x)
 | 
			
		||||
                for count, value in enumerate(x):
 | 
			
		||||
                for count,_ in enumerate(x):
 | 
			
		||||
                    x[count] -= xmin
 | 
			
		||||
 | 
			
		||||
            if 'y' in normalised: 
 | 
			
		||||
                ymin = min(y)
 | 
			
		||||
                for count, value in enumerate(y):
 | 
			
		||||
                for count,_ in enumerate(y):
 | 
			
		||||
                    y[count] -= ymin
 | 
			
		||||
 | 
			
		||||
            if 'z' in normalised: 
 | 
			
		||||
                zmin = min(z)
 | 
			
		||||
                for count, value in enumerate(z):
 | 
			
		||||
                for count,_ in enumerate(z):
 | 
			
		||||
                    z[count] -= zmin
 | 
			
		||||
 | 
			
		||||
        return ScannedObject(list(zip(x,y,z)), triangles, result_file_path)
 | 
			
		||||
        return ScannedObject(list(zip(x,y,z)), triangles, )
 | 
			
		||||
    
 | 
			
		||||
    @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.
 | 
			
		||||
 | 
			
		||||
        :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 normalised: the axis to normalise
 | 
			
		||||
        :return: A ScannedObject
 | 
			
		||||
        """
 | 
			
		||||
        x , y , z = [], [], []
 | 
			
		||||
        with open(file_path, 'r') as f:
 | 
			
		||||
        with open(file_path, 'r',encoding='utf-8') as f:
 | 
			
		||||
            data = f.readlines()
 | 
			
		||||
            for line in data:
 | 
			
		||||
                x.append(float(line.split(delimiter)[0]))
 | 
			
		||||
                y.append(float(line.split(delimiter)[1]))
 | 
			
		||||
                z.append(float(line.split(delimiter)[2]))
 | 
			
		||||
 | 
			
		||||
        if 'x' in normalised: 
 | 
			
		||||
        if 'x' in normalised:
 | 
			
		||||
            xmin = min(x)
 | 
			
		||||
            for count, value in enumerate(x):
 | 
			
		||||
            for count,_ in enumerate(x):
 | 
			
		||||
                x[count] -= xmin
 | 
			
		||||
 | 
			
		||||
        if 'y' in normalised: 
 | 
			
		||||
        if 'y' in normalised:
 | 
			
		||||
            ymin = min(y)
 | 
			
		||||
            for count, value in enumerate(y):
 | 
			
		||||
            for count,_ in enumerate(y):
 | 
			
		||||
                y[count] -= ymin
 | 
			
		||||
 | 
			
		||||
        if 'z' in normalised: 
 | 
			
		||||
        if 'z' in normalised:
 | 
			
		||||
            zmin = min(z)
 | 
			
		||||
            for count, value in enumerate(z):
 | 
			
		||||
            for count,_ in enumerate(z):
 | 
			
		||||
                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:
 | 
			
		||||
        """
 | 
			
		||||
@ -194,6 +173,12 @@ class ScannedObject:
 | 
			
		||||
        return vertices
 | 
			
		||||
 | 
			
		||||
    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":
 | 
			
		||||
            return self.get_discrete_vertices_1(step)
 | 
			
		||||
        return self.get_discrete_vertices_2(step)
 | 
			
		||||
@ -206,10 +191,14 @@ class ScannedObject:
 | 
			
		||||
        :param step: Step of the discretization
 | 
			
		||||
        :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:
 | 
			
		||||
            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):
 | 
			
		||||
@ -223,43 +212,53 @@ class ScannedObject:
 | 
			
		||||
    
 | 
			
		||||
    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
 | 
			
		||||
        first and last point of a slice is greater or equal then the step interval.
 | 
			
		||||
 | 
			
		||||
        :param step: Step of the discretization
 | 
			
		||||
        :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:
 | 
			
		||||
            return self.old_discrete
 | 
			
		||||
        self.old_delta = step
 | 
			
		||||
        self.old_discrete_type = 1
 | 
			
		||||
        L = [[]]
 | 
			
		||||
 | 
			
		||||
        splitted_data = [[]]
 | 
			
		||||
        z = min(self.get_z())
 | 
			
		||||
        sorted_vertices = self.get_vertices(sort=True)
 | 
			
		||||
        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:
 | 
			
		||||
                z = sorted_vertices[index+1][2]
 | 
			
		||||
                L.append([])
 | 
			
		||||
        self.old_discrete = L
 | 
			
		||||
        return L
 | 
			
		||||
                splitted_data.append([])
 | 
			
		||||
        self.old_discrete = splitted_data
 | 
			
		||||
        return splitted_data
 | 
			
		||||
    
 | 
			
		||||
    def get_faces(self,resolved:bool = False)->list:
 | 
			
		||||
        """
 | 
			
		||||
        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
 | 
			
		||||
        """
 | 
			
		||||
        if self.faces is None:
 | 
			
		||||
            raise FacesNotGiven('No faces was given')
 | 
			
		||||
            raise FacesNotGiven('No faces were given')
 | 
			
		||||
        if resolved:
 | 
			
		||||
            return self.vertices[self.faces]
 | 
			
		||||
        return self.faces
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
        """
 | 
			
		||||
@ -304,7 +303,12 @@ class ScannedObject:
 | 
			
		||||
 | 
			
		||||
        :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"):
 | 
			
		||||
        """
 | 
			
		||||
@ -314,12 +318,11 @@ class ScannedObject:
 | 
			
		||||
        :param separator: chars used to separate the values
 | 
			
		||||
        """
 | 
			
		||||
        string = ''
 | 
			
		||||
        with open(file_path, "w") as f:
 | 
			
		||||
            for vertex in self.get_vertices(sort=True):
 | 
			
		||||
                x = round(vertex[0], 6)
 | 
			
		||||
                y = round(vertex[1], 6)
 | 
			
		||||
                z = round(vertex[2], 6)
 | 
			
		||||
                string+=f"{x}{separator}{y}{separator}{z}\n"
 | 
			
		||||
        for vertex in self.get_vertices(sort=True):
 | 
			
		||||
            x = round(vertex[0], 6)
 | 
			
		||||
            y = round(vertex[1], 6)
 | 
			
		||||
            z = round(vertex[2], 6)
 | 
			
		||||
            string+=f"{x}{separator}{y}{separator}{z}\n"
 | 
			
		||||
        save_output_file(file_path,string)
 | 
			
		||||
 | 
			
		||||
    def export_obj(self,file_path):
 | 
			
		||||
@ -329,17 +332,16 @@ class ScannedObject:
 | 
			
		||||
        :param file_path: Path of the file
 | 
			
		||||
        """
 | 
			
		||||
        string = ''
 | 
			
		||||
        with open(file_path, "w") as f:
 | 
			
		||||
            for vertex in self.get_vertices():
 | 
			
		||||
                x = round(vertex[0], 6)
 | 
			
		||||
                y = round(vertex[1], 6)
 | 
			
		||||
                z = round(vertex[2], 6)
 | 
			
		||||
                string+=f"v {x} {y} {z}\n"
 | 
			
		||||
            for face in self.get_faces():
 | 
			
		||||
                string+="f "
 | 
			
		||||
                for vertex in face:
 | 
			
		||||
                    string+=f"{vertex+1} "
 | 
			
		||||
                string+="\n"
 | 
			
		||||
        for vertex in self.get_vertices():
 | 
			
		||||
            x = round(vertex[0], 6)
 | 
			
		||||
            y = round(vertex[1], 6)
 | 
			
		||||
            z = round(vertex[2], 6)
 | 
			
		||||
            string+=f"v {x} {y} {z}\n"
 | 
			
		||||
        for face in self.get_faces():
 | 
			
		||||
            string+="f "
 | 
			
		||||
            for vertex in face:
 | 
			
		||||
                string+=f"{vertex+1} "
 | 
			
		||||
            string+="\n"
 | 
			
		||||
        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
 | 
			
		||||
    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 separator: chars used to separate the values
 | 
			
		||||
 | 
			
		||||
@ -5,10 +5,8 @@ Created on Mon Apr 17 2023
 | 
			
		||||
@auth:   Djalim Simaila
 | 
			
		||||
@e-mail: djalim.simaila@inrae.fr
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from utils.settings.SettingManager import SettingManager
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def format_data(data:dict, separator:str, selected_columns:list = None) -> str:
 | 
			
		||||
    """
 | 
			
		||||
    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
 | 
			
		||||
    for column_name in selected_columns:
 | 
			
		||||
        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:
 | 
			
		||||
            output += column_name + separator
 | 
			
		||||
    output += '\n'
 | 
			
		||||
@ -62,5 +60,5 @@ def save_output_file(output_file:str, content:str):
 | 
			
		||||
    :param output_file: Path to the output file
 | 
			
		||||
    :param content: Content of the output file
 | 
			
		||||
    """
 | 
			
		||||
    with open(output_file, 'w') as f:
 | 
			
		||||
        f.write(content)
 | 
			
		||||
    with open(output_file, 'w',encoding='utf-8') as file:
 | 
			
		||||
        file.write(content)
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,8 @@
 | 
			
		||||
"""
 | 
			
		||||
Created on Fri Apr 21 2023
 | 
			
		||||
@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
 | 
			
		||||
@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 xlabel: Label of the x 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)
 | 
			
		||||
    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 xlabel: Label of the x 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)
 | 
			
		||||
    fig = vp.Fig(show=False)
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,8 @@
 | 
			
		||||
"""
 | 
			
		||||
Created on Fri Apr 21 2023
 | 
			
		||||
@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
 | 
			
		||||
@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
 | 
			
		||||
 | 
			
		||||
class AboutThis(QtWidgets.QMainWindow,Ui_AboutThis):
 | 
			
		||||
    """
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, parent=None) -> None:
 | 
			
		||||
        super().__init__(parent)
 | 
			
		||||
        self.setupUi(self)
 | 
			
		||||
 | 
			
		||||
@ -30,8 +30,12 @@ class ErrorPopup(object):
 | 
			
		||||
        self.button_callback = button_callback
 | 
			
		||||
        self.details = details
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    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.setWindowTitle("Erreur")
 | 
			
		||||
        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.settings.Settings import Settings
 | 
			
		||||
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.graph2D.visplot_render import cross_section, render2D
 | 
			
		||||
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):
 | 
			
		||||
    """
 | 
			
		||||
    Main window of the application, it contains all the UI elements
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        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
 | 
			
		||||
        self.setupUi(self)
 | 
			
		||||
        # Setup buttons listeners
 | 
			
		||||
@ -42,10 +48,12 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
 | 
			
		||||
        self.actionQuitter.triggered.connect(self.close)
 | 
			
		||||
        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.currentIndexChanged.connect(self.layer_changed)
 | 
			
		||||
 | 
			
		||||
        self.graphType = [
 | 
			
		||||
        # Prepare available graph type list for the slots combobox
 | 
			
		||||
        self.graph_type = [
 | 
			
		||||
            "Aucun",
 | 
			
		||||
            "Mesh3D",
 | 
			
		||||
            "Coupe XZ",
 | 
			
		||||
@ -55,15 +63,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
 | 
			
		||||
            "Coupe 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.slot0ComboBox,
 | 
			
		||||
            self.slot1ComboBox,
 | 
			
		||||
@ -78,10 +79,12 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
 | 
			
		||||
            self.slot10ComboBox
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        # Setup all combo boxes with values and listener
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
        # put all slots in a list for conveniance and store their state in a string
 | 
			
		||||
        self.slots = [
 | 
			
		||||
                    [self.slot0,"Aucun"],
 | 
			
		||||
                    [self.slot1,"Aucun"],
 | 
			
		||||
@ -96,19 +99,32 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
 | 
			
		||||
                    [self.slot10,"Aucun"]
 | 
			
		||||
                    ]
 | 
			
		||||
        
 | 
			
		||||
        # Retrieve the slot previous value from the settings
 | 
			
		||||
        for slot_nb,slot in  enumerate(self.slots):
 | 
			
		||||
            slot[1] = SettingManager.get_instance().get_last_graph(slot_nb)
 | 
			
		||||
            self.combo_boxes[slot_nb].setCurrentText(slot[1])
 | 
			
		||||
        
 | 
			
		||||
        # Graph number indicator
 | 
			
		||||
        self.graph_nb =0
 | 
			
		||||
        self.graph_type_changed()
 | 
			
		||||
 | 
			
		||||
        # Construct sub windows
 | 
			
		||||
        self.settings_window = Settings()
 | 
			
		||||
        self.about_window = AboutThis()
 | 
			
		||||
 | 
			
		||||
        # Variable to check if parametters has changed to avoid re-computation
 | 
			
		||||
        self.has_changed = True
 | 
			
		||||
        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):
 | 
			
		||||
        """
 | 
			
		||||
        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]
 | 
			
		||||
        self.input_file_path.setPlainText(file)
 | 
			
		||||
@ -136,19 +153,30 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
 | 
			
		||||
 | 
			
		||||
    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()):
 | 
			
		||||
            ErrorPopup("Fichier d'entrée invalide",button_label="Choisir un fichier d'entrée",button_callback=self.select_file).show_popup()
 | 
			
		||||
        if not os.path.isfile(self.input_file_path.toPlainText()) :
 | 
			
		||||
            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 True
 | 
			
		||||
 | 
			
		||||
    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()):
 | 
			
		||||
            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 True
 | 
			
		||||
 | 
			
		||||
@ -284,13 +312,13 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
 | 
			
		||||
 | 
			
		||||
    def set_obj(self,obj:ScannedObject):
 | 
			
		||||
        """
 | 
			
		||||
        Set the obj to the main window
 | 
			
		||||
        Persists the pre-processed obj
 | 
			
		||||
        """
 | 
			
		||||
        self.obj = obj
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
        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):
 | 
			
		||||
        """
 | 
			
		||||
        Set the raw data to the main window
 | 
			
		||||
        Persists the calculated raw data
 | 
			
		||||
        """
 | 
			
		||||
        self.raw_data = raw_data 
 | 
			
		||||
 | 
			
		||||
    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.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]
 | 
			
		||||
                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]
 | 
			
		||||
                θs = [get_true_theta_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>",
 | 
			
		||||
                                                     "r - <r> en mm",
 | 
			
		||||
                                                     False).native)
 | 
			
		||||
                self.set_status("Graphs rendered!")
 | 
			
		||||
        self.set_status("Graphs rendered!")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def clear_slot(self,slot):
 | 
			
		||||
 | 
			
		||||
@ -567,6 +567,13 @@
 | 
			
		||||
                 </property>
 | 
			
		||||
                </widget>
 | 
			
		||||
               </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>
 | 
			
		||||
             </item>
 | 
			
		||||
            </layout>
 | 
			
		||||
 | 
			
		||||
@ -277,6 +277,9 @@ class Ui_MainWindow(object):
 | 
			
		||||
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
 | 
			
		||||
        self.line.setObjectName("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.SettingsTab.addTab(self.tab_6, "")
 | 
			
		||||
        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.MI_p_label.setText(_translate("MainWindow", "MI_p"))
 | 
			
		||||
        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.GraphTabs.setTabText(self.GraphTabs.indexOf(self.tab), _translate("MainWindow", "1"))
 | 
			
		||||
        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\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('.')]
 | 
			
		||||
 | 
			
		||||
@ -101,51 +101,51 @@ def get_radius_std(discrete_values:list):
 | 
			
		||||
        radius.append(get_radius_from_x_y(x,y,x_mean,y_mean))
 | 
			
		||||
    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
 | 
			
		||||
    :return: mean of the teta in the discrete range
 | 
			
		||||
    :return: mean of the theta in the discrete range
 | 
			
		||||
 | 
			
		||||
    :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
 | 
			
		||||
    """
 | 
			
		||||
    x_mean, y_mean, z_mean = get_x_y_z_mean(discrete_values)
 | 
			
		||||
    teta = []
 | 
			
		||||
    theta = []
 | 
			
		||||
    for x,y,z in discrete_values:
 | 
			
		||||
        teta.append(get_teta_from_x_y(x,y,x_mean,y_mean))
 | 
			
		||||
    return get_mean(teta)
 | 
			
		||||
        theta.append(get_theta_from_x_y(x,y,x_mean,y_mean))
 | 
			
		||||
    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 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
 | 
			
		||||
    :return: theta for this point
 | 
			
		||||
    
 | 
			
		||||
    :Example:
 | 
			
		||||
    >>> get_teta_from_x_y(1,2,3,4)
 | 
			
		||||
    >>> get_theta_from_x_y(1,2,3,4)
 | 
			
		||||
    0.7853981633974483
 | 
			
		||||
    """
 | 
			
		||||
    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 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
 | 
			
		||||
    :return: theta for this point
 | 
			
		||||
    
 | 
			
		||||
    :Example:
 | 
			
		||||
    >>> get_true_teta_from_x_y(1,2,3,4)
 | 
			
		||||
    >>> get_true_theta_from_x_y(1,2,3,4)
 | 
			
		||||
    0.7853981633974483
 | 
			
		||||
    """
 | 
			
		||||
    return math.atan2((xi-x_mean),(yi-y_mean))
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user