LayerData.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. # Copyright (c) 2015 Ultimaker B.V.
  2. # Cura is released under the terms of the AGPLv3 or higher.
  3. from UM.Mesh.MeshData import MeshData
  4. from UM.Mesh.MeshBuilder import MeshBuilder
  5. from UM.Math.Color import Color
  6. from UM.Math.Vector import Vector
  7. import numpy
  8. class LayerData(MeshData):
  9. def __init__(self):
  10. super().__init__()
  11. self._layers = {}
  12. self._element_counts = {}
  13. def addLayer(self, layer):
  14. if layer not in self._layers:
  15. self._layers[layer] = Layer(layer)
  16. def addPolygon(self, layer, polygon_type, data, line_width):
  17. if layer not in self._layers:
  18. self.addLayer(layer)
  19. p = Polygon(self, polygon_type, data, line_width)
  20. self._layers[layer].polygons.append(p)
  21. def getLayer(self, layer):
  22. if layer in self._layers:
  23. return self._layers[layer]
  24. def getLayers(self):
  25. return self._layers
  26. def getElementCounts(self):
  27. return self._element_counts
  28. def setLayerHeight(self, layer, height):
  29. if layer not in self._layers:
  30. self.addLayer(layer)
  31. self._layers[layer].setHeight(height)
  32. def setLayerThickness(self, layer, thickness):
  33. if layer not in self._layers:
  34. self.addLayer(layer)
  35. self._layers[layer].setThickness(thickness)
  36. def build(self):
  37. vertex_count = 0
  38. for layer, data in self._layers.items():
  39. vertex_count += data.vertexCount()
  40. vertices = numpy.empty((vertex_count, 3), numpy.float32)
  41. colors = numpy.empty((vertex_count, 4), numpy.float32)
  42. indices = numpy.empty((vertex_count, 2), numpy.int32)
  43. offset = 0
  44. for layer, data in self._layers.items():
  45. offset = data.build(offset, vertices, colors, indices)
  46. self._element_counts[layer] = data.elementCount
  47. self.clear()
  48. self.addVertices(vertices)
  49. self.addColors(colors)
  50. self.addIndices(indices.flatten())
  51. class Layer():
  52. def __init__(self, layer_id):
  53. self._id = layer_id
  54. self._height = 0.0
  55. self._thickness = 0.0
  56. self._polygons = []
  57. self._element_count = 0
  58. @property
  59. def height(self):
  60. return self._height
  61. @property
  62. def thickness(self):
  63. return self._thickness
  64. @property
  65. def polygons(self):
  66. return self._polygons
  67. @property
  68. def elementCount(self):
  69. return self._element_count
  70. def setHeight(self, height):
  71. self._height = height
  72. def setThickness(self, thickness):
  73. self._thickness = thickness
  74. def vertexCount(self):
  75. result = 0
  76. for polygon in self._polygons:
  77. result += polygon.vertexCount()
  78. return result
  79. def build(self, offset, vertices, colors, indices):
  80. result = offset
  81. for polygon in self._polygons:
  82. if polygon.type == Polygon.InfillType or polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType:
  83. continue
  84. polygon.build(result, vertices, colors, indices)
  85. result += polygon.vertexCount()
  86. self._element_count += polygon.elementCount
  87. return result
  88. def createMesh(self):
  89. return self.createMeshOrJumps(True)
  90. def createJumps(self):
  91. return self.createMeshOrJumps(False)
  92. def createMeshOrJumps(self, make_mesh):
  93. builder = MeshBuilder()
  94. for polygon in self._polygons:
  95. if make_mesh and (polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType):
  96. continue
  97. if not make_mesh and not (polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType):
  98. continue
  99. poly_color = polygon.getColor()
  100. points = numpy.copy(polygon.data)
  101. if polygon.type == Polygon.InfillType or polygon.type == Polygon.SkinType or polygon.type == Polygon.SupportInfillType:
  102. points[:,1] -= 0.01
  103. if polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType:
  104. points[:,1] += 0.01
  105. normals = polygon.getNormals()
  106. # Scale all by the line width of the polygon so we can easily offset.
  107. normals *= (polygon.lineWidth / 2)
  108. #TODO: Use numpy magic to perform the vertex creation to speed up things.
  109. for i in range(len(points)):
  110. start = points[i - 1]
  111. end = points[i]
  112. normal = normals[i - 1]
  113. point1 = Vector(data = start - normal)
  114. point2 = Vector(data = start + normal)
  115. point3 = Vector(data = end + normal)
  116. point4 = Vector(data = end - normal)
  117. builder.addQuad(point1, point2, point3, point4, color = poly_color)
  118. return builder.getData()
  119. class Polygon():
  120. NoneType = 0
  121. Inset0Type = 1
  122. InsetXType = 2
  123. SkinType = 3
  124. SupportType = 4
  125. SkirtType = 5
  126. InfillType = 6
  127. SupportInfillType = 7
  128. MoveCombingType = 8
  129. MoveRetractionType = 9
  130. def __init__(self, mesh, polygon_type, data, line_width):
  131. self._mesh = mesh
  132. self._type = polygon_type
  133. self._data = data
  134. self._line_width = line_width / 1000
  135. self._color = self.__color_map[polygon_type]
  136. def build(self, offset, vertices, colors, indices):
  137. self._begin = offset
  138. self._end = self._begin + len(self._data) - 1
  139. vertices[self._begin:self._end + 1, :] = self._data[:, :]
  140. colors[self._begin:self._end + 1, :] = numpy.array([self._color.r * 0.5, self._color.g * 0.5, self._color.b * 0.5, self._color.a], numpy.float32)
  141. for i in range(self._begin, self._end):
  142. indices[i, 0] = i
  143. indices[i, 1] = i + 1
  144. indices[self._end, 0] = self._end
  145. indices[self._end, 1] = self._begin
  146. def getColor(self):
  147. return self._color
  148. def vertexCount(self):
  149. return len(self._data)
  150. @property
  151. def type(self):
  152. return self._type
  153. @property
  154. def data(self):
  155. return self._data
  156. @property
  157. def elementCount(self):
  158. return ((self._end - self._begin) + 1) * 2 #The range of vertices multiplied by 2 since each vertex is used twice
  159. @property
  160. def lineWidth(self):
  161. return self._line_width
  162. # Calculate normals for the entire polygon using numpy.
  163. def getNormals(self):
  164. normals = numpy.copy(self._data)
  165. normals[:,1] = 0.0 # We are only interested in 2D normals
  166. # Calculate the edges between points.
  167. # The call to numpy.roll shifts the entire array by one so that
  168. # we end up subtracting each next point from the current, wrapping
  169. # around. This gives us the edges from the next point to the current
  170. # point.
  171. normals[:] = normals[:] - numpy.roll(normals, -1, axis = 0)
  172. # Calculate the length of each edge using standard Pythagoras
  173. lengths = numpy.sqrt(normals[:,0] ** 2 + normals[:,2] ** 2)
  174. # The normal of a 2D vector is equal to its x and y coordinates swapped
  175. # and then x inverted. This code does that.
  176. normals[:,[0, 2]] = normals[:,[2, 0]]
  177. normals[:,0] *= -1
  178. # Normalize the normals.
  179. normals[:,0] /= lengths
  180. normals[:,2] /= lengths
  181. return normals
  182. __color_map = {
  183. NoneType: Color(1.0, 1.0, 1.0, 1.0),
  184. Inset0Type: Color(1.0, 0.0, 0.0, 1.0),
  185. InsetXType: Color(0.0, 1.0, 0.0, 1.0),
  186. SkinType: Color(1.0, 1.0, 0.0, 1.0),
  187. SupportType: Color(0.0, 1.0, 1.0, 1.0),
  188. SkirtType: Color(0.0, 1.0, 1.0, 1.0),
  189. InfillType: Color(1.0, 0.74, 0.0, 1.0),
  190. SupportInfillType: Color(0.0, 1.0, 1.0, 1.0),
  191. MoveCombingType: Color(0.0, 0.0, 1.0, 1.0),
  192. MoveRetractionType: Color(0.5, 0.5, 1.0, 1.0),
  193. }