```"""Reader for NIST SAVG files.

with one vtkPartitionedDataSet for each type of primitive: points, lines,
and polygons.

"""

from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase

from .. import print_error

[docs]def get_coords_from_line(line):
""" Given a line, split it, and parse out the coordinates.

Return:

A tuple containing coords (position, color, normal)
"""
values = line.split()

pt = None
pt_n = None
pt_col = None

# The first three are always the point coords
if len(values) >= 3:
pt = [float(values[0]), float(values[1]), float(values[2])]

# Then if there are only 6 total, the next three are normal coords
if len(values) == 6:
pt_n = [float(values[3]), float(values[4]), float(values[5])]
else:
if len(values) >= 7:  # Otherwise the next 4 are colors
pt_col = [float(values[3]), float(values[4]), float(values[5]), float(values[6])]
if len(values) >= 10:  # And if there are more, those are the normals
pt_n = [float(values[7]), float(values[8]), float(values[9])]

return pt, pt_col, pt_n

[docs]def not_supported(line):
return (
line.startswith('tri') or
line.startswith('pixel') or
line.startswith('text') or
line.startswith('program') or
line.startswith('attribute') or
line.startswith('nooptimizations')
)

def __init__(self):
VTKPythonAlgorithmBase.__init__(self, nInputPorts=0, nOutputPorts=1,
outputType="vtkPartitionedDataSetCollection")
self._filename = None
self._ndata = None
self._timesteps = None

[docs]    def SetFileName(self, name):
"""Specify filename for the file to read."""
if name.lower().endswith("savg"):
if self._filename != name:
self._filename = name
self.Modified()

[docs]    def RequestData(self, request, inInfoVec, outInfoVec):
from vtkmodules.vtkCommonCore import vtkFloatArray, vtkPoints
from vtkmodules.vtkCommonDataModel import (
vtkCellArray,
vtkCompositeDataSet,
vtkPartitionedDataSet,
vtkPartitionedDataSetCollection,
vtkPolyData
)

output = vtkPartitionedDataSetCollection.GetData(outInfoVec);
partitioned_datasets = []
partitioned_dataset_names = []

# Parse line file
if not self._filename:
return 0

# Stores lines of text from the file associated with each group of
# geometries encountered.
geometries = {
"lines": [],
"points": [],
"poly": []
}

# Read the file and build up data structure to hold the primitives
with open(self._filename, "r") as file:
current = None
for line in file:
parts = line.split("#")
line = parts[0].strip().lower()

if len(line) < 1:
continue

if not_supported(line):
continue

if line.startswith("lin"):
geometries["lines"].append({
"rgba": None,
"values": []
})
current = geometries["lines"][-1]
line_parts = line.split(" ")
if len(line_parts) == 5:
current["rgba"] = [float(n) for n in line_parts[1:]]
elif line.startswith("point"):
geometries["points"].append({
"rgba": None,
"values": [],
})
current = geometries["points"][-1]
line_parts = line.split(" ")
if len(line_parts) == 5:
current["rgba"] = [float(n) for n in line_parts[1:]]
elif line.startswith("poly"):
geometries["poly"].append({
"rgba": None,
"npts": None,
"values": [],
})
current = geometries["poly"][-1]
line_parts = line.split(" ")
if len(line_parts) == 2:
current["npts"] = int(line_parts[1])
elif len(line_parts) == 6:
current["rgba"] = [float(n) for n in line_parts[1:5]]
current["npts"] = int(line_parts[5])
elif line.startswith("end"):
current = None
else:
if current is not None:
if "npts" in current and current["npts"] is not None:
# polygon, known num pts per poly
if len(current["values"]) == current["npts"]:
# Reached the number of points for the current one,
# start a new one.
geometries["poly"].append({
"npts": current["npts"],
"rgba": current["rgba"],
"values": []
})
current = geometries["poly"][-1]
pt, pt_col, pt_n = get_coords_from_line(line)
if pt:
current["values"].append({
"pos": pt,
})
color = pt_col or current["rgba"]
if color:
current["values"][-1]["col"] = color
if pt_n:
current["values"][-1]["norm"] = pt_n

# Build lines polydata if there were any lines
if geometries["lines"]:
line_points = vtkPoints()
line_cells = vtkCellArray()
line_point_colors = vtkFloatArray()
line_point_colors.SetNumberOfComponents(4)
line_point_colors.SetName("rgba_colors")
line_point_normals = vtkFloatArray()
line_point_normals.SetNumberOfComponents(3)
line_point_normals.SetName("vertex_normals")

pt_count = 0

for batch in geometries["lines"]:
num_in_batch = len(batch["values"])
first_in_batch = True
for coord in batch["values"]:
if "pos" in coord:
line_points.InsertNextPoint(coord["pos"])
if "norm" in coord:
line_point_normals.InsertNextTuple(coord["norm"])
if "col" in coord:
line_point_colors.InsertNextTuple(coord["col"])

if first_in_batch:
line_cells.InsertNextCell(num_in_batch)
first_in_batch = False

line_cells.InsertCellPoint(pt_count)

pt_count += 1

output_lines = vtkPolyData()

output_lines.SetPoints(line_points)
output_lines.SetLines(line_cells)

if line_point_colors.GetNumberOfTuples() > 0:

if line_point_normals.GetNumberOfTuples() > 0:

ds = vtkPartitionedDataSet()
ds.SetNumberOfPartitions(1)
ds.SetPartition(0, output_lines)

partitioned_datasets.append(ds)
partitioned_dataset_names.append("Lines")

# Build the points polydata if we found points
if geometries["points"]:
p_points = vtkPoints()
p_cells = vtkCellArray()
p_point_colors = vtkFloatArray()
p_point_colors.SetNumberOfComponents(4)
p_point_colors.SetName("rgba_colors")
p_point_normals = vtkFloatArray()
p_point_normals.SetNumberOfComponents(3)
p_point_normals.SetName("vertex_normals")

p_count = 0

for batch in geometries["points"]:
num_in_batch = len(batch["values"])
first_in_batch = True
for coord in batch["values"]:
if "pos" in coord:
p_points.InsertNextPoint(coord["pos"])
if "norm" in coord:
p_point_normals.InsertNextTuple(coord["norm"])
if "col" in coord:
p_point_colors.InsertNextTuple(coord["col"])

if first_in_batch:
p_cells.InsertNextCell(num_in_batch)
first_in_batch = False

p_cells.InsertCellPoint(p_count)

p_count += 1

output_points = vtkPolyData()

output_points.SetPoints(p_points)
output_points.SetVerts(p_cells)

if p_point_colors.GetNumberOfTuples() > 0:

if p_point_normals.GetNumberOfTuples() > 0:

ds = vtkPartitionedDataSet()
ds.SetNumberOfPartitions(1)
ds.SetPartition(0, output_points)

partitioned_datasets.append(ds)
partitioned_dataset_names.append("Points")

# Build the polygons if there were any
if geometries["poly"]:
poly_points = vtkPoints()
poly_cells = vtkCellArray()
poly_point_colors = vtkFloatArray()
poly_point_colors.SetNumberOfComponents(4)
poly_point_colors.SetName("rgba_colors")
poly_point_normals = vtkFloatArray()
poly_point_normals.SetNumberOfComponents(3)
poly_point_normals.SetName("vertex_normals")

pt_count = 0

for batch in geometries["poly"]:
num_in_batch = len(batch["values"])
if num_in_batch < 1:
continue
first_in_batch = True
for coord in batch["values"]:
if "pos" in coord:
poly_points.InsertNextPoint(coord["pos"])
if "norm" in coord:
poly_point_normals.InsertNextTuple(coord["norm"])
if "col" in coord:
poly_point_colors.InsertNextTuple(coord["col"])

if first_in_batch:
np_in_cell = num_in_batch
poly_cells.InsertNextCell(np_in_cell)
first_in_batch = False

poly_cells.InsertCellPoint(pt_count)

pt_count += 1

output_polys = vtkPolyData()

output_polys.SetPoints(poly_points)
output_polys.SetPolys(poly_cells)

if poly_point_colors.GetNumberOfTuples() > 0:

if poly_point_normals.GetNumberOfTuples() > 0:

ds = vtkPartitionedDataSet()
ds.SetNumberOfPartitions(1)
ds.SetPartition(0, output_polys)

partitioned_datasets.append(ds)
partitioned_dataset_names.append("Polygons")

# Add any partioned datasets we created
output.SetNumberOfPartitionedDataSets(len(partitioned_datasets))

for idx, pds in enumerate(partitioned_datasets):
output.SetPartitionedDataSet(idx, pds)