🐛 fix(input.py): add authorised_extensions as a static variable to ScannedObject class

 feat(input.py): add support for binary and ascii STL files to ScannedObject class
The ScannedObject class now has a static variable called authorised_extensions which contains a list of file extensions that are supported by the class. This improves the readability of the code and makes it easier to maintain. The ScannedObject class now also supports binary and ascii STL files, which increases the flexibility of the class and allows it to handle more file formats.
This commit is contained in:
Djalim Simaila 2023-05-12 11:17:00 +02:00
parent 5a6be4717b
commit 5babe707c6
2 changed files with 82 additions and 4 deletions

View File

@ -5,6 +5,7 @@ Created on Thu Apr 20 2023
@auth: Djalim Simaila @auth: Djalim Simaila
@e-mail: djalim.simaila@inrae.fr @e-mail: djalim.simaila@inrae.fr
""" """
import struct
import numpy as np import numpy as np
import os import os
from utils.files.output import save_output_file from utils.files.output import save_output_file
@ -53,6 +54,9 @@ class ScannedObject:
:raises FacesNotGiven: If no faces was given :raises FacesNotGiven: If no faces was given
:raises ResultFileNotGiven: If no result file was given :raises ResultFileNotGiven: If no result file was given
""" """
authorised_extensions = [".obj",".stl",".xyz"]
def __init__(self, vertices, faces=None): def __init__(self, vertices, faces=None):
self.vertices = np.asarray(vertices) self.vertices = np.asarray(vertices)
self.faces = np.asarray(faces) if faces is not None else None self.faces = np.asarray(faces) if faces is not None else None
@ -78,9 +82,27 @@ class ScannedObject:
return ScannedObject.from_xyz_file(file_path, ' ',ratio, normalised) return ScannedObject.from_xyz_file(file_path, ' ',ratio, normalised)
elif os.path.splitext(file_path)[1].lower() == ".obj": elif os.path.splitext(file_path)[1].lower() == ".obj":
return ScannedObject.from_obj_file(file_path, ratio, normalised) return ScannedObject.from_obj_file(file_path, ratio, normalised)
elif os.path.splitext(file_path)[1].lower() == ".stl":
if open(file_path, 'rb').read(5) == b'solid':
return ScannedObject.from_ascii_stl_file(file_path, ratio, normalised)
else:
return ScannedObject.from_binary_stl_file(file_path, ratio, normalised)
else: else:
raise InvalidFileFormat("The file format is not supported.") raise InvalidFileFormat("The file format is not supported.")
@staticmethod
def from_triangles(triangles:list, normalised:str = '')->'ScannedObject':
"""
Create an Object from a list of triangles.
:param triangles: List of triangles
:param vertices: List of vertices
:return: A ScannedObject
"""
obj = ScannedObject([], None)
obj.update_from_faces(triangles)
obj.normalise(normalised)
return obj
@staticmethod @staticmethod
def from_obj_file(file_path:str, ratio:float = 1,normalised:str = '')->'ScannedObject': def from_obj_file(file_path:str, ratio:float = 1,normalised:str = '')->'ScannedObject':
@ -165,6 +187,60 @@ class ScannedObject:
z[count] -= zmin z[count] -= zmin
return ScannedObject(list(zip(x,y,z))) return ScannedObject(list(zip(x,y,z)))
@staticmethod
def from_binary_stl_file(file_path:str, ratio:float = 1, normalised:str = '')->'ScannedObject':
"""
Create an Object from a binary STL file.
:param file_path: Path to the STL file
:param ratio: Ratio to apply to the vertices
:param normalised: the axis to normalise
:return: A ScannedObject
"""
with open(file_path, 'rb') as f:
data = f.read(84)
n_triangles = int.from_bytes(data[80:84], byteorder='little')
triangles = []
for numero_triangles in range(n_triangles):
f.read(12)
current_triangle = []
for i in range(3):
vertex = list(struct.unpack('fff', f.read(12)))
for point in vertex:
point *= ratio
current_triangle.append(vertex)
triangles.append(current_triangle)
f.read(2)
return ScannedObject.from_triangles(triangles, normalised)
@staticmethod
def from_ascii_stl_file(file_path:str, ratio:float = 1, normalised:str = '')->'ScannedObject':
"""
Create an Object from an STL file.
:param file_path: Path to the STL file
:param ratio: Ratio to apply to the vertices
:param normalised: the axis to normalise
:return: A ScannedObject
"""
with open(file_path, 'r', encoding='utf-8') as f:
triangles = []
vertex_buffer = []
data = f.readlines()
for line in data :
if line.startswith('vertex'):
x = float(line.split()[1]) * ratio
y = float(line.split()[2]) * ratio
z = float(line.split()[3]) * ratio
vertex_buffer.append([x,y,z])
if len(vertex_buffer) == 3:
triangles.append(vertex_buffer)
vertex_buffer = []
return ScannedObject.from_triangles(triangles, normalised)
def get_x(self)->list: def get_x(self)->list:
""" """
Get the x coordinates of the object. Get the x coordinates of the object.
@ -257,8 +333,10 @@ class ScannedObject:
for index,_ in enumerate(sorted_vertices): for index,_ in enumerate(sorted_vertices):
splitted_data[-1].append(sorted_vertices[index]) splitted_data[-1].append(sorted_vertices[index])
if sorted_vertices[index][2] - z > step: if sorted_vertices[index][2] - z > step:
z = sorted_vertices[index+1][2] # if you split on the very last point, dont create an empty slice
splitted_data.append([]) if index + 1 < len(sorted_vertices):
z = sorted_vertices[index+1][2]
splitted_data.append([])
self.old_discrete = splitted_data self.old_discrete = splitted_data
return splitted_data return splitted_data
@ -313,7 +391,6 @@ class ScannedObject:
self.x = self.vertices[:,0] self.x = self.vertices[:,0]
self.y = self.vertices[:,1] self.y = self.vertices[:,1]
self.z = self.vertices[:,2] self.z = self.vertices[:,2]
self.normalise()
def normalise(self, axis:str = 'z'): def normalise(self, axis:str = 'z'):
""" """

View File

@ -163,7 +163,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
- if it exists - if it exists
- if its extension is .obj - if its extension is .obj
""" """
authorised_extensions = [".obj",".xyz"] authorised_extensions = ScannedObject.authorised_extensions
if not os.path.isfile(self.input_file_path.toPlainText()) : if not os.path.isfile(self.input_file_path.toPlainText()) :
ErrorPopup("Fichier d'entrée invalide: Aucun fichier selectionné, ou le fichier n'existe pas", ErrorPopup("Fichier d'entrée invalide: Aucun fichier selectionné, ou le fichier n'existe pas",
button_label="Choisir un fichier d'entrée", button_label="Choisir un fichier d'entrée",