LayerPolygon.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. from UM.Math.Color import Color
  2. import numpy
  3. class LayerPolygon:
  4. NoneType = 0
  5. Inset0Type = 1
  6. InsetXType = 2
  7. SkinType = 3
  8. SupportType = 4
  9. SkirtType = 5
  10. InfillType = 6
  11. SupportInfillType = 7
  12. MoveCombingType = 8
  13. MoveRetractionType = 9
  14. __jump_map = numpy.logical_or( numpy.arange(10) == NoneType, numpy.arange(10) >= MoveCombingType )
  15. def __init__(self, mesh, line_types, data, line_widths):
  16. self._mesh = mesh
  17. self._types = line_types
  18. self._data = data
  19. self._line_widths = line_widths / 1000
  20. self._vertex_begin = 0
  21. self._vertex_end = 0
  22. self._index_begin = 0
  23. self._index_end = 0
  24. self._jump_mask = self.__jump_map[self._types]
  25. self._jump_count = numpy.sum(self._jump_mask)
  26. self._mesh_line_count = len(self._types)-self._jump_count
  27. self._vertex_count = self._mesh_line_count + numpy.sum( self._types[1:] == self._types[:-1])
  28. # Buffering the colors shouldn't be necessary as it is not
  29. # re-used and can save alot of memory usage.
  30. self._colors = self.__color_map[self._types]
  31. self._color_map = self.__color_map
  32. # When type is used as index returns true if type == LayerPolygon.InfillType or type == LayerPolygon.SkinType or type == LayerPolygon.SupportInfillType
  33. # Should be generated in better way, not hardcoded.
  34. self._isInfillOrSkinTypeMap = numpy.array([0, 0, 0, 1, 0, 0, 1, 1, 0, 0], dtype=numpy.bool)
  35. self._build_cache_line_mesh_mask = None
  36. self._build_cache_needed_points = None
  37. def buildCache(self):
  38. # For the line mesh we do not draw Infill or Jumps. Therefore those lines are filtered out.
  39. self._build_cache_line_mesh_mask = numpy.logical_not(numpy.logical_or(self._jump_mask, self._types == LayerPolygon.InfillType ))
  40. mesh_line_count = numpy.sum(self._build_cache_line_mesh_mask)
  41. self._index_begin = 0
  42. self._index_end = mesh_line_count
  43. self._build_cache_needed_points = numpy.ones((len(self._types), 2), dtype=numpy.bool)
  44. # Only if the type of line segment changes do we need to add an extra vertex to change colors
  45. self._build_cache_needed_points[1:, 0][:, numpy.newaxis] = self._types[1:] != self._types[:-1]
  46. # Mark points as unneeded if they are of types we don't want in the line mesh according to the calculated mask
  47. numpy.logical_and(self._build_cache_needed_points, self._build_cache_line_mesh_mask, self._build_cache_needed_points )
  48. self._vertex_begin = 0
  49. self._vertex_end = numpy.sum( self._build_cache_needed_points )
  50. def build(self, vertex_offset, index_offset, vertices, colors, indices):
  51. if (self._build_cache_line_mesh_mask == None) or (self._build_cache_needed_points == None ):
  52. self.buildCache()
  53. line_mesh_mask = self._build_cache_line_mesh_mask
  54. needed_points_list = self._build_cache_needed_points
  55. # Index to the points we need to represent the line mesh. This is constructed by generating simple
  56. # start and end points for each line. For line segment n these are points n and n+1. Row n reads [n n+1]
  57. # Then then the indices for the points we don't need are thrown away based on the pre-calculated list.
  58. index_list = ( numpy.arange(len(self._types)).reshape((-1, 1)) + numpy.array([[0, 1]]) ).reshape((-1, 1))[needed_points_list.reshape((-1, 1))]
  59. # The relative values of begin and end indices have already been set in buildCache, so we only need to offset them to the parents offset.
  60. self._vertex_begin += vertex_offset
  61. self._vertex_end += vertex_offset
  62. # Points are picked based on the index list to get the vertices needed.
  63. vertices[self._vertex_begin:self._vertex_end, :] = self._data[index_list, :]
  64. # Create an array with colors for each vertex and remove the color data for the points that has been thrown away.
  65. colors[self._vertex_begin:self._vertex_end, :] = numpy.tile(self._colors, (1, 2)).reshape((-1, 4))[needed_points_list.ravel()]
  66. colors[self._vertex_begin:self._vertex_end, :] *= numpy.array([[0.5, 0.5, 0.5, 1.0]], numpy.float32)
  67. # The relative values of begin and end indices have already been set in buildCache, so we only need to offset them to the parents offset.
  68. self._index_begin += index_offset
  69. self._index_end += index_offset
  70. indices[self._index_begin:self._index_end, :] = numpy.arange(self._index_end-self._index_begin, dtype=numpy.int32).reshape((-1, 1))
  71. # When the line type changes the index needs to be increased by 2.
  72. indices[self._index_begin:self._index_end, :] += numpy.cumsum(needed_points_list[line_mesh_mask.ravel(), 0], dtype=numpy.int32).reshape((-1, 1))
  73. # Each line segment goes from it's starting point p to p+1, offset by the vertex index.
  74. # The -1 is to compensate for the neccecarily True value of needed_points_list[0,0] which causes an unwanted +1 in cumsum above.
  75. indices[self._index_begin:self._index_end, :] += numpy.array([self._vertex_begin - 1, self._vertex_begin])
  76. self._build_cache_line_mesh_mask = None
  77. self._build_cache_needed_points = None
  78. def getColors(self):
  79. return self._colors
  80. def mapLineTypeToColor(self, line_types):
  81. return self._color_map[line_types]
  82. def isInfillOrSkinType(self, line_types):
  83. return self._isInfillOrSkinTypeMap[line_types]
  84. def lineMeshVertexCount(self):
  85. return (self._vertex_end - self._vertex_begin)
  86. def lineMeshElementCount(self):
  87. return (self._index_end - self._index_begin)
  88. @property
  89. def types(self):
  90. return self._types
  91. @property
  92. def data(self):
  93. return self._data
  94. @property
  95. def elementCount(self):
  96. return (self._index_end - self._index_begin) * 2 # The range of vertices multiplied by 2 since each vertex is used twice
  97. @property
  98. def lineWidths(self):
  99. return self._line_widths
  100. @property
  101. def jumpMask(self):
  102. return self._jump_mask
  103. @property
  104. def meshLineCount(self):
  105. return self._mesh_line_count
  106. @property
  107. def jumpCount(self):
  108. return self._jump_count
  109. # Calculate normals for the entire polygon using numpy.
  110. def getNormals(self):
  111. normals = numpy.copy(self._data)
  112. normals[:, 1] = 0.0 # We are only interested in 2D normals
  113. # Calculate the edges between points.
  114. # The call to numpy.roll shifts the entire array by one so that
  115. # we end up subtracting each next point from the current, wrapping
  116. # around. This gives us the edges from the next point to the current
  117. # point.
  118. normals = numpy.diff(normals, 1, 0)
  119. # Calculate the length of each edge using standard Pythagoras
  120. lengths = numpy.sqrt(normals[:, 0] ** 2 + normals[:, 2] ** 2)
  121. # The normal of a 2D vector is equal to its x and y coordinates swapped
  122. # and then x inverted. This code does that.
  123. normals[:, [0, 2]] = normals[:, [2, 0]]
  124. normals[:, 0] *= -1
  125. # Normalize the normals.
  126. normals[:, 0] /= lengths
  127. normals[:, 2] /= lengths
  128. return normals
  129. __color_mapping = {
  130. NoneType: Color(1.0, 1.0, 1.0, 1.0),
  131. Inset0Type: Color(1.0, 0.0, 0.0, 1.0),
  132. InsetXType: Color(0.0, 1.0, 0.0, 1.0),
  133. SkinType: Color(1.0, 1.0, 0.0, 1.0),
  134. SupportType: Color(0.0, 1.0, 1.0, 1.0),
  135. SkirtType: Color(0.0, 1.0, 1.0, 1.0),
  136. InfillType: Color(1.0, 0.74, 0.0, 1.0),
  137. SupportInfillType: Color(0.0, 1.0, 1.0, 1.0),
  138. MoveCombingType: Color(0.0, 0.0, 1.0, 1.0),
  139. MoveRetractionType: Color(0.5, 0.5, 1.0, 1.0),
  140. }
  141. # Should be generated in better way, not hardcoded.
  142. __color_map = numpy.array([
  143. [1.0, 1.0, 1.0, 1.0],
  144. [1.0, 0.0, 0.0, 1.0],
  145. [0.0, 1.0, 0.0, 1.0],
  146. [1.0, 1.0, 0.0, 1.0],
  147. [0.0, 1.0, 1.0, 1.0],
  148. [0.0, 1.0, 1.0, 1.0],
  149. [1.0, 0.74, 0.0, 1.0],
  150. [0.0, 1.0, 1.0, 1.0],
  151. [0.0, 0.0, 1.0, 1.0],
  152. [0.5, 0.5, 1.0, 1.0]
  153. ])