From 39d68a8037b8d88597e2fa84beef054a9a265786 Mon Sep 17 00:00:00 2001 From: Djalim Simaila <DjalimS.pro@outlook.fr> Date: Thu, 20 Apr 2023 13:57:06 +0200 Subject: [PATCH] General code cleanup --- integration_tests/data_test.py | 280 ++++++++++++----- main.py | 45 ++- outputchecker.py | 53 ---- test.py | 72 ++--- utils/files/file_data.py | 166 ---------- utils/files/input.py | 318 ++++++++++++++++++++ utils/files/norm.py | 4 + utils/files/output.py | 13 + utils/files/parsers.py | 75 ----- utils/graph2D/plots.py | 11 + utils/math/{utils.py => data_extraction.py} | 42 ++- 11 files changed, 644 insertions(+), 435 deletions(-) delete mode 100644 outputchecker.py delete mode 100644 utils/files/file_data.py create mode 100644 utils/files/input.py delete mode 100644 utils/files/parsers.py create mode 100644 utils/graph2D/plots.py rename utils/math/{utils.py => data_extraction.py} (77%) diff --git a/integration_tests/data_test.py b/integration_tests/data_test.py index ef2889d..0e3db87 100644 --- a/integration_tests/data_test.py +++ b/integration_tests/data_test.py @@ -1,68 +1,114 @@ +""" +This module gives functions to assist tests +""" +import os +import time +import numpy as np +from main import get_discrete_data, get_raw_data from utils.files import output +from utils.files.input import ScannedObject -def check_discrete_data(expected_file, actual_file, ndigits=7,eps=0.00001) : +def check_discrete_data(expected_file: str, actual_file: str, ndigits=7, eps=0.00001, silent: bool = False) -> dict: + """ + Compares the output of the main process with the reference output for the same file, + + This function assume the passed paramters are the discretised one and not the raw, + this function will not test that for the user> + + :param expected_file: The reference file, the one we assume is correct + :param actual_file: The output of the main process + :param ndigits: The number of values to keep after the dot + :param eps: the degree of precision we consider two values different + :return: dict containing the average difference for each variable + """ output_file = "check_discrete_data_full.txt" minimal_output_file = "check_discrete_data_minimal.txt" minimal_output_data = "" output_data = "" expected = [] - x,y,z,r,std = 0,0,0,0,0 + list_x_diffs, list_y_diffs, list_z_diffs, list_r_diffs, list_std_diffs = [], [], [], [], [] + with open(expected_file, "r") as f: expected = f.readlines()[1:] - expected = [line.replace(',','.').split('\t') for line in expected] + expected = [line.replace(',', '.').split('\t') for line in expected] expected = [[float(x) for x in line] for line in expected] actual = [] with open(actual_file, "r") as f: actual = f.readlines()[1:] actual = [line.split('\t') for line in actual] actual = [[float(x) for x in line if x != '\n'] for line in actual] - for i in range(min(len(expected), len(actual))): - x_diff = round(abs(expected[i][0] - actual[i][0]),ndigits) - y_diff = round(abs(expected[i][1] - actual[i][1]),ndigits) - z_diff = round(abs(expected[i][2] - actual[i][2]),ndigits) - r_diff = round(abs(expected[i][3] - actual[i][3]),ndigits) - std_diff = round(abs(expected[i][4] - actual[i][4]),ndigits) - - x += x_diff - y += y_diff - z += z_diff - r += r_diff - std += std_diff - - line = f"{str(i).rjust(4)}:\t X: {str(x_diff).rjust(8)}\t Y: {str(y_diff).rjust(8)}\t Z: {str(z_diff).rjust(8)}\t R: {str(r_diff).rjust(8)}\t STD: {str(std_diff).rjust(8)}" + for point in range(min(len(expected), len(actual))): + # Calculate the difference for each variable + x_diff, y_diff, z_diff, r_diff, std_diff = round(abs(expected[point][0] - actual[point][0]), ndigits), round(abs(expected[point][1] - actual[point][1]), ndigits), round( + abs(expected[point][2] - actual[point][2]), ndigits), round(abs(expected[point][3] - actual[point][3]), ndigits), round(abs(expected[point][4] - actual[point][4]), ndigits) + + # Add the difference to their respective list + list_x_diffs.append(x_diff) + list_y_diffs.append(y_diff) + list_z_diffs.append(z_diff) + list_r_diffs.append(r_diff) + list_std_diffs.append(std_diff) + + # Format the line + line = f"{str(point).rjust(4)}:\t X: {str(x_diff).rjust(8)}\t Y: {str(y_diff).rjust(8)}\t Z: {str(z_diff).rjust(8)}\t R: {str(r_diff).rjust(8)}\t STD: {str(std_diff).rjust(8)}" output_data += line + "\n" + + # If one of the values is greater than epsilon, add the line to the minimal output file if x_diff > eps or y_diff > eps or z_diff > eps or r_diff > eps or std_diff > eps: minimal_output_data += line + "\n" + + # Save output to file output.save_output_file(output_file, output_data) output.save_output_file(minimal_output_file, minimal_output_data) - x = round(x/len(expected),ndigits) - y = round(y/len(expected),ndigits) - z = round(z/len(expected),ndigits) - r = round(r/len(expected),ndigits) - std = round(std/len(expected),ndigits) - print() - print("Analyse données discretisées:") - print(f"difference moyenne X: {x}") - print(f"difference moyenne Y: {y}") - print(f"difference moyenne Z: {z}") - print(f"difference moyenne R: {r}") - print(f"difference moyenne STD: {std}") - print(f"diff globale des fichiers: {(x+y+z+r+std)/5}") - print(f"Voir {output_file} pour plus de détails") - print(f"Voir {minimal_output_file} pour les différences significatives") - print("_"*80) - -def check_raw_data(expected_file, actual_file, ndigits=7,eps=0.00001) : + # Print the results + if not silent: + x = round(sum(list_x_diffs)/len(expected), ndigits) + y = round(sum(list_y_diffs)/len(expected), ndigits) + z = round(sum(list_z_diffs)/len(expected), ndigits) + r = round(sum(list_r_diffs)/len(expected), ndigits) + std = round(sum(list_std_diffs)/len(expected), ndigits) + print() + print("Analyse données discretisées:") + print(f"difference moyenne X: {x}") + print(f"difference moyenne Y: {y}") + print(f"difference moyenne Z: {z}") + print(f"difference moyenne R: {r}") + print(f"difference moyenne STD: {std}") + print(f"diff globale des fichiers: {(x+y+z+r+std)/5}") + print(f"Voir {output_file} pour plus de détails") + print(f"Voir {minimal_output_file} pour les différences significatives") + print("_"*80) + return { + "x": x, + "y": y, + "z": z, + "r": r, + "std": std} + +def check_raw_data(expected_file, actual_file, ndigits=7, eps=0.00001, silent: bool = False) -> dict: + """ + Compares the output of the main process with the reference output for the same file, + + This function assume the passed paramters are the raw one and not the discretised, + this function will not test that for the user. + + :param expected_file: The reference file, the one we assume is correct + :param actual_file: The output of the main process + :param ndigits: The number of values to keep after the dot + :param eps: the degree of precision we consider two values different + :return: dict containing the average difference for each variable + """ output_file = "check_raw_data_full.txt" minimal_output_file = "check_raw_data_minimal.txt" minimal_output_data = "" output_data = "" expected = [] - x,y,z,t,r,xmoy,ymoy = 0,0,0,0,0,0,0 + list_x_diffs, list_y_diffs, list_z_diffs, list_teta_diffs, list_radius_diffs, list_xmoy_diffs, list_ymoy_diffs = [], [], [], [], [], [], [] + with open(expected_file, "r") as f: expected = f.readlines()[1:] - expected = [line.replace(',','.').split('\t') for line in expected] + expected = [line.replace(',', '.').split('\t') for line in expected] expected = [[float(x) for x in line] for line in expected] actual = [] with open(actual_file, "r") as f: @@ -70,24 +116,24 @@ def check_raw_data(expected_file, actual_file, ndigits=7,eps=0.00001) : actual = [line.split('\t') for line in actual] actual = [[float(x) for x in line if x != '\n'] for line in actual] for i in range(min(len(expected), len(actual))): - x_diff = round(abs(expected[i][0] - actual[i][0]),ndigits) - y_diff = round(abs(expected[i][1] - actual[i][1]),ndigits) - z_diff = round(abs(expected[i][2] - actual[i][2]),ndigits) - t_diff = round(abs(expected[i][3] - actual[i][3]),ndigits) - r_diff = round(abs(expected[i][4] - actual[i][4]),ndigits) - xmoy_diff = round(abs(expected[i][5] - actual[i][5]),ndigits) - ymoy_diff = round(abs(expected[i][6] - actual[i][6]),ndigits) - - x += x_diff - y += y_diff - z += z_diff - t += t_diff - r += r_diff - xmoy += xmoy_diff - ymoy += ymoy_diff + # Calculate the absolute difference between expected and actual + x_diff, y_diff, z_diff, t_diff, r_diff, xmoy_diff, ymoy_diff = round(abs(expected[i][0] - actual[i][0]), ndigits), round(abs(expected[i][1] - actual[i][1]), ndigits), round(abs(expected[i][2] - actual[i][2]), ndigits), round( + abs(expected[i][3] - actual[i][3]), ndigits), round(abs(expected[i][4] - actual[i][4]), ndigits), round(abs(expected[i][5] - actual[i][5]), ndigits), round(abs(expected[i][6] - actual[i][6]), ndigits) + + # Add each difference to its respctive list + 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_radius_diffs.append(r_diff) + list_xmoy_diffs.append(xmoy_diff) + list_ymoy_diffs.append(ymoy_diff) + # Format the line line = f"{str(i).rjust(4)}:\t X: {str(x_diff).rjust(8)}\t Y: {str(y_diff).rjust(8)}\t Z: {str(z_diff).rjust(8)}\t T: {str(t_diff).rjust(8)}\t R: {str(r_diff).rjust(8)}\t Xmoy: {str(xmoy_diff).rjust(8)}\t Ymoy: {str(ymoy_diff).rjust(8)}" output_data += line + "\n" + + # If the diff is greater than epsilon, add it to the minimal output file if x_diff > eps: minimal_output_data += f"{i} : X diff {x_diff}\n" if y_diff > eps: @@ -102,26 +148,116 @@ def check_raw_data(expected_file, actual_file, ndigits=7,eps=0.00001) : minimal_output_data += f"{i} : Xi-Xmoy diff {xmoy_diff}\n" if ymoy_diff > eps: minimal_output_data += f"{i} : Yi-Ymoy diff{ymoy_diff}\n" + + # Save output to file output.save_output_file(output_file, output_data) output.save_output_file(minimal_output_file, minimal_output_data) - x = round(x/len(expected),ndigits) - y = round(y/len(expected),ndigits) - z = round(z/len(expected),ndigits) - t = round(t/len(expected),ndigits) - r = round(r/len(expected),ndigits) - xmoy = round(xmoy/len(expected),ndigits) - ymoy = round(ymoy/len(expected),ndigits) - + + # Print the output + if not silent: + 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_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) + print() + print("Analyse données brutes :") + 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 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}") + print(f"Voir {output_file} pour plus de détails") + print(f"Voir {minimal_output_file} pour les différences significatives") + print("_"*80) + + return {"x": list_x_diffs, + "y": list_y_diffs, + "z": list_z_diffs, + "teta": list_teta_diffs, + "radius": list_radius_diffs, + "xmoy": list_xmoy_diffs, + "ymoy": list_ymoy_diffs} + +def check_consistency(obj: ScannedObject, discretised_file_path: str): + """ + This function takes a obj file, and checks if the discretised result file + respect what should be the correct discritisation + + :param obj: The object to check + :param discretised_file_path: The discetised file output + """ + obj.export("verification.txt") + mean_list = [] + moyx, moyy, moyz = parse_result_file(discretised_file_path) + moy = moyy + verticies = obj.get_vertices(sort=True) + position = 0 + while position < len(verticies) and len(moy) > 0: + xi_value = verticies[position][0] + yi_value = verticies[position][1] + zi_value = verticies[position][2] + mean_list.append(yi_value) + mean = np.mean(mean_list) + print(f"searching for {moy[0]}, currently at {position}", end="\r") + if abs(mean - moy[0]) < 0.000001: + moy.pop(0) + mean_list = [] + copyposition = position + if int(verticies[copyposition][2]) >= int(verticies[copyposition+1][2]): + while int(verticies[copyposition][2]) == int(verticies[copyposition-1][2]): + copyposition -= 1 + # Position +1 pour l'allignement des indices avec le numero de ligne + # Position - copyposition + 1 car on se deplace seulement si, la condition est fasse aka, on bouge pas si on est aussi de la ou on doit etre + print("line position :", position+1, "|should have stoped :", position - copyposition + 1, + "position higher| Z difference :", verticies[position][2] - verticies[copyposition-1][2]) + position += 1 + +def test_get_raw_data(obj: ScannedObject, expected_file: str, ndigits: int = 6, eps: float = 0.0001): + """ + Test the get_raw_data function, mesure how long it takes to run it then + checks if the values match with the exepected values + + :param obj: the object to calculate the data from + :param expected_file: reference output to check data against + :param ndigits: The number of digits to keep after the comma + :param eps: the degree of precision we consider two values different + """ + + # Calculate raw data and save it in a file + now = time.time() + data = get_raw_data(obj, ndigits) + 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"])) + check_raw_data(expected_file, 'tmp_analyse_brute.txt', ndigits, eps) + os.remove('tmp_analyse_brute.txt') + +def test_get_discrete_data(obj: ScannedObject, expected_file: str, ndigits: int = 6, eps: float = 0.0001): + """ + Test the get_discrete_data function mesure how long it takes to run it then + checks if the values match with the exepected values + + :param obj: the object to calculate the data from + :param expected_file: reference output to check data against + :param ndigits: The number of digits to keep after the comma + :param eps: the degree of precision we consider two values different + """ + + # Calculate discrete data and save it in a file + now = time.time() + data = get_discrete_data(obj, ndigits) print() - print("Analyse données brutes :") - print(f"diff moyenne de x : {x}") - print(f"diff moyenne de y : {y}") - print(f"diff moyenne de z : {z}") - print(f"diff moyenne de t : {t}") - print(f"diff moyenne de r : {r}") - print(f"diff moyenne de xmoy : {xmoy}") - print(f"diff moyenne de ymoy : {ymoy}") - print(f"diff gloabale des fichiers : {(x+y+z+t+r+xmoy+ymoy)/7}") - print(f"Voir {output_file} pour plus de détails") - print(f"Voir {minimal_output_file} pour les différences significatives") - print("_"*80) + print("Time to calculate discrete data: ", time.time() - now) + output.save_output_file('tmp_analyse_discrete.txt', output.format_data(data, '\t', [ + "X moy (en mm)", "Y moy (en mm)", "Z moy (en mm)", "Rayon moyen (en mm)", "Rayon ecart type (en mm)"])) + check_discrete_data( + expected_file, 'tmp_analyse_discrete.txt', ndigits, eps) + os.remove('tmp_analyse_discrete.txt') diff --git a/main.py b/main.py index 37eb7b1..3eec56a 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,8 @@ -from utils.math import utils +from utils.math import data_extraction from utils.files import output -from utils.files import parsers -from utils.files.file_data import Object +from utils.files.input import ScannedObject -def get_raw_data(obj:Object, ndigits:int)->dict: +def get_raw_data(obj:ScannedObject, ndigits:int)->dict: """ Calculates data from the given object @@ -23,18 +22,18 @@ def get_raw_data(obj:Object, ndigits:int)->dict: for colone in colones: data[colone] = [] for discrete_values in obj.get_discrete_vertices(): - mean_x ,mean_y, mean_z = utils.get_x_y_z_mean(discrete_values) + mean_x ,mean_y, mean_z = data_extraction.get_x_y_z_mean(discrete_values) for x,y,z in discrete_values: 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(utils.get_teta_from_x_y(x,y,mean_x,mean_y), ndigits)) - data["rayon (en mm)"].append(round(utils.get_radius_from_x_y(x,y,mean_x,mean_y), ndigits)) + data["teta (en rad)"].append(round(data_extraction.get_teta_from_x_y(x,y,mean_x,mean_y), ndigits)) + data["rayon (en mm)"].append(round(data_extraction.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)) return data -def get_discrete_data(obj:Object, ndigits:int)->dict: +def get_discrete_data(obj:ScannedObject, ndigits:int)->dict: """ Calculates data from the given object @@ -51,29 +50,43 @@ def get_discrete_data(obj:Object, ndigits:int)->dict: data = {} for colone in colones: data[colone] = [] - cpt = 0 for discrete_values in obj.get_discrete_vertices(): - x,y,z = utils.get_x_y_z_mean(discrete_values) + x,y,z = data_extraction.get_x_y_z_mean(discrete_values) data["X moy (en mm)"].append(round(x, ndigits)) data["Y moy (en mm)"].append(round(y, ndigits)) data["Z moy (en mm)"].append(round(z, ndigits)) - data["Rayon moyen (en mm)"].append(round(utils.get_mean_radius(discrete_values), ndigits)) - data["Rayon ecart type (en mm)"].append(round(utils.get_radius_std(discrete_values), ndigits)) + data["Rayon moyen (en mm)"].append(round(data_extraction.get_mean_radius(discrete_values), ndigits)) + data["Rayon ecart type (en mm)"].append(round(data_extraction.get_radius_std(discrete_values), ndigits)) return data def main(): # Create an object from the given file - obj = parsers.parse_xyz_file("test_cylindre.xyz",normalised='z') + obj = ScannedObject.from_xyz_file("test_cylindre.xyz",normalised='z') # Calculate raw data and save it in a file data = get_raw_data(obj, 6) - output.save_output_file('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"] )) + output.save_output_file('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"] )) # Calculate discrete data and save it in a file data = get_discrete_data(obj, 6) - output.save_output_file('analyse_rayon.txt', output.format_data(data, '\t', ["X moy (en mm)", "Y moy (en mm)", "Z moy (en mm)","Rayon moyen (en mm)","Rayon ecart type (en mm)"] )) - + output.save_output_file('analyse_rayon.txt', + output.format_data(data, + '\t', + ["X moy (en mm)", + "Y moy (en mm)", + "Z moy (en mm)", + "Rayon moyen (en mm)", + "Rayon ecart type (en mm)"] )) if __name__ == '__main__': main() \ No newline at end of file diff --git a/outputchecker.py b/outputchecker.py deleted file mode 100644 index 717819b..0000000 --- a/outputchecker.py +++ /dev/null @@ -1,53 +0,0 @@ -from utils.files.parsers import parse_obj_file, parse_xyz_file -import numpy as np - -def parse_result_file(file_path): - lines = [] - x,y,z = [],[],[] - with open(file_path, "r") as f: - lines = f.readlines()[1:] - for line in lines: - line = line.replace(",", ".") - values = line.split("\t") - x.append(float(values[0])) - y.append(float(values[1])) - z.append(float(values[2])) - return x,y,z - -def verifier_coherance(): - obj = parse_obj_file("datasets/Barette/3 - BARETTE v1.obj",normalised='z') - obj.export("verification.txt") - #obj = parse_xyz_file("datasets/Barette/4 - BARETTE v1.xyz",normalised='') - cpt = 0 - L = [] - moyx, moyy, moyz = parse_result_file("datasets/Barette/BARETTE_Delta 1,0_analyse rayon.txt") - print(len(moyx),len(moyy),len(moyz)) - moy = moyx - verticies = obj.get_vertices(sort=True) - position = 0 - while position < len(verticies) and len(moy) > 0: - x = verticies[position][0] - y = verticies[position][1] - z = verticies[position][2] - L.append(x) - m = np.mean(L) - print(f"searching for {moy[0]}, currently at {position}",end="\r") - if abs(m - moy[0]) < 0.000001: - moy.pop(0) - L = [] - copyposition = position - if int(verticies[copyposition][2]) >= int(verticies[copyposition+1][2]): - while int(verticies[copyposition][2]) == int(verticies[copyposition-1][2]): - copyposition -= 1 - """ - if verticies[position][2] - verticies[copyposition][2] > 1: - copyposition = position + 1 - break - """ - # Position +1 pour l'allignement des indices avec le numero de ligne - # Position - copyposition + 1 car on se deplace seulement si, la condition est fasse aka, on bouge pas si on est aussi de la ou on doit etre - print("index :",position,"|should have stoped :",position - copyposition + 1,"position higher| Z difference :",verticies[position][2] - verticies[copyposition-1][2]) - position += 1 - -if __name__ == "__main__": - verifier_coherance() \ No newline at end of file diff --git a/test.py b/test.py index ec276e3..2eac71a 100644 --- a/test.py +++ b/test.py @@ -1,69 +1,37 @@ -import numpy as np -from main import get_raw_data, get_discrete_data -from utils.files.file_data import Object -from utils.files.output import save_output_file, format_data -from utils.files.parsers import parse_obj_file, parse_xyz_file +from utils.files.input import ScannedObject from integration_tests import data_test -import time -import os -def test_get_raw_data(obj:Object, expected_file:str, ndigits:int = 6, eps:float = 0.0001): - """ - Test the get_raw_data function - """ - - # Calculate raw data and save it in a file - now = time.time() - data = get_raw_data(obj, ndigits) - print() - print("Time to calculate raw data: ", time.time() - now) - save_output_file('tmp_analyse_brute.txt', format_data(data, '\t', ["X (en mm)", "Y (en mm)", "Z (en mm)", "teta (en rad)", "rayon (en mm)","Xi-Xmoy","Yi-Ymoy"] )) - data_test.check_raw_data(expected_file, 'tmp_analyse_brute.txt', ndigits, eps) - os.remove('tmp_analyse_brute.txt') - -def test_get_discrete_data(obj: Object,expected_file:str, ndigits:int = 6, eps:float = 0.0001): - """ - Test the get_discrete_data function - """ - - # Calculate discrete data and save it in a file - now = time.time() - data = get_discrete_data(obj, ndigits) - print() - print("Time to calculate discrete data: ", time.time() - now) - save_output_file('tmp_analyse_discrete.txt', format_data(data, '\t', ["X moy (en mm)", "Y moy (en mm)", "Z moy (en mm)","Rayon moyen (en mm)","Rayon ecart type (en mm)"] )) - data_test.check_discrete_data(expected_file, 'tmp_analyse_discrete.txt', ndigits, eps) - os.remove('tmp_analyse_discrete.txt') - -def test(): +def test_differences(): eps = 0.000001 + obj = ScannedObject.from_xyz_file("datasets/Barette/4 - BARETTE v1.xyz", + "datasets/Barette/BARETTE_Delta 1,0_analyse rayon.txt", normalised='z') + + data_test.test_get_raw_data(obj, + "datasets/Barette/BARETTE_Delta 1,0_analyse brute.txt", + eps=eps) - #obj = parse_obj_file("datasets/Barette/3 - BARETTE v1.obj",normalised='z') - #obj = parse_xyz_file("datasets/Barette/4 - BARETTE v1.xyz",normalised='z') - obj = parse_xyz_file("datasets/Barette/4 - BARETTE v1.xyz","datasets/Barette/BARETTE_Delta 1,0_analyse rayon.txt",normalised='z') + data_test.test_get_discrete_data(obj, + "datasets/Barette/BARETTE_Delta 1,0_analyse rayon.txt", + eps=eps) - test_get_raw_data(obj, - "datasets/Barette/BARETTE_Delta 1,0_analyse brute.txt", - eps=eps) - - test_get_discrete_data(obj, - "datasets/Barette/BARETTE_Delta 1,0_analyse rayon.txt", - eps=eps) -def show_diff_between_obj_and_xyz(): - obj1 = parse_obj_file("datasets/Barette/3 - BARETTE v1.obj",normalised='z') - obj2 = parse_xyz_file("datasets/Barette/4 - BARETTE v1.xyz",normalised='z') +def show_diff_two_obj(): + obj1 = ScannedObject.from_obj_file( + "datasets/Barette/3 - BARETTE v1.obj", normalised='z') + obj2 = ScannedObject.from_xyz_file( + "datasets/Barette/4 - BARETTE v1.xyz", normalised='z') obj2verts = obj2.get_vertices(sort=True) for count, values in enumerate(obj1.get_vertices(sort=True)): L = [abs(values[i] - obj2verts[count][i]) for i in range(len(values))] - print(*L,sep="\t") + print(*L, sep="\t") def count_elements_in_discrete_array(): - obj = parse_xyz_file("datasets/Barette/4 - BARETTE v1.xyz","datasets/Barette/BARETTE_Delta 1,0_analyse rayon.txt",normalised='z') + obj = ScannedObject.from_xyz_file("datasets/Barette/4 - BARETTE v1.xyz", + "datasets/Barette/BARETTE_Delta 1,0_analyse rayon.txt", normalised='z') cpt = 0 for i in obj.bruteforce_discretization(): - print(f"nb of element in z{cpt} to z{cpt+1}:",len(i)) + print(f"nb of element in z{cpt} to z{cpt+1}:", len(i)) cpt += 1 if __name__ == "__main__": diff --git a/utils/files/file_data.py b/utils/files/file_data.py deleted file mode 100644 index 087effa..0000000 --- a/utils/files/file_data.py +++ /dev/null @@ -1,166 +0,0 @@ -""" -This module contains the File class. -""" -import numpy as np -class FaceNotGiven(Exception): - """ - Exception raised when no faces was given. - """ - -def parse_result_file(file_path): - lines = [] - x,y,z = [],[],[] - with open(file_path, "r") as f: - lines = f.readlines()[1:] - for line in lines: - line = line.replace(",", ".") - values = line.split("\t") - x.append(float(values[0])) - y.append(float(values[1])) - z.append(float(values[2])) - return x,y,z - -class Object: - """ - This class is used to manage the data of the 3D object. - """ - def __init__(self, vertices, faces=None, result_file_path=None): - self.vertices = vertices - self.faces = faces - self.result_file_path = result_file_path - self.bruteforce_discretization_result = None - self.x = [vertex[0] for vertex in vertices] - self.y = [vertex[1] for vertex in vertices] - self.z = [vertex[2] for vertex in vertices] - - def get_x(self)->list: - """ - Get the x coordinates of the object. - return: x coordinates - """ - return self.x - - def get_y(self)->list: - """ - Get the y coordinates of the object. - return: y coordinates - """ - return self.y - - def get_z(self)->list: - """ - Get the z coordinates of the object. - return: z coordinates - """ - return self.z - - def get_vertices(self, sort:bool = False): - """ - Get the vertices of the object. - :param sort: Sort the vertices by z coordinate - :return: vertices - """ - - vertices = self.vertices if not sort else sorted(self.vertices, key=lambda vertex: vertex[2]) - return vertices - - def get_discrete_vertices(self, step:float = 1): - """ - Discretize the vertices of the object. - :param step: Step of the discretization - :return: Discretized vertices - """ - current_interval = int(min(self.get_z())) - splitted_data = [[]] - for line in self.get_vertices(sort=True): - # TODO check distance instead of equality - if line[2] >= current_interval + step: - splitted_data.append([]) - current_interval += step - splitted_data[-1].append(line) - if splitted_data[0] == []: - splitted_data = splitted_data[1:] - return splitted_data - - def get_discrete_vertices2(self, step:float = 1): - """ - fait un tour de boucle en plus - """ - cpt = 0 - L = [[]] - for vertex in self.get_vertices(sort=True): - step = 1 - L[-1].append(vertex) - if vertex[2] > cpt + step: - cpt += step - L.append([]) - return L - - def get_faces(self)->list: - """ - Get the faces of the object. - :return: faces - """ - if self.faces is None: - raise FaceNotGiven('No faces was given') - return self.faces - - def get_data(self)->dict: - """ - Get the 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} - - def bruteforce_discretization(self): - """ - Discretize the object. - :param step: Step of the discretization - :return: Discretized object - """ - if self.bruteforce_discretization_result: - return self.bruteforce_discretization_result - if self.result_file_path is None: - raise Exception("No result file was given") - L = [] - splitted_data = [[]] - moyx, moyy, moyz = parse_result_file(self.result_file_path) - moy = moyx - verticies = self.get_vertices(sort=True) - position = 0 - while position < len(verticies): - print(position/len(verticies)*100,end="\r") - x = verticies[position][0] - y = verticies[position][1] - z = verticies[position][2] - L.append(x) - splitted_data[-1].append(verticies[position]) - m = np.mean(L) - if len(moy) > 0 and abs(m - moy[0]) < 0.000001: - moy.pop(0) - L = [] - splitted_data.append([]) - copyposition = position - while int(verticies[copyposition][2]) == int(verticies[copyposition-1][2]): - copyposition -= 1 - position += 1 - print(50*" ") - self.bruteforce_discretization_result = splitted_data - return splitted_data - - def export(self, file_path:str): - """ - Export the object in a file. - :param file_path: Path of the file - """ - with open(file_path, "w") as f: - cpt = 0 - for vertex in self.get_vertices(sort=True): - x = round(vertex[0], 6) - y = round(vertex[1], 6) - z = round(vertex[2], 6) - #f.write(f"{x}\t{y}\t{z}\n") - f.write(f"{cpt}\n") - f.write(f"{x}\n{y}\n{z}\n") - f.write(f"{15*'-'}\n") - \ No newline at end of file diff --git a/utils/files/input.py b/utils/files/input.py new file mode 100644 index 0000000..0da7d37 --- /dev/null +++ b/utils/files/input.py @@ -0,0 +1,318 @@ +""" +This module contains the functions to parse the input files, and create a ScannedObject. +""" +import numpy as np +from utils.files.output import save_output_file + + +class FacesNotGiven(Exception): + """ + Exception raised when no faces was given. + """ + +class ResultFileNotGiven(Exception): + """ + Exception raised when no faces was given. + """ + +class ScannedObject: + """ + This class is used to manage the data of the 3D object. + + :param vertices: List of vertices + :param faces: List of faces + :param result_file_path: Path to the result file (deprecated, used for the bruteforce discretization) + + :static method from_xyz_file(): Creates a ScannedObject from a .xyz file + :static method from_obj_file(): Creates a ScannedObject from a .obj file + :method get_x(): Returns the x values of the vertices + :method get_y(): Returns the y values of the vertices + :method get_z(): Returns the z values of the vertices + :method get_vertices(): Returns the vertices + :method get_faces(): Returns the faces + :method get_discrete_vertices(): Returns the discrete vertices + :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)]] + #TODO Add and test exemples + """ + def __init__(self, vertices, faces=None, result_file_path=None): + self.vertices = vertices + self.faces = faces + # Deprecated + self.result_file_path = result_file_path + self.bruteforce_discretization_result = None + # + self.x = [vertex[0] for vertex in vertices] + self.y = [vertex[1] for vertex in vertices] + self.z = [vertex[2] for vertex in vertices] + + + @staticmethod + def from_obj_file(file_path:str, result_file_path:str = None, 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: + x, y, z = [], [], [] + triangles = [] + data = f.readlines() + for line in data : + if line.startswith('f'): + # Face indices start at 1, not 0 + triangles.append([int(line.split()[1])-1, int(line.split()[2])-1, int(line.split()[3])-1]) + elif line.startswith('v'): + x.append(float(line.split()[1]) * ratio) + y.append(float(line.split()[2]) * ratio) + z.append(float(line.split()[3]) * ratio) + + if 'x' in normalised: + xmin = min(x) + for count, value in enumerate(x): + x[count] -= xmin + + if 'y' in normalised: + ymin = min(y) + for count, value in enumerate(y): + y[count] -= ymin + + if 'z' in normalised: + zmin = min(z) + for count, value in enumerate(z): + z[count] -= zmin + + return ScannedObject(list(zip(x,y,z)), triangles, result_file_path) + + @staticmethod + def from_xyz_file(file_path: str, result_file_path:str = None, 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: + 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: + xmin = min(x) + for count, value in enumerate(x): + x[count] -= xmin + + if 'y' in normalised: + ymin = min(y) + for count, value in enumerate(y): + y[count] -= ymin + + if 'z' in normalised: + zmin = min(z) + for count, value in enumerate(z): + z[count] -= zmin + return ScannedObject(list(zip(x,y,z)), result_file_path=result_file_path) + + def get_x(self)->list: + """ + Get the x coordinates of the object. + return: x coordinates + """ + return self.x + + def get_y(self)->list: + """ + Get the y coordinates of the object. + return: y coordinates + """ + return self.y + + def get_z(self)->list: + """ + Get the z coordinates of the object. + return: z coordinates + """ + return self.z + + def get_vertices(self, sort:bool = False): + """ + Get the vertices of the object. + :param sort: Sort the vertices by z coordinate + :return: vertices + """ + + vertices = self.vertices if not sort else sorted(self.vertices, key=lambda vertex: vertex[2]) + return vertices + + def get_discrete_vertices(self, step:float = 1): + """ + Discretize the vertices of the object. + :param step: Step of the discretization + :return: Discretized vertices + """ + current_interval = int(min(self.get_z())) + splitted_data = [[]] + for line in self.get_vertices(sort=True): + # TODO check distance instead of equality + if line[2] >= current_interval + step: + splitted_data.append([]) + current_interval += step + splitted_data[-1].append(line) + return splitted_data + + def get_discrete_vertices2(self, step:float = 1): + """ + Deprecated + """ + cpt = 0 + L = [[]] + for vertex in self.get_vertices(sort=True): + step = 1 + L[-1].append(vertex) + if vertex[2] > cpt + step: + cpt += step + L.append([]) + return L + + def get_discrete_vertices3(self, step:float = 1): + """ + Deprecated + """ + cpt = 0 + L = [[]] + z = min(self.get_z()) + sorted = self.get_vertices(sort=True) + for index in range(len(sorted)): + L[-1].append(sorted[index]) + if sorted[index][2] - z > step: + z = sorted[index][2] + L.append([]) + return L + + + def get_faces(self)->list: + """ + Get the faces of the object. + :return: faces + """ + if self.faces is None: + raise FacesNotGiven('No faces was given') + return self.faces + + def get_data(self)->dict: + """ + Get the 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} + + def bruteforce_discretization(self): + """ + Deprecated + TODO Remove this when its not needed anymore + """ + if self.bruteforce_discretization_result: + return self.bruteforce_discretization_result + if self.result_file_path is None: + raise ResultFileNotGiven("No result file was given") + L = [] + splitted_data = [[]] + moyx, moyy, moyz = parse_result_file(self.result_file_path) + moy = moyx + verticies = self.get_vertices(sort=True) + position = 0 + while position < len(verticies): + print(position/len(verticies)*100,end="\r") + x = verticies[position][0] + y = verticies[position][1] + z = verticies[position][2] + L.append(x) + splitted_data[-1].append(verticies[position]) + m = np.mean(L) + if len(moy) > 0 and abs(m - moy[0]) < 0.000001: + moy.pop(0) + L = [] + splitted_data.append([]) + copyposition = position + while int(verticies[copyposition][2]) == int(verticies[copyposition-1][2]): + copyposition -= 1 + position += 1 + print(50*" ") + self.bruteforce_discretization_result = splitted_data + return splitted_data + + def export(self, file_path:str,separator:str="\t"): + """ + Export the object in a file. + :param file_path: Path of the file + :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" + save_output_file(file_path,string) + + +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 + + :param file_path: Path of the file + :param separator: chars used to separate the values + :return: x, y, z + + :Example: + >>> parse_result_file("test.txt") + ([1.0, 2.0, 3.0], [1.0, 2.0, 3.0], [1.0, 2.0, 3.0]) + """ + lines = [] + x, y, z = [], [], [] + with open(file_path, "r") as f: + lines = f.readlines()[1:] + for line in lines: + line = line.replace(",", ".") + values = line.split(separator) + x.append(float(values[0])) + y.append(float(values[1])) + z.append(float(values[2])) + return x, y, z diff --git a/utils/files/norm.py b/utils/files/norm.py index e4531d1..61c1d61 100644 --- a/utils/files/norm.py +++ b/utils/files/norm.py @@ -1,3 +1,7 @@ +""" +Deprecated +""" + def denormalizeXYZ(filePath:str, output:str): """ Denormalize an XYZ file diff --git a/utils/files/output.py b/utils/files/output.py index 185cc3a..3eca9ab 100644 --- a/utils/files/output.py +++ b/utils/files/output.py @@ -10,6 +10,19 @@ def format_data(data:dict, separator:str, selected_columns:list = None) -> str: :param selected_columns: Columns to be saved :param separator: Separator of the columns :return: Formatted data + + Example: + >>> data = { + ... 'col1': [1, 2, 3], + ... 'col2': [4, 5, 6], + ... 'col3': [7, 8, 9] + ... } + >>> format_data(data, separator=';') + 'col1;col2;col3 + 1 ;4 ;7 + 2 ;5 ;8 + 3 ;6 ;9 + ' """ output = '' if selected_columns is None: diff --git a/utils/files/parsers.py b/utils/files/parsers.py deleted file mode 100644 index 8633ebb..0000000 --- a/utils/files/parsers.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -This module contains functions to parse files. -""" -from utils.files.file_data import Object - -def parse_obj_file(file_path:str, result_file_path:str = None, ratio:float = 1,normalised:str = '')->Object: - """ - Parse an OBJ file and return a dict with the vertices and faces - - :param filePath: Path to the OBJ file - :param ratio: Ratio to apply to the vertices - :param cornered: If True, the vertices will be cornered - :return: A dict with the vertices and faces - """ - with open(file_path, 'r') as f: - x, y, z = [], [], [] - triangles = [] - data = f.readlines() - for line in data : - if line.startswith('f'): - # Face indices start at 1, not 0 - triangles.append([int(line.split()[1])-1, int(line.split()[2])-1, int(line.split()[3])-1]) - elif line.startswith('v'): - x.append(float(line.split()[1]) * ratio) - y.append(float(line.split()[2]) * ratio) - z.append(float(line.split()[3]) * ratio) - - if 'x' in normalised: - xmin = min(x) - for count, value in enumerate(x): - x[count] -= xmin - - if 'y' in normalised: - ymin = min(y) - for count, value in enumerate(y): - y[count] -= ymin - - if 'z' in normalised: - zmin = min(z) - for count, value in enumerate(z): - z[count] -= zmin - - return Object(list(zip(x,y,z)), triangles, result_file_path) - -def parse_xyz_file(file_path: str, result_file_path:str = None, delimiter: str = ' ',normalised:str = '') -> Object: - """ - Parses an xyz file and returns a dict containing the coordinates. - - :param file: The xyz file to be parsed. - :param delimiter: The delimiter used in the xyz file. - :return: A dictionary containing the coordinates. - """ - x , y , z = [], [], [] - with open(file_path, 'r') 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: - xmin = min(x) - for count, value in enumerate(x): - x[count] -= xmin - - if 'y' in normalised: - ymin = min(y) - for count, value in enumerate(y): - y[count] -= ymin - - if 'z' in normalised: - zmin = min(z) - for count, value in enumerate(z): - z[count] -= zmin - return Object(list(zip(x,y,z)), result_file_path=result_file_path) \ No newline at end of file diff --git a/utils/graph2D/plots.py b/utils/graph2D/plots.py new file mode 100644 index 0000000..be38083 --- /dev/null +++ b/utils/graph2D/plots.py @@ -0,0 +1,11 @@ +import matplotlib.pyplot as plt + +def render2D(values:list): + """ + Render a 2D model using matplotlib + :param values: A list with the values + """ + fig = plt.figure() + ax = fig.add_subplot() + ax.plot(values) + plt.show() \ No newline at end of file diff --git a/utils/math/utils.py b/utils/math/data_extraction.py similarity index 77% rename from utils/math/utils.py rename to utils/math/data_extraction.py index e30a74d..4e82b7d 100644 --- a/utils/math/utils.py +++ b/utils/math/data_extraction.py @@ -7,6 +7,13 @@ import math def get_mean(values:list): """ Get the mean of the values. + + :param values: values + :return: mean of the values + + :Example: + >>> get_mean([1,2,3,4,5]) + 3.0 """ return np.mean(values) @@ -16,6 +23,10 @@ def get_standard_deviation(values:list): :param values: values :return: standard deviation of the values + + :Example: + >>> get_standard_deviation([1,2,3,4,5]) + 1.4142135623730951 """ return np.std(values) @@ -26,6 +37,10 @@ def get_x_y_z_mean(discrete_values:list): :param x: x coordinates :param y: y coordinates :return: mean of x and y coordinates in the discrete range + + :Example: + >>> get_x_y_z_mean([(1,2,3),(4,5,6),(7,8,9)]) + (4.0, 5.0, 6.0) """ x = [vertex[0] for vertex in discrete_values] y = [vertex[1] for vertex in discrete_values] @@ -41,6 +56,10 @@ def get_radius_from_x_y(xi:float, yi:float, x_mean:float, y_mean:float): :param x_mean: mean of x coordinates in the discrete range :param y_mean: mean of y coordinates in the discrete range :return: radius for this point + + :Example: + >>> get_radius_from_x_y(1,2,3,4) + 2.8284271247461903 """ return np.sqrt(np.power((xi - x_mean), 2) + np.power((yi - y_mean), 2)) @@ -50,6 +69,10 @@ def get_mean_radius(discrete_values:list): :param discrete_values: discrete values :return: mean of the radius in the discrete range + + :Example: + >>> get_mean_radius([(1,2,3),(4,5,6),(7,8,9)]) + 2.82842712474619 """ x_mean, y_mean, z_mean = get_x_y_z_mean(discrete_values) radius = [] @@ -63,6 +86,10 @@ def get_radius_std(discrete_values:list): :param discrete_values: discrete values :return: standard deviation of the radius in the discrete range + + :Example: + >>> get_radius_std([(1,2,3),(4,5,6),(7,8,9)]) + 2.8284271247461903 """ x_mean, y_mean, z_mean = get_x_y_z_mean(discrete_values) radius = [] @@ -76,6 +103,10 @@ def get_mean_teta(discrete_values:list): :param discrete_values: discrete values :return: mean of the teta in the discrete range + + :Example: + >>> get_mean_teta([(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 = [] @@ -92,5 +123,14 @@ def get_teta_from_x_y(xi:float, yi:float, x_mean:float, y_mean:float): :param x_mean: mean of x coordinates in the discrete range :param y_mean: mean of y coordinates in the discrete range :return: teta for this point + + :Example: + >>> get_teta_from_x_y(1,2,3,4) + 0.7853981633974483 """ - return math.atan((xi - x_mean)/(yi - y_mean)) \ No newline at end of file + return math.atan((xi - x_mean)/(yi - y_mean)) + +#todo fix examples +if __name__ == "__main__": + import doctest + doctest.testmod() \ No newline at end of file -- GitLab