LayerData.py 7.8 KB

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