Compare commits

..

4 Commits

Author SHA1 Message Date
b09b72ae73 Working Decimate script 2022-10-10 16:53:04 +02:00
e53991945e Renaming vector to vertex 2022-10-10 14:44:39 +02:00
449aaf7790 Refactoring 2022-09-29 13:52:22 +02:00
a009b661d1 Updating README.md 2022-09-28 15:33:34 +02:00
4 changed files with 1038 additions and 1019 deletions

View File

@ -46,10 +46,10 @@ cela fait, vous pouvez allez sur [localhost:8000](http://localhost:8000) pour
lancer le streaming. Le navigateur télécharge progressivement les données et lancer le streaming. Le navigateur télécharge progressivement les données et
les affiche. les affiche.
Les modèles doivent être sauvegardés dans le dossiers `assets`, et peuvent être Les modèles peuvent être
visualisés en ajouter `?nom_du_modele.obj` à la fin de l'url. Par exemple, visualisés en ajoutant `?chemin/nom_du_modele.obj` à la fin de l'url. Par exemple,
[localhost:8000/?example/suzanne.obja](http://localhost:8000/?example/suzanne.obja) [localhost:8000/?example/bunny.obja](http://localhost:8000/?example/bunny.obja)
chargera le modèle `bunny.obj` du dossier `assets`. Ce modèle est un modèle chargera le modèle `bunny.obja` du dossier `example`. Ce modèle est un modèle
d'exemple, il commence par encoder la version basse résolution du [Stanford d'exemple, il commence par encoder la version basse résolution du [Stanford
bunny](https://graphics.stanford.edu/data/3Dscanrep/), translate tous ses bunny](https://graphics.stanford.edu/data/3Dscanrep/), translate tous ses
sommets, les retranslate vers leurs positions d'origine puis supprime toutes sommets, les retranslate vers leurs positions d'origine puis supprime toutes

File diff suppressed because it is too large Load Diff

94
obja.py
View File

@ -8,11 +8,13 @@ import random
obja model for python. obja model for python.
""" """
class Face: class Face:
""" """
The class that holds a, b, and c, the indices of the vertices of the face. The class that holds a, b, and c, the indices of the vertices of the face.
""" """
def __init__(self, a, b, c, visible = True):
def __init__(self, a, b, c, visible=True):
self.a = a self.a = a
self.b = b self.b = b
self.c = c self.c = c
@ -20,7 +22,7 @@ class Face:
def from_array(array): def from_array(array):
""" """
Initializes a face from an array of strings representing vector indices (starting at 1) Initializes a face from an array of strings representing vertex indices (starting at 1)
""" """
face = Face(0, 0, 0) face = Face(0, 0, 0)
face.set(array) face.set(array)
@ -29,7 +31,7 @@ class Face:
def set(self, array): def set(self, array):
""" """
Sets a face from an array of strings representing vector indices (starting at 1) Sets a face from an array of strings representing vertex indices (starting at 1)
""" """
self.a = int(array[0].split('/')[0]) - 1 self.a = int(array[0].split('/')[0]) - 1
self.b = int(array[1].split('/')[0]) - 1 self.b = int(array[1].split('/')[0]) - 1
@ -52,7 +54,7 @@ class Face:
self.visible = other.visible self.visible = other.visible
return self return self
def test(self, vertices, line = "unknown"): def test(self, vertices, line="unknown"):
""" """
Tests if a face references only vertices that exist when the face is declared. Tests if a face references only vertices that exist when the face is declared.
""" """
@ -69,10 +71,12 @@ class Face:
def __repr__(self): def __repr__(self):
return str(self) return str(self)
class VertexError(Exception): class VertexError(Exception):
""" """
An operation references a vertex that does not exist. An operation references a vertex that does not exist.
""" """
def __init__(self, index, line): def __init__(self, index, line):
""" """
Creates the error from index of the referenced vertex and the line where the error occured. Creates the error from index of the referenced vertex and the line where the error occured.
@ -85,15 +89,17 @@ class VertexError(Exception):
""" """
Pretty prints the error. Pretty prints the error.
""" """
return f'There is no vector {self.index} (line {self.line})' return f"There is no vertex {self.index} (line {self.line})"
class FaceError(Exception): class FaceError(Exception):
""" """
An operation references a face that does not exist. An operation references a face that does not exist.
""" """
def __init__(self, index, line): def __init__(self, index, line):
""" """
Creates the error from index of the referenced face and the line where the error occured. Creates the error from index of the referenced face and the line where the error occurred.
""" """
self.line = line self.line = line
self.index = index self.index = index
@ -105,13 +111,15 @@ class FaceError(Exception):
""" """
return f'There is no face {self.index} (line {self.line})' return f'There is no face {self.index} (line {self.line})'
class FaceVertexError(Exception): class FaceVertexError(Exception):
""" """
An operation references a face vector that does not exist. An operation references a face vertex that does not exist.
""" """
def __init__(self, index, line): def __init__(self, index, line):
""" """
Creates the error from index of the referenced face vector and the line where the error occured. Creates the error from index of the referenced face vertex and the line where the error occured.
""" """
self.line = line self.line = line
self.index = index self.index = index
@ -121,12 +129,14 @@ class FaceVertexError(Exception):
""" """
Pretty prints the error. Pretty prints the error.
""" """
return f'Face has no vector {self.index} (line {self.line})' return f'Face has no vertex {self.index} (line {self.line})'
class UnknownInstruction(Exception): class UnknownInstruction(Exception):
""" """
An instruction is unknown. An instruction is unknown.
""" """
def __init__(self, instruction, line): def __init__(self, instruction, line):
""" """
Creates the error from instruction and the line where the error occured. Creates the error from instruction and the line where the error occured.
@ -141,23 +151,25 @@ class UnknownInstruction(Exception):
""" """
return f'Instruction {self.instruction} unknown (line {self.line})' return f'Instruction {self.instruction} unknown (line {self.line})'
class Model: class Model:
""" """
The OBJA model. The OBJA model.
""" """
def __init__(self): def __init__(self):
""" """
Intializes an empty model. Initializes an empty model.
""" """
self.vertices = [] self.vertices = []
self.faces = [] self.faces = []
self.line = 0 self.line = 0
def get_vector_from_string(self, string): def get_vertex_from_string(self, string):
""" """
Gets a vector from a string representing the index of the vector, starting at 1. Gets a vertex from a string representing the index of the vertex, starting at 1.
To get the vector from its index, simply use model.vertices[i]. To get the vertex from its index, simply use model.vertices[i].
""" """
index = int(string) - 1 index = int(string) - 1
if index >= len(self.vertices): if index >= len(self.vertices):
@ -198,14 +210,14 @@ class Model:
self.vertices.append(np.array(split[1:], np.double)) self.vertices.append(np.array(split[1:], np.double))
elif split[0] == "ev": elif split[0] == "ev":
self.get_vector_from_string(split[1]).set(split[2:]) self.get_vertex_from_string(split[1]).set(split[2:])
elif split[0] == "tv": elif split[0] == "tv":
self.get_vector_from_string(split[1]).translate(split[2:]) self.get_vertex_from_string(split[1]).translate(split[2:])
elif split[0] == "f" or split[0] == "tf": elif split[0] == "f" or split[0] == "tf":
for i in range(1, len(split) - 2): for i in range(1, len(split) - 2):
face = Face.from_array(split[i:i+3]) face = Face.from_array(split[i:i + 3])
face.test(self.vertices, self.line) face.test(self.vertices, self.line)
self.faces.append(face) self.faces.append(face)
@ -223,16 +235,16 @@ class Model:
elif split[0] == "efv": elif split[0] == "efv":
face = self.get_face_from_string(split[1]) face = self.get_face_from_string(split[1])
vector = int(split[2]) vertex = int(split[2])
new_index = int(split[3]) - 1 new_index = int(split[3]) - 1
if vector == 1: if vertex == 1:
face.a = new_index face.a = new_index
elif vector == 2: elif vertex == 2:
face.b = new_index face.b = new_index
elif vector == 3: elif vertex == 3:
face.c = new_index face.c = new_index
else: else:
raise FaceVertexError(vector, self.line) raise FaceVertexError(vertex, self.line)
elif split[0] == "df": elif split[0] == "df":
self.get_face_from_string(split[1]).visible = False self.get_face_from_string(split[1]).visible = False
@ -244,6 +256,7 @@ class Model:
return return
# raise UnknownInstruction(split[0], self.line) # raise UnknownInstruction(split[0], self.line)
def parse_file(path): def parse_file(path):
""" """
Parses a file and returns the model. Parses a file and returns the model.
@ -252,13 +265,15 @@ def parse_file(path):
model.parse_file(path) model.parse_file(path)
return model return model
class Output: class Output:
""" """
The type for a model that outputs as obja. The type for a model that outputs as obja.
""" """
def __init__(self, output, random_color = False):
def __init__(self, output, random_color=False):
""" """
Initializes the index mapping dictionnaries. Initializes the index mapping dictionaries.
""" """
self.vertex_mapping = dict() self.vertex_mapping = dict()
self.face_mapping = dict() self.face_mapping = dict()
@ -270,16 +285,17 @@ class Output:
Adds a new vertex to the model with the specified index. Adds a new vertex to the model with the specified index.
""" """
self.vertex_mapping[index] = len(self.vertex_mapping) self.vertex_mapping[index] = len(self.vertex_mapping)
print('v {} {} {}'.format(vertex[0], vertex[1], vertex[2]), file = self.output) print('v {} {} {}'.format(vertex[0], vertex[1], vertex[2]), file=self.output)
def edit_vertex(self, index, vertex): def edit_vertex(self, index, vertex):
""" """
Changes the coordinates of a vertex. Changes the coordinates of a vertex.
""" """
if len(self.vertex_mapping) == 0: if len(self.vertex_mapping) == 0:
print('ev {} {} {} {}'.format(index, vertex[0], vertex[1],vertex[2]), file = self.output) print('ev {} {} {} {}'.format(index, vertex[0], vertex[1], vertex[2]), file=self.output)
else: else:
print('ev {} {} {} {}'.format(self.vertex_mapping[index] + 1, vertex[0], vertex[1],vertex[2]), file = self.output) print('ev {} {} {} {}'.format(self.vertex_mapping[index] + 1, vertex[0], vertex[1], vertex[2]),
file=self.output)
def add_face(self, index, face): def add_face(self, index, face):
""" """
@ -287,11 +303,11 @@ class Output:
""" """
self.face_mapping[index] = len(self.face_mapping) self.face_mapping[index] = len(self.face_mapping)
print('f {} {} {}'.format( print('f {} {} {}'.format(
self.vertex_mapping[face.a] + 1, self.vertex_mapping[face.a] + 1,
self.vertex_mapping[face.b] + 1, self.vertex_mapping[face.b] + 1,
self.vertex_mapping[face.c] + 1, self.vertex_mapping[face.c] + 1,
), ),
file = self.output file=self.output
) )
if self.random_color: if self.random_color:
@ -300,7 +316,7 @@ class Output:
random.uniform(0, 1), random.uniform(0, 1),
random.uniform(0, 1), random.uniform(0, 1),
random.uniform(0, 1)), random.uniform(0, 1)),
file = self.output file=self.output
) )
def edit_face(self, index, face): def edit_face(self, index, face):
@ -308,14 +324,15 @@ class Output:
Changes the indices of the vertices of the specified face. Changes the indices of the vertices of the specified face.
""" """
print('ef {} {} {} {}'.format( print('ef {} {} {} {}'.format(
self.face_mapping[index] + 1, self.face_mapping[index] + 1,
self.vertex_mapping[face.a] + 1, self.vertex_mapping[face.a] + 1,
self.vertex_mapping[face.b] + 1, self.vertex_mapping[face.b] + 1,
self.vertex_mapping[face.c] + 1 self.vertex_mapping[face.c] + 1
), ),
file = self.output file=self.output
) )
def main(): def main():
if len(sys.argv) == 1: if len(sys.argv) == 1:
print("obja needs a path to an obja file") print("obja needs a path to an obja file")
@ -325,5 +342,6 @@ def main():
print(model.vertices) print(model.vertices)
print(model.faces) print(model.faces)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -6,7 +6,6 @@
# Standard library imports. # Standard library imports.
import sys import sys
if sys.version_info[0] < 3: if sys.version_info[0] < 3:
from SocketServer import ThreadingMixIn from SocketServer import ThreadingMixIn
import BaseHTTPServer import BaseHTTPServer
@ -17,7 +16,7 @@ else:
import http.server as BaseHTTPServer import http.server as BaseHTTPServer
from http.server import SimpleHTTPRequestHandler from http.server import SimpleHTTPRequestHandler
from socketserver import ThreadingMixIn from socketserver import ThreadingMixIn
from urllib.parse import quote, unquote from urllib.parse import quote, unquote
from io import BytesIO as cStringIO from io import BytesIO as cStringIO
import os import os
@ -35,6 +34,7 @@ import errno
DATA_DIR = getcwd() DATA_DIR = getcwd()
class ThreadingHTTPServer(ThreadingMixIn, BaseHTTPServer.HTTPServer): class ThreadingHTTPServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
pass pass
@ -61,7 +61,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
in_file.seek(self.range_from) in_file.seek(self.range_from)
# Add 1 because the range is inclusive # Add 1 because the range is inclusive
left_to_copy = 1 + self.range_to - self.range_from left_to_copy = 1 + self.range_to - self.range_from
buf_length = 64*1024 buf_length = 64 * 1024
bytes_copied = 0 bytes_copied = 0
while bytes_copied < left_to_copy: while bytes_copied < left_to_copy:
read_buf = in_file.read(min(buf_length, left_to_copy - bytes_copied)) read_buf = in_file.read(min(buf_length, left_to_copy - bytes_copied))
@ -125,7 +125,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
file_size = fs.st_size file_size = fs.st_size
if self.range_from is not None: if self.range_from is not None:
if self.range_to is None or self.range_to >= file_size: if self.range_to is None or self.range_to >= file_size:
self.range_to = file_size-1 self.range_to = file_size - 1
self.send_header("Content-Range", self.send_header("Content-Range",
"bytes %d-%d/%d" % (self.range_from, "bytes %d-%d/%d" % (self.range_from,
self.range_to, self.range_to,
@ -184,8 +184,8 @@ class RequestHandler(SimpleHTTPRequestHandler):
def translate_path(self, path): def translate_path(self, path):
""" Override to handle redirects. """ Override to handle redirects.
""" """
path = path.split('?',1)[0] path = path.split('?', 1)[0]
path = path.split('#',1)[0] path = path.split('#', 1)[0]
path = normpath(unquote(path)) path = normpath(unquote(path))
words = path.split('/') words = path.split('/')
words = filter(None, words) words = filter(None, words)
@ -240,12 +240,13 @@ def get_server(port=8000, next_attempts=0, serve_path=None):
else: else:
raise raise
def main(args=None): def main(args=None):
if args is None: if args is None:
args = sys.argv[1:] args = sys.argv[1:]
PORT = 8000 PORT = 8000
if len(args)>0: if len(args) > 0:
PORT = int(args[-1]) PORT = int(args[-1])
serve_path = DATA_DIR serve_path = DATA_DIR
if len(args) > 1: if len(args) > 1:
@ -256,6 +257,6 @@ def main(args=None):
print("serving at port " + str(PORT)) print("serving at port " + str(PORT))
httpd.serve_forever() httpd.serve_forever()
if __name__ == "__main__" :
main()
if __name__ == "__main__":
main()