Layer.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. # Copyright (c) 2019 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from typing import List
  4. import numpy
  5. from UM.Mesh.MeshBuilder import MeshBuilder
  6. from UM.Mesh.MeshData import MeshData
  7. from cura.LayerPolygon import LayerPolygon
  8. class Layer:
  9. def __init__(self, layer_id: int) -> None:
  10. self._id = layer_id
  11. self._height = 0.0
  12. self._thickness = 0.0
  13. self._polygons = [] # type: List[LayerPolygon]
  14. self._vertex_count = 0
  15. self._element_count = 0
  16. @property
  17. def height(self):
  18. return self._height
  19. @property
  20. def thickness(self):
  21. return self._thickness
  22. @property
  23. def polygons(self) -> List[LayerPolygon]:
  24. return self._polygons
  25. @property
  26. def vertexCount(self):
  27. return self._vertex_count
  28. @property
  29. def elementCount(self):
  30. return self._element_count
  31. def setHeight(self, height: float) -> None:
  32. self._height = height
  33. def setThickness(self, thickness: float) -> None:
  34. self._thickness = thickness
  35. def lineMeshVertexCount(self) -> int:
  36. result = 0
  37. for polygon in self._polygons:
  38. result += polygon.lineMeshVertexCount()
  39. return result
  40. def lineMeshElementCount(self) -> int:
  41. result = 0
  42. for polygon in self._polygons:
  43. result += polygon.lineMeshElementCount()
  44. return result
  45. def lineMeshCumulativeTypeChangeCount(self, path: int) -> int:
  46. """ The number of line-type changes in this layer up until #path.
  47. See also LayerPolygon::cumulativeTypeChangeCounts.
  48. :param path: The path-index up until which the cumulative changes are counted.
  49. :return: The cumulative number of line-type changes up until this path.
  50. """
  51. result = 0
  52. for polygon in self._polygons:
  53. num_counts = len(polygon.cumulativeTypeChangeCounts)
  54. if path < num_counts:
  55. return result + polygon.cumulativeTypeChangeCounts[path]
  56. path -= num_counts
  57. result += polygon.cumulativeTypeChangeCounts[num_counts - 1]
  58. return result
  59. def build(self, vertex_offset, index_offset, vertices, colors, line_dimensions, feedrates, extruders, line_types, indices):
  60. result_vertex_offset = vertex_offset
  61. result_index_offset = index_offset
  62. self._vertex_count = 0
  63. self._element_count = 0
  64. for polygon in self._polygons:
  65. polygon.build(result_vertex_offset, result_index_offset, vertices, colors, line_dimensions, feedrates, extruders, line_types, indices)
  66. result_vertex_offset += polygon.lineMeshVertexCount()
  67. result_index_offset += polygon.lineMeshElementCount()
  68. self._vertex_count += polygon.vertexCount
  69. self._element_count += polygon.elementCount
  70. return result_vertex_offset, result_index_offset
  71. def createMesh(self) -> MeshData:
  72. return self.createMeshOrJumps(True)
  73. def createJumps(self) -> MeshData:
  74. return self.createMeshOrJumps(False)
  75. # Defines the two triplets of local point indices to use to draw the two faces for each line segment in createMeshOrJump
  76. __index_pattern = numpy.array([[0, 3, 2, 0, 1, 3]], dtype = numpy.int32 )
  77. def createMeshOrJumps(self, make_mesh: bool) -> MeshData:
  78. builder = MeshBuilder()
  79. line_count = 0
  80. if make_mesh:
  81. for polygon in self._polygons:
  82. line_count += polygon.meshLineCount
  83. else:
  84. for polygon in self._polygons:
  85. line_count += polygon.jumpCount
  86. # Reserve the necessary space for the data upfront
  87. builder.reserveFaceAndVertexCount(2 * line_count, 4 * line_count)
  88. for polygon in self._polygons:
  89. # Filter out the types of lines we are not interested in depending on whether we are drawing the mesh or the jumps.
  90. index_mask = numpy.logical_not(polygon.jumpMask) if make_mesh else polygon.jumpMask
  91. # Create an array with rows [p p+1] and only keep those we want to draw based on make_mesh
  92. points = numpy.concatenate((polygon.data[:-1], polygon.data[1:]), 1)[index_mask.ravel()]
  93. # Line types of the points we want to draw
  94. line_types = polygon.types[index_mask]
  95. # Shift the z-axis according to previous implementation.
  96. if make_mesh:
  97. points[polygon.isInfillOrSkinType(line_types), 1::3] -= 0.01
  98. else:
  99. points[:, 1::3] += 0.01
  100. # Create an array with normals and tile 2 copies to match size of points variable
  101. normals = numpy.tile( polygon.getNormals()[index_mask.ravel()], (1, 2))
  102. # Scale all normals by the line width of the current line so we can easily offset.
  103. normals *= (polygon.lineWidths[index_mask.ravel()] / 2)
  104. # Create 4 points to draw each line segment, points +- normals results in 2 points each.
  105. # After this we reshape to one point per line.
  106. f_points = numpy.concatenate((points-normals, points+normals), 1).reshape((-1, 3))
  107. # __index_pattern defines which points to use to draw the two faces for each lines egment, the following linesegment is offset by 4
  108. f_indices = ( self.__index_pattern + numpy.arange(0, 4 * len(normals), 4, dtype=numpy.int32).reshape((-1, 1)) ).reshape((-1, 3))
  109. f_colors = numpy.repeat(polygon.mapLineTypeToColor(line_types), 4, 0)
  110. builder.addFacesWithColor(f_points, f_indices, f_colors)
  111. return builder.build()