-
Notifications
You must be signed in to change notification settings - Fork 0
/
Parser.py
155 lines (124 loc) · 5.11 KB
/
Parser.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
from io import TextIOWrapper
from os import path
from typing import Dict, List, Tuple
from Edge import Edge
from Face import Face, FaceVertex
class ParseResults:
"""Class holding a legible representation of the target wavefront .obj file."""
global_props : List[str] = []
object_props : List[str] = []
verts : List[Tuple[float,float,float]] = []
norms : List[Tuple[float,float,float]] = []
texs : List[Tuple[float,float]] = []
faces : List[Face] = []
edges : Dict[Edge, List[Face]] = {}
def display(self):
print("PARSE RESULTS")
print("Globals")
print(self.global_props)
print()
print("Object Properties")
print(self.object_props)
print()
print("Vertices")
print(self.verts)
print()
print("Texture Coordinates")
print(self.texs)
print()
print("Normals")
print(self.norms)
print()
print("Faces")
print(self.faces)
print()
print("Edges")
print(self.edges)
print()
def __init__(self):
self.global_props = []
self.object_props = []
self.verts = []
self.norms = []
self.texs = []
self.faces = []
self.edges = {}
def ConsumeLine(results : ParseResults, tokens : List[str], in_object : bool):
"""Consume a line of unknown type from the .obj file. We ignore these lines, and write them into our properties buffers, which are written back into the output .obj file as they were."""
if(in_object):
results.object_props.append(" ".join(tokens))
else:
results.global_props.append(" ".join(tokens))
def ConsumeVertex(results : ParseResults, tokens : List[str]):
"""Consume a vertex position definition from the .obj file."""
if(len(tokens) != 4):
print("Vertex Coordinates Cannot be n-dimensional (n>3). Exiting.")
exit(1)
results.verts.append(tuple([float(token) for token in tokens[1:]]))
def ConsumeTexture(results : ParseResults, tokens : List[str]):
"""Consume a vertex texture coordinate definition from the .obj file. We do not use them, and simply write them back into the output .obj file as they were."""
if(len(tokens) != 3):
print("Texture Coordinates Cannot be n-dimensional (n>2). Exiting.")
exit(1)
results.texs.append(tuple([float(token) for token in tokens[1:]]))
def ConsumeNormal(results: ParseResults, tokens : List[str]):
"""Consume a vertex normal direction definition from the .obj file. We do not use them, and simply write them back into the output .obj file as they were."""
if(len(tokens) != 4):
print("Vertex Normals Cannot be n-dimensional (n>3). Exiting.")
exit(1)
results.norms.append(tuple([float(token) for token in tokens[1:]]))
def ConsumeFace(results: ParseResults, tokens : List[str]):
"""Consume a face definition from the .obj file. This function also has the side-effect of generating edges, which are used to create the graph later."""
verts : List[FaceVertex] = []
edges : List[Edge] = []
n = len(tokens)
for i in range(1, n):
vert = tokens[i]
if("/" not in vert):
vert = f"{vert}//"
vert_data = [(-1 if i == "" else int(i)-1) for i in vert.split("/")]
if(vert_data[0] == -1):
print("Vertex Position Must Be Specified. Exiting.")
exit(1)
verts.append(FaceVertex(vert_data[0], vert_data[1], vert_data[2]))
next_vert_index = int(tokens[1].split("/")[0])
if(i != n-1):
next_vert_index = int(tokens[i+1].split("/")[0])
edges.append(Edge(vert_data[0], next_vert_index - 1))
face_index = len(results.faces)
this_face = Face(verts, face_index)
for edge in edges:
if(edge in results.edges):
results.edges[edge].append(this_face)
else:
results.edges[edge] = [this_face]
results.faces.append(this_face)
def ConsumeVoid(results : ParseResults, tokens : List[str]):
pass
function_table = {"v": ConsumeVertex, "vt": ConsumeTexture, "vn": ConsumeNormal, "f": ConsumeFace, "l": ConsumeVoid}
def Parse(wavefront : TextIOWrapper) -> ParseResults:
"""Parses a given input stream (assumed to be a .obj file) and returns a ParseResults object."""
result = ParseResults()
in_object = False
for line in wavefront.readlines():
tokens = line.split(" ")
key = tokens[0]
if(in_object and key == "o"):
print("Only One Object is Supported, Skipping any Further Objects.")
return result
if(not in_object and key == "o"):
in_object = True
if(key in function_table):
function_table[key](result, tokens)
else:
ConsumeLine(result, tokens, in_object)
return result
if __name__ == "__main__":
file_path = "examples/flap.obj"
if(not path.exists(file_path)):
print(f"File {path.abspath(file_path)} not found.")
exit(1)
results = None
with open(file_path, "r") as f:
results = Parse(f)
results.display()