Files
speckle-server/packages/fileimport-service/obj/obj_file.py
T
Iain Sproat 96bed71022 fix(logging): Improves error logging and pretty-prints logs during dev & test (#1255)
* Improves error logging
- use pino error logger correctly by passing in error as first argument

* monitor deployment: Filter logging at INFO level and above
* Use structured logging to create parameters for monitoring results
* Add structured logging to obj fileimport service
* Fileimport service, fix and improve logging
    - use child logger with additional context where possible
    - select appropriate logging level
- fix duplicated context in log statement
* REST endpoints, add context to structured logging and remove same context from message
* Webhook service provides context to bound logger to properly use structured logging
    - Pass bound logger containing context to `makeNetworkRequest`
    - do not log url, as it may contain a secret (like Discord's webhook urls), instead log the webhook Id
     - log error message when network call fails
* upload: make better use of structured logging when recording data
* pino-pretty when in dev or test mode
    - pino-pretty configured to send to stderr
* LOG_PRETTY env var
* Silence structured logging during testing
     - can not rely on determining the port number by reading from stdout/stderr
     - instead we determine which port is free, then create our server on that port
     - we then poll that port until the server is ready before commencing tests
* Allow puppeteer to install chromium
* Do not need to install chromium separately
2022-12-13 09:18:28 +00:00

125 lines
4.4 KiB
Python

from mtl_file_collection import MtlFileCollection
import os
import structlog
LOG = structlog.get_logger()
class ObjFile(object):
def __init__(self, file_path) -> None:
self.logged_unsupported = set()
self.mtl_files = MtlFileCollection(os.path.dirname(file_path))
self.crt_object = ""
self.crt_mtl = ""
self.vertices = []
self.vertex_colors = []
self.faces = []
# Constructed in the post-process phase
self.objects = {}
with open(file_path, "r") as f:
while True:
line = f.readline()
if not line:
break
if not line.strip() or line.startswith("#"):
continue
parts = line.strip().split(" ")
if parts[0] == "v":
self.on_v(parts[1:])
elif parts[0] == "l":
self.on_l(parts[1:])
elif parts[0] == "f":
self.on_f(parts[1:])
elif parts[0] == "mtllib":
self.mtl_files.mtllib(" ".join(parts[1:]))
elif parts[0] == "usemtl":
self.crt_mtl = " ".join(parts[1:])
elif parts[0] == "o":
self.crt_object = parts[1]
else:
if parts[0] not in self.logged_unsupported:
LOG.warn("Unsupported OBJ directive: " + parts[0])
self.logged_unsupported.add(parts[0])
self.post_process()
def flatten_vertices(self):
return [coord for point in self.vertices for coord in point]
def on_v(self, params):
r, g, b = None, None, None
w = 1.0
if len(params) == 3:
x, y, z = [float(param) for param in params]
if len(params) == 4:
x, y, z, w = [float(param) for param in params]
if len(params) == 6:
x, y, z, r, g, b = [float(param) for param in params]
self.vertices.append((x, z, y))
if r is None or g is None or b is None:
self.vertex_colors.append(None)
else:
self.vertex_colors.append((r, g, b))
def on_l(self, params):
# TODO: handle lines
pass
def on_f(self, params):
indices = []
for param in params:
# TODO: use texture coordinate index / use vertex normal index?
v_index = int(param.split("/")[0])
# If an index is positive then it refers to the offset in that vertex list, starting at 1.
# If an index is negative then it relatively refers to the end of the vertex list, -1 referring to the last element.
if v_index > 0:
v_index -= 1
indices.append(v_index)
self.faces.append(
{"indices": indices, "object": self.crt_object, "mtl": self.crt_mtl}
)
def post_process(self):
# Step 1: group into object_id/material_id/[faces_with_global_indices]
objects = {}
for face in self.faces:
if face["object"] not in objects:
objects[face["object"]] = {}
obj = objects[face["object"]]
if face["mtl"] not in obj:
obj[face["mtl"]] = []
obj[face["mtl"]].append(face["indices"])
# Step 2: construct final structure: object_id / [{material, local_vertices, vertex_colors, faces_with_local_indices}]
for object in objects:
self.objects[object] = []
for mtl in objects[object].keys():
material = self.mtl_files.get_material(mtl)
vertices = []
vertex_colors = []
faces = []
v_global2local_id = {}
for face in objects[object][mtl]:
for global_v in face:
if global_v not in v_global2local_id:
v_global2local_id[global_v] = len(vertices)
vertices.append(self.vertices[global_v])
vertex_colors.append(self.vertex_colors[global_v])
faces.append([v_global2local_id[global_id] for global_id in face])
self.objects[object].append(
{
"material": material,
"vertices": vertices,
"vertex_colors": vertex_colors,
"faces": faces,
}
)