321 lines
11 KiB
Python
321 lines
11 KiB
Python
"""
|
|
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'):
|
|
if "//" in line:
|
|
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])
|
|
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
|