|
1 |
| -import pandas as pd |
| 1 | +import re |
| 2 | + |
2 | 3 | import numpy as np
|
| 4 | +import pandas as pd |
3 | 5 |
|
4 | 6 |
|
5 | 7 | def read_off(filename):
|
6 |
| - |
7 |
| - with open(filename) as off: |
8 |
| - |
9 |
| - first_line = off.readline() |
| 8 | + with open(filename) as f: |
| 9 | + first_line = f.readline() |
10 | 10 | if "OFF" not in first_line:
|
11 |
| - raise ValueError('The file does not start with the word OFF') |
12 |
| - color = True if "C" in first_line else False |
| 11 | + raise ValueError("The file does not start with the word OFF") |
| 12 | + has_color = "C" in first_line |
| 13 | + |
| 14 | + num_rows = None |
| 15 | + n_points = None |
| 16 | + n_faces = None |
| 17 | + n_header = 1 |
13 | 18 |
|
14 |
| - n_points = 0 |
15 |
| - n_faces = 0 |
| 19 | + # Backtrack to account for faulty headers, e.g. "OFF4 4 0". |
| 20 | + m = re.match(r"^(?P<prefix>\D+)([\d\s]+)$", first_line) |
| 21 | + if m: |
| 22 | + f.seek(len(m.group("prefix"))) |
| 23 | + n_header = 0 |
16 | 24 |
|
17 |
| - count = 1 |
18 |
| - for line in off: |
19 |
| - count += 1 |
| 25 | + # Read header. |
| 26 | + for line in f: |
| 27 | + n_header += 1 |
20 | 28 | if line.startswith("#"):
|
21 | 29 | continue
|
22 | 30 | line = line.strip().split()
|
23 |
| - if len(line) > 1: |
24 |
| - n_points = int(line[0]) |
25 |
| - n_faces = int(line[1]) |
26 |
| - break |
| 31 | + if len(line) <= 1: |
| 32 | + continue |
| 33 | + n_points = int(line[0]) |
| 34 | + n_faces = int(line[1]) |
| 35 | + num_rows = n_points + n_faces |
| 36 | + break |
| 37 | + |
| 38 | + if num_rows is None: |
| 39 | + raise ValueError("The file does not contain a valid header") |
27 | 40 |
|
28 |
| - if (n_points == 0): |
29 |
| - raise ValueError('The file has no points') |
| 41 | + if n_points == 0: |
| 42 | + raise ValueError("The file contains no points") |
30 | 43 |
|
31 | 44 | data = {}
|
32 | 45 | point_names = ["x", "y", "z"]
|
33 |
| - point_types = {'x': np.float32, 'y': np.float32, 'z': np.float32} |
| 46 | + point_types = {"x": np.float32, "y": np.float32, "z": np.float32} |
34 | 47 |
|
35 |
| - if color: |
| 48 | + if has_color: |
36 | 49 | point_names.extend(["red", "green", "blue"])
|
37 |
| - point_types = dict(point_types, **{'red': np.uint8, 'green': np.uint8, 'blue': np.uint8}) |
| 50 | + color_point_types = {"red": np.uint8, "green": np.uint8, "blue": np.uint8} |
| 51 | + point_types = {**point_types, **color_point_types} |
38 | 52 |
|
39 | 53 | data["points"] = pd.read_csv(
|
40 |
| - off, |
| 54 | + f, |
41 | 55 | sep=" ",
|
42 | 56 | header=None,
|
43 | 57 | engine="c",
|
44 | 58 | nrows=n_points,
|
45 | 59 | names=point_names,
|
46 | 60 | dtype=point_types,
|
47 | 61 | index_col=False,
|
48 |
| - comment="#" |
| 62 | + comment="#", |
49 | 63 | )
|
50 | 64 |
|
| 65 | + assert len(data["points"]) == n_points |
| 66 | + |
| 67 | + f.seek(0) |
| 68 | + |
51 | 69 | data["mesh"] = pd.read_csv(
|
52 |
| - filename, |
| 70 | + f, |
53 | 71 | sep=" ",
|
54 | 72 | header=None,
|
55 | 73 | engine="c",
|
56 |
| - skiprows=(count + n_points), |
| 74 | + skiprows=n_header + n_points, |
57 | 75 | nrows=n_faces,
|
58 | 76 | usecols=[1, 2, 3],
|
59 | 77 | names=["v1", "v2", "v3"],
|
60 |
| - comment="#" |
| 78 | + comment="#", |
61 | 79 | )
|
62 |
| - return data |
| 80 | + |
| 81 | + assert len(data["mesh"]) == n_faces |
| 82 | + |
| 83 | + return data |
0 commit comments