Source code for off_parser.parser.off_parser
import csv
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
__all__ = ['OffParser']
[docs]class OffParser():
"""
An object for parsing and storing data from OFF (Object File Format)
files, which store 3D model data. See `wikipedia <https://en.wikipedia.org/wiki/OFF_(file_format)>`_
for the file format specification.
Parameters
----------
path : str
The path to the ASCII .OFF file.
label : str
The data label.
Attributes
----------
label : str
The data label.
points : array
The array of vertex points of shape (N, 3).
faces : list
A list of lists of point indices constructing each face.
"""
def __init__(self, path, label=None):
self.label = label
if path is not None:
self.load_ascii(path)
def __repr__(self):
return '{}(label={!r}, num_points={!r}, num_faces={!r})'.format(
type(self).__name__, self.label, len(self.points), len(self.faces))
[docs] @classmethod
def from_data(cls, data, label=None):
"""
Creates a new object from parsed data.
Parameters
----------
data : list
A list of data.
label : str
The data label.
"""
obj = cls(path=None, label=label)
obj.load_list(data)
return obj
[docs] def load_ascii(self, path):
"""
Loads an ASCII .OFF file into the object.
Parameters
----------
path : str
The path to the .OFF file.
"""
with open(path, 'rt') as fh:
data = csv.reader(fh, delimiter=' ')
data = list(data)
self.load_list(data)
[docs] def load_list(self, data):
"""
Loads a list of file data into the object.
Parameters
----------
data : list
A list of loaded file data.
"""
# First row should contain OFF
if len(data[0]) != 1 and data[0][0] != 'OFF':
raise ValueError('Data does not include OFF header.')
# Second row should contain number of points and faces
p, f = [int(x) for x in data[1][:2]]
# Rows 2 through p should contain points
points = [r[:3] for r in data[2:p+2]]
# Rows p+2 through f+p+2 should contain faces
faces = []
for r in data[p+2:f+p+2]:
n = int(r[0])
face = [int(x) for x in r[1:n+1]]
faces.append(face)
self.points = np.array(points, dtype='float')
self.faces = faces
[docs] def plot(self, ax=None, symbols={}):
"""
Plots the model in 3D.
Parameters
----------
ax : :class:`matplotlib.axes.Axes`
The axes to which the plot will be added. If None, a new figure and
axes will be created.
symbols : dict
A dictionary of symbols to use for the plot. The following keys
may be used:
* 'points': The point symbols, default is None.
* 'faces': The face color, default is 'g'.
* 'edges': The edge color, default is '#006400'.
Examples
--------
.. plot:: ../examples/toilet_ex1.py
"""
if ax is None:
mx = self.points.max(axis=0)
c = 0.5 * (mx + self.points.min(axis=0))
r = 1.1 * np.max(mx - c)
xlim, ylim, zlim = np.column_stack([c - r, c + r])
fig = plt.figure()
ax = fig.add_subplot(111,
projection='3d',
xlim=xlim,
ylim=ylim,
zlim=zlim,
xlabel='X',
ylabel='Y',
zlabel='Z',
aspect='equal'
)
sym = dict(
points=None,
faces='g',
edges='#006400'
)
sym.update(symbols)
if sym['points'] is not None:
x = self.points
ax.plot(x[:,0], x[:,1], x[:,2], sym['points'])
if sym['faces'] is not None:
if sym['edges'] is None:
sym['edges'] = sym['faces']
v = [self.points[f] for f in self.faces]
poly = Poly3DCollection(v, edgecolor=sym['edges'], facecolor=sym['faces'])
ax.add_collection(poly)
return ax