savgreader.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. """Reader for NIST SAVG files.
  2. This reader reads in a .savg file and produces a vtkPartionedDatasSetCollection
  3. with one vtkPartitionedDataSet for each type of primitive: points, lines,
  4. and polygons.
  5. """
  6. from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase
  7. from .. import print_error
  8. def get_coords_from_line(line):
  9. """ Given a line, split it, and parse out the coordinates.
  10. Return:
  11. A tuple containing coords (position, color, normal)
  12. """
  13. values = line.split()
  14. pt = None
  15. pt_n = None
  16. pt_col = None
  17. # The first three are always the point coords
  18. if len(values) >= 3:
  19. pt = [float(values[0]), float(values[1]), float(values[2])]
  20. # Then if there are only 6 total, the next three are normal coords
  21. if len(values) == 6:
  22. pt_n = [float(values[3]), float(values[4]), float(values[5])]
  23. else:
  24. if len(values) >= 7: # Otherwise the next 4 are colors
  25. pt_col = [float(values[3]), float(values[4]), float(values[5]), float(values[6])]
  26. if len(values) >= 10: # And if there are more, those are the normals
  27. pt_n = [float(values[7]), float(values[8]), float(values[9])]
  28. return pt, pt_col, pt_n
  29. def not_supported(line):
  30. return (
  31. line.startswith('tri') or
  32. line.startswith('pixel') or
  33. line.startswith('text') or
  34. line.startswith('program') or
  35. line.startswith('attribute') or
  36. line.startswith('nooptimizations')
  37. )
  38. class SAVGReader(VTKPythonAlgorithmBase):
  39. def __init__(self):
  40. VTKPythonAlgorithmBase.__init__(self, nInputPorts=0, nOutputPorts=1, outputType="vtkPartitionedDataSetCollection")
  41. self._filename = None
  42. self._ndata = None
  43. self._timesteps = None
  44. def SetFileName(self, name):
  45. """Specify filename for the file to read."""
  46. if name.lower().endswith("savg"):
  47. if self._filename != name:
  48. self._filename = name
  49. self.Modified()
  50. def RequestData(self, request, inInfoVec, outInfoVec):
  51. from vtkmodules.vtkCommonCore import vtkFloatArray, vtkPoints
  52. from vtkmodules.vtkCommonDataModel import (
  53. vtkCellArray,
  54. vtkCompositeDataSet,
  55. vtkPartitionedDataSet,
  56. vtkPartitionedDataSetCollection,
  57. vtkPolyData
  58. )
  59. output = vtkPartitionedDataSetCollection.GetData(outInfoVec);
  60. partitioned_datasets = []
  61. partitioned_dataset_names = []
  62. # Parse line file
  63. if not self._filename:
  64. print_error("SAVGReader requires a FileName")
  65. return 0
  66. # Stores lines of text from the file associated with each group of
  67. # geometries encountered.
  68. geometries = {
  69. "lines": [],
  70. "points": [],
  71. "poly": []
  72. }
  73. # Read the file and build up data structure to hold the primitives
  74. with open(self._filename, "r") as file:
  75. current = None
  76. for line in file:
  77. parts = line.split("#")
  78. line = parts[0].strip().lower()
  79. if len(line) < 1:
  80. continue
  81. if not_supported(line):
  82. continue
  83. if line.startswith("lin"):
  84. geometries["lines"].append({
  85. "rgba": None,
  86. "values": []
  87. })
  88. current = geometries["lines"][-1]
  89. line_parts = line.split(" ")
  90. if len(line_parts) == 5:
  91. current["rgba"] = [float(n) for n in line_parts[1:]]
  92. elif line.startswith("point"):
  93. geometries["points"].append({
  94. "rgba": None,
  95. "values": [],
  96. })
  97. current = geometries["points"][-1]
  98. line_parts = line.split(" ")
  99. if len(line_parts) == 5:
  100. current["rgba"] = [float(n) for n in line_parts[1:]]
  101. elif line.startswith("poly"):
  102. geometries["poly"].append({
  103. "rgba": None,
  104. "npts": None,
  105. "values": [],
  106. })
  107. current = geometries["poly"][-1]
  108. line_parts = line.split(" ")
  109. if len(line_parts) == 2:
  110. current["npts"] = int(line_parts[1])
  111. elif len(line_parts) == 6:
  112. current["rgba"] = [float(n) for n in line_parts[1:5]]
  113. current["npts"] = int(line_parts[5])
  114. elif line.startswith("end"):
  115. current = None
  116. else:
  117. if current is not None:
  118. if "npts" in current and current["npts"] is not None:
  119. # polygon, known num pts per poly
  120. if len(current["values"]) == current["npts"]:
  121. # Reached the number of points for the current one,
  122. # start a new one.
  123. geometries["poly"].append({
  124. "npts": current["npts"],
  125. "rgba": current["rgba"],
  126. "values": []
  127. })
  128. current = geometries["poly"][-1]
  129. pt, pt_col, pt_n = get_coords_from_line(line)
  130. if pt:
  131. current["values"].append({
  132. "pos": pt,
  133. })
  134. color = pt_col or current["rgba"]
  135. if color:
  136. current["values"][-1]["col"] = color
  137. if pt_n:
  138. current["values"][-1]["norm"] = pt_n
  139. # Build lines polydata if there were any lines
  140. if geometries["lines"]:
  141. line_points = vtkPoints()
  142. line_cells = vtkCellArray()
  143. line_point_colors = vtkFloatArray()
  144. line_point_colors.SetNumberOfComponents(4)
  145. line_point_colors.SetName("rgba_colors")
  146. line_point_normals = vtkFloatArray()
  147. line_point_normals.SetNumberOfComponents(3)
  148. line_point_normals.SetName("vertex_normals")
  149. pt_count = 0
  150. for batch in geometries["lines"]:
  151. num_in_batch = len(batch["values"])
  152. first_in_batch = True
  153. for coord in batch["values"]:
  154. if "pos" in coord:
  155. line_points.InsertNextPoint(coord["pos"])
  156. if "norm" in coord:
  157. line_point_normals.InsertNextTuple(coord["norm"])
  158. if "col" in coord:
  159. line_point_colors.InsertNextTuple(coord["col"])
  160. if first_in_batch:
  161. line_cells.InsertNextCell(num_in_batch)
  162. first_in_batch = False
  163. line_cells.InsertCellPoint(pt_count)
  164. pt_count += 1
  165. output_lines = vtkPolyData()
  166. output_lines.SetPoints(line_points)
  167. output_lines.SetLines(line_cells)
  168. if line_point_colors.GetNumberOfTuples() > 0:
  169. output_lines.GetPointData().AddArray(line_point_colors)
  170. if line_point_normals.GetNumberOfTuples() > 0:
  171. output_lines.GetPointData().AddArray(line_point_normals)
  172. ds = vtkPartitionedDataSet()
  173. ds.SetNumberOfPartitions(1)
  174. ds.SetPartition(0, output_lines)
  175. partitioned_datasets.append(ds)
  176. partitioned_dataset_names.append("Lines")
  177. # Build the points polydata if we found points
  178. if geometries["points"]:
  179. p_points = vtkPoints()
  180. p_cells = vtkCellArray()
  181. p_point_colors = vtkFloatArray()
  182. p_point_colors.SetNumberOfComponents(4)
  183. p_point_colors.SetName("rgba_colors")
  184. p_point_normals = vtkFloatArray()
  185. p_point_normals.SetNumberOfComponents(3)
  186. p_point_normals.SetName("vertex_normals")
  187. p_count = 0
  188. for batch in geometries["points"]:
  189. num_in_batch = len(batch["values"])
  190. first_in_batch = True
  191. for coord in batch["values"]:
  192. if "pos" in coord:
  193. p_points.InsertNextPoint(coord["pos"])
  194. if "norm" in coord:
  195. p_point_normals.InsertNextTuple(coord["norm"])
  196. if "col" in coord:
  197. p_point_colors.InsertNextTuple(coord["col"])
  198. if first_in_batch:
  199. p_cells.InsertNextCell(num_in_batch)
  200. first_in_batch = False
  201. p_cells.InsertCellPoint(p_count)
  202. p_count += 1
  203. output_points = vtkPolyData()
  204. output_points.SetPoints(p_points)
  205. output_points.SetVerts(p_cells)
  206. if p_point_colors.GetNumberOfTuples() > 0:
  207. output_points.GetPointData().AddArray(p_point_colors)
  208. if p_point_normals.GetNumberOfTuples() > 0:
  209. output_points.GetPointData().AddArray(p_point_normals)
  210. ds = vtkPartitionedDataSet()
  211. ds.SetNumberOfPartitions(1)
  212. ds.SetPartition(0, output_points)
  213. partitioned_datasets.append(ds)
  214. partitioned_dataset_names.append("Points")
  215. # Build the polygons if there were any
  216. if geometries["poly"]:
  217. poly_points = vtkPoints()
  218. poly_cells = vtkCellArray()
  219. poly_point_colors = vtkFloatArray()
  220. poly_point_colors.SetNumberOfComponents(4)
  221. poly_point_colors.SetName("rgba_colors")
  222. poly_point_normals = vtkFloatArray()
  223. poly_point_normals.SetNumberOfComponents(3)
  224. poly_point_normals.SetName("vertex_normals")
  225. pt_count = 0
  226. for batch in geometries["poly"]:
  227. num_in_batch = len(batch["values"])
  228. if num_in_batch < 1:
  229. continue
  230. first_in_batch = True
  231. for coord in batch["values"]:
  232. if "pos" in coord:
  233. poly_points.InsertNextPoint(coord["pos"])
  234. if "norm" in coord:
  235. poly_point_normals.InsertNextTuple(coord["norm"])
  236. if "col" in coord:
  237. poly_point_colors.InsertNextTuple(coord["col"])
  238. if first_in_batch:
  239. np_in_cell = num_in_batch
  240. poly_cells.InsertNextCell(np_in_cell)
  241. first_in_batch = False
  242. poly_cells.InsertCellPoint(pt_count)
  243. pt_count += 1
  244. output_polys = vtkPolyData()
  245. output_polys.SetPoints(poly_points)
  246. output_polys.SetPolys(poly_cells)
  247. if poly_point_colors.GetNumberOfTuples() > 0:
  248. output_polys.GetPointData().AddArray(poly_point_colors)
  249. if poly_point_normals.GetNumberOfTuples() > 0:
  250. output_polys.GetPointData().AddArray(poly_point_normals)
  251. ds = vtkPartitionedDataSet()
  252. ds.SetNumberOfPartitions(1)
  253. ds.SetPartition(0, output_polys)
  254. partitioned_datasets.append(ds)
  255. partitioned_dataset_names.append("Polygons")
  256. # Add any partioned datasets we created
  257. output.SetNumberOfPartitionedDataSets(len(partitioned_datasets))
  258. for idx, pds in enumerate(partitioned_datasets):
  259. output.SetPartitionedDataSet(idx, pds)
  260. output.GetMetaData(idx).Set(vtkCompositeDataSet.NAME(), partitioned_dataset_names[idx])
  261. return 1