🐛 fix(input.py): handle case where faces is None in ScannedObject constructor

 feat(input.py): add support for reading .xyz files and raise InvalidFileFormat exception if file format is not supported
🚸 chore(gui): add warning popup when trying to use Mesh3D graph with .xyz file
🚸 chore(gui): refactor MainWindow.check_input_file method to handle .xyz files
🚸 chore(gui): refactor PreProcessWorker to use ScannedObject.from_file method
🚸 chore(math): add check for faces in verticalise function to avoid errors when faces is None
The ScannedObject constructor now handles the case where faces is None. Support for reading .xyz files has been added, and an InvalidFileFormat exception is raised if the file format is not supported. A warning popup has been added to the MainWindow when trying to use the Mesh3D graph with a .xyz file. The MainWindow.check_input_file method
This commit is contained in:
Djalim Simaila 2023-05-11 15:03:38 +02:00
parent 4430309b41
commit d64c8dd333
4 changed files with 48 additions and 9 deletions

View File

@ -6,6 +6,7 @@ Created on Thu Apr 20 2023
@e-mail: djalim.simaila@inrae.fr
"""
import numpy as np
import os
from utils.files.output import save_output_file
from utils.settings.SettingManager import SettingManager
@ -20,6 +21,11 @@ class ResultFileNotGiven(Exception):
Exception raised when no faces was given.
"""
class InvalidFileFormat(Exception):
"""
Exception raised when the file format is not supported.
"""
class ScannedObject:
"""
This class is used to manage the data of the 3D object.
@ -49,7 +55,7 @@ class ScannedObject:
"""
def __init__(self, vertices, faces=None):
self.vertices = np.asarray(vertices)
self.faces = np.asarray(faces)
self.faces = np.asarray(faces) if faces is not None else None
self.old_delta = None
self.old_discrete = None
self.old_discrete_type = None
@ -58,6 +64,24 @@ class ScannedObject:
self.z = np.asarray([vertex[2] for vertex in vertices])
@staticmethod
def from_file(file_path:str, ratio:float = 1,normalised:str = '')->'ScannedObject':
"""
Create an Object from a file.
:param file_path: Path to the file
:param ratio: Ratio to apply to the vertices
:param normalised: the axis to normalise
:return: A ScannedObject
"""
if os.path.splitext(file_path)[1].lower() == ".xyz":
return ScannedObject.from_xyz_file(file_path, ' ',ratio, normalised)
elif os.path.splitext(file_path)[1].lower() == ".obj":
return ScannedObject.from_obj_file(file_path, ratio, normalised)
else:
raise InvalidFileFormat("The file format is not supported.")
@staticmethod
def from_obj_file(file_path:str, ratio:float = 1,normalised:str = '')->'ScannedObject':
"""
@ -108,7 +132,7 @@ class ScannedObject:
return ScannedObject(list(zip(x,y,z)), triangles, )
@staticmethod
def from_xyz_file(file_path:str, delimiter:str = ' ', normalised:str = '')->'ScannedObject':
def from_xyz_file(file_path:str, delimiter:str = ' ', ratio:float=1, normalised:str = '')->'ScannedObject':
"""
Create an Object from an XYZ file.
@ -121,9 +145,10 @@ class ScannedObject:
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]))
print(line)
x.append(float(line.split(delimiter)[0])* ratio)
y.append(float(line.split(delimiter)[1])* ratio)
z.append(float(line.split(delimiter)[2])* ratio)
if 'x' in normalised:
xmin = min(x)
@ -238,6 +263,14 @@ class ScannedObject:
self.old_discrete = splitted_data
return splitted_data
def has_faces(self)->bool:
"""
Check if the object has faces.
:return: True if the object has faces, False otherwise
"""
return self.faces is not None
def get_faces(self,resolved:bool = False)->list:
"""
Get the faces of the object.

View File

@ -163,16 +163,20 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
- if it exists
- if its extension is .obj
"""
authorised_extensions = [".obj",".xyz"]
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":
if os.path.splitext(self.input_file_path.toPlainText())[1].lower() not in authorised_extensions:
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
if os.path.splitext(self.input_file_path.toPlainText())[1].lower() == ".xyz":
print("test")
ErrorPopup("Attention!, Ce programme ne peut pas redresser les fichiers .xyz, assurez vous que l'objet est deja droit. Il sera aussi impossible d'utiliser le graphe 'Mesh3D'").show_popup()
return True
def check_output_folder(self):
@ -430,7 +434,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
current_slot = slot[0]
graph_type = slot[1]
if graph_type == "Mesh3D":
current_slot.addWidget(render3D(obj,False).native)
if obj.has_faces():
current_slot.addWidget(render3D(obj,False).native)
if graph_type == "Coupe XZ":
current_slot.addWidget(cross_section(obj.get_x(),
obj.get_z(),

View File

@ -43,7 +43,7 @@ class PreProcessWorker(Worker):
"""
# Read the file
self.set_status("Loading file...")
obj = ScannedObject.from_obj_file(self.objpath)
obj = ScannedObject.from_file(self.objpath)
self.update_progress(5)
# Verticalise the object

View File

@ -75,12 +75,13 @@ def get_mass_properties(obj:ScannedObject)->tuple:
inertia[1, 2] = inertia[2, 1] = -(intg[8] - volume * cog[1] * cog[2])
inertia[0, 2] = inertia[2, 0] = -(intg[9] - volume * cog[2] * cog[0])
return volume, cog, inertia
def verticalise(obj:ScannedObject):
"""
Rotate the object so that the principal axis of inertia is vertical
:param obj: Object to analyse
"""
if not obj.has_faces():
return
cog, inertia = get_mass_properties(obj)[1:]
[val,vect] = np.linalg.eig(inertia)