TestBuildVolume.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. from unittest.mock import MagicMock, patch
  2. import pytest
  3. from UM.Math.Polygon import Polygon
  4. from UM.Math.Vector import Vector
  5. from cura.BuildVolume import BuildVolume, PRIME_CLEARANCE
  6. import numpy
  7. @pytest.fixture
  8. def build_volume() -> BuildVolume:
  9. mocked_application = MagicMock()
  10. mocked_platform = MagicMock(name="platform")
  11. with patch("cura.BuildVolume.Platform", mocked_platform):
  12. return BuildVolume(mocked_application)
  13. def test_buildVolumeSetSizes(build_volume):
  14. build_volume.setWidth(10)
  15. assert build_volume.getDiagonalSize() == 10
  16. build_volume.setWidth(0)
  17. build_volume.setHeight(100)
  18. assert build_volume.getDiagonalSize() == 100
  19. build_volume.setHeight(0)
  20. build_volume.setDepth(200)
  21. assert build_volume.getDiagonalSize() == 200
  22. def test_buildMesh(build_volume):
  23. mesh = build_volume._buildMesh(0, 100, 0, 100, 0, 100, 1)
  24. result_vertices = numpy.array([[0., 0., 0.], [100., 0., 0.], [0., 0., 0.], [0., 100., 0.], [0., 100., 0.], [100., 100., 0.], [100., 0., 0.], [100., 100., 0.], [0., 0., 100.], [100., 0., 100.], [0., 0., 100.], [0., 100., 100.], [0., 100., 100.], [100., 100., 100.], [100., 0., 100.], [100., 100., 100.], [0., 0., 0.], [0., 0., 100.], [100., 0., 0.], [100., 0., 100.], [0., 100., 0.], [0., 100., 100.], [100., 100., 0.], [100., 100., 100.]], dtype=numpy.float32)
  25. assert numpy.array_equal(result_vertices, mesh.getVertices())
  26. def test_buildGridMesh(build_volume):
  27. mesh = build_volume._buildGridMesh(0, 100, 0, 100, 0, 100, 1)
  28. result_vertices = numpy.array([[0., -1., 0.], [100., -1., 100.], [100., -1., 0.], [0., -1., 0.], [0., -1., 100.], [100., -1., 100.]])
  29. assert numpy.array_equal(result_vertices, mesh.getVertices())
  30. def test_clamp(build_volume):
  31. assert build_volume._clamp(0, 0, 200) == 0
  32. assert build_volume._clamp(0, -200, 200) == 0
  33. assert build_volume._clamp(300, -200, 200) == 200
  34. class TestCalculateBedAdhesionSize:
  35. setting_property_dict = {"adhesion_type": {"value": "brim"},
  36. "skirt_brim_line_width": {"value": 0},
  37. "initial_layer_line_width_factor": {"value": 0},
  38. "brim_line_count": {"value": 0},
  39. "machine_width": {"value": 200},
  40. "machine_depth": {"value": 200},
  41. "skirt_line_count": {"value": 0},
  42. "skirt_gap": {"value": 0},
  43. "raft_margin": {"value": 0}
  44. }
  45. def getPropertySideEffect(*args, **kwargs):
  46. properties = TestCalculateBedAdhesionSize.setting_property_dict.get(args[1])
  47. if properties:
  48. return properties.get(args[2])
  49. def createAndSetGlobalStack(self, build_volume):
  50. mocked_stack = MagicMock()
  51. mocked_stack.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  52. build_volume._global_container_stack = mocked_stack
  53. def test_noGlobalStack(self, build_volume: BuildVolume):
  54. assert build_volume._calculateBedAdhesionSize([]) is None
  55. @pytest.mark.parametrize("setting_dict, result", [
  56. ({}, 0),
  57. ({"adhesion_type": {"value": "skirt"}}, 0),
  58. ({"adhesion_type": {"value": "raft"}}, 0),
  59. ({"adhesion_type": {"value": "none"}}, 0),
  60. ({"adhesion_type": {"value": "skirt"}, "skirt_line_count": {"value": 2}, "initial_layer_line_width_factor": {"value": 1}, "skirt_brim_line_width": {"value": 2}}, 0.02),
  61. # Even though it's marked as skirt, it should behave as a brim as the prime tower has a brim (skirt line count is still at 0!)
  62. ({"adhesion_type": {"value": "skirt"}, "prime_tower_brim_enable": {"value": True}, "skirt_brim_line_width": {"value": 2}, "initial_layer_line_width_factor": {"value": 3}}, -0.06),
  63. ({"brim_line_count": {"value": 1}, "skirt_brim_line_width": {"value": 2}, "initial_layer_line_width_factor": {"value": 3}}, 0),
  64. ({"brim_line_count": {"value": 2}, "skirt_brim_line_width": {"value": 2}, "initial_layer_line_width_factor": {"value": 3}}, 0.06),
  65. ({"brim_line_count": {"value": 9000000}, "skirt_brim_line_width": {"value": 90000}, "initial_layer_line_width_factor": {"value": 9000}}, 100), # Clamped at half the max size of buildplate
  66. ])
  67. def test_singleExtruder(self, build_volume: BuildVolume, setting_dict, result):
  68. self.createAndSetGlobalStack(build_volume)
  69. patched_dictionary = self.setting_property_dict.copy()
  70. patched_dictionary.update(setting_dict)
  71. with patch.dict(self.setting_property_dict, patched_dictionary):
  72. assert build_volume._calculateBedAdhesionSize([]) == result
  73. def test_unknownBedAdhesion(self, build_volume: BuildVolume):
  74. self.createAndSetGlobalStack(build_volume)
  75. patched_dictionary = self.setting_property_dict.copy()
  76. patched_dictionary.update({"adhesion_type": {"value": "OMGZOMGBBQ"}})
  77. with patch.dict(self.setting_property_dict, patched_dictionary):
  78. with pytest.raises(Exception):
  79. build_volume._calculateBedAdhesionSize([])
  80. class TestComputeDisallowedAreasStatic:
  81. setting_property_dict = {"machine_disallowed_areas": {"value": [[[-200, 112.5], [ -82, 112.5], [ -84, 102.5], [-115, 102.5]]]},
  82. "machine_width": {"value": 200},
  83. "machine_depth": {"value": 200},
  84. }
  85. def getPropertySideEffect(*args, **kwargs):
  86. properties = TestComputeDisallowedAreasStatic.setting_property_dict.get(args[1])
  87. if properties:
  88. return properties.get(args[2])
  89. def test_computeDisallowedAreasStaticNoExtruder(self, build_volume: BuildVolume):
  90. mocked_stack = MagicMock()
  91. mocked_stack.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  92. build_volume._global_container_stack = mocked_stack
  93. assert build_volume._computeDisallowedAreasStatic(0, []) == {}
  94. def test_computeDisalowedAreasStaticSingleExtruder(self, build_volume: BuildVolume):
  95. mocked_stack = MagicMock()
  96. mocked_stack.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  97. mocked_extruder = MagicMock()
  98. mocked_extruder.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  99. mocked_extruder.getId = MagicMock(return_value = "zomg")
  100. build_volume._global_container_stack = mocked_stack
  101. with patch("cura.Settings.ExtruderManager.ExtruderManager.getInstance"):
  102. result = build_volume._computeDisallowedAreasStatic(0, [mocked_extruder])
  103. assert result == {"zomg": [Polygon([[-84.0, 102.5], [-115.0, 102.5], [-200.0, 112.5], [-82.0, 112.5]])]}
  104. def test_computeDisalowedAreasMutliExtruder(self, build_volume):
  105. mocked_stack = MagicMock()
  106. mocked_stack.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  107. mocked_extruder = MagicMock()
  108. mocked_extruder.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  109. mocked_extruder.getId = MagicMock(return_value="zomg")
  110. extruder_manager = MagicMock()
  111. extruder_manager.getActiveExtruderStacks = MagicMock(return_value = [mocked_stack])
  112. build_volume._global_container_stack = mocked_stack
  113. with patch("cura.Settings.ExtruderManager.ExtruderManager.getInstance", MagicMock(return_value = extruder_manager)):
  114. result = build_volume._computeDisallowedAreasStatic(0, [mocked_extruder])
  115. assert result == {"zomg": [Polygon([[-84.0, 102.5], [-115.0, 102.5], [-200.0, 112.5], [-82.0, 112.5]])]}
  116. class TestUpdateRaftThickness:
  117. setting_property_dict = {"raft_base_thickness": {"value": 1},
  118. "raft_interface_thickness": {"value": 1},
  119. "raft_surface_layers": {"value": 1},
  120. "raft_surface_thickness": {"value": 1},
  121. "raft_airgap": {"value": 1},
  122. "layer_0_z_overlap": {"value": 1},
  123. "adhesion_type": {"value": "raft"}}
  124. def getPropertySideEffect(*args, **kwargs):
  125. properties = TestUpdateRaftThickness.setting_property_dict.get(args[1])
  126. if properties:
  127. return properties.get(args[2])
  128. def createMockedStack(self):
  129. mocked_global_stack = MagicMock(name="mocked_global_stack")
  130. mocked_global_stack.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  131. extruder_stack = MagicMock()
  132. mocked_global_stack.extruders = {"0": extruder_stack}
  133. return mocked_global_stack
  134. def test_simple(self, build_volume: BuildVolume):
  135. build_volume.raftThicknessChanged = MagicMock()
  136. mocked_global_stack = self.createMockedStack()
  137. build_volume._global_container_stack = mocked_global_stack
  138. assert build_volume.getRaftThickness() == 0
  139. build_volume._updateRaftThickness()
  140. assert build_volume.getRaftThickness() == 3
  141. assert build_volume.raftThicknessChanged.emit.call_count == 1
  142. def test_adhesionIsNotRaft(self, build_volume: BuildVolume):
  143. patched_dictionary = self.setting_property_dict.copy()
  144. patched_dictionary["adhesion_type"] = {"value": "not_raft"}
  145. mocked_global_stack = self.createMockedStack()
  146. build_volume._global_container_stack = mocked_global_stack
  147. assert build_volume.getRaftThickness() == 0
  148. with patch.dict(self.setting_property_dict, patched_dictionary):
  149. build_volume._updateRaftThickness()
  150. assert build_volume.getRaftThickness() == 0
  151. def test_noGlobalStack(self, build_volume: BuildVolume):
  152. build_volume.raftThicknessChanged = MagicMock()
  153. assert build_volume.getRaftThickness() == 0
  154. build_volume._updateRaftThickness()
  155. assert build_volume.getRaftThickness() == 0
  156. assert build_volume.raftThicknessChanged.emit.call_count == 0
  157. class TestComputeDisallowedAreasPrimeBlob:
  158. setting_property_dict = {"machine_width": {"value": 50},
  159. "machine_depth": {"value": 100},
  160. "prime_blob_enable": {"value": True},
  161. "extruder_prime_pos_x": {"value": 25},
  162. "extruder_prime_pos_y": {"value": 50},
  163. "machine_center_is_zero": {"value": True},
  164. }
  165. def getPropertySideEffect(*args, **kwargs):
  166. properties = TestComputeDisallowedAreasPrimeBlob.setting_property_dict.get(args[1])
  167. if properties:
  168. return properties.get(args[2])
  169. def test_noGlobalContainer(self, build_volume: BuildVolume):
  170. # No global container and no extruders, so we expect no blob areas
  171. assert build_volume._computeDisallowedAreasPrimeBlob(12, []) == {}
  172. def test_noExtruders(self, build_volume: BuildVolume):
  173. mocked_stack = MagicMock()
  174. mocked_stack.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  175. build_volume._global_container_stack = mocked_stack
  176. # No extruders, so still expect that we get no area
  177. assert build_volume._computeDisallowedAreasPrimeBlob(12, []) == {}
  178. def test_singleExtruder(self, build_volume: BuildVolume):
  179. mocked_global_stack = MagicMock(name = "mocked_global_stack")
  180. mocked_global_stack.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  181. mocked_extruder_stack = MagicMock(name = "mocked_extruder_stack")
  182. mocked_extruder_stack.getId = MagicMock(return_value = "0")
  183. mocked_extruder_stack.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  184. build_volume._global_container_stack = mocked_global_stack
  185. # Create a polygon that should be the result
  186. resulting_polygon = Polygon.approximatedCircle(PRIME_CLEARANCE)
  187. # Since we want a blob of size 12;
  188. resulting_polygon = resulting_polygon.getMinkowskiHull(Polygon.approximatedCircle(12))
  189. # In the The translation result is 25, -50 (due to the settings used)
  190. resulting_polygon = resulting_polygon.translate(25, -50)
  191. assert build_volume._computeDisallowedAreasPrimeBlob(12, [mocked_extruder_stack]) == {"0": [resulting_polygon]}
  192. class TestCalculateExtraZClearance:
  193. setting_property_dict = {"retraction_hop": {"value": 12},
  194. "retraction_hop_enabled": {"value": True}}
  195. def getPropertySideEffect(*args, **kwargs):
  196. properties = TestCalculateExtraZClearance.setting_property_dict.get(args[1])
  197. if properties:
  198. return properties.get(args[2])
  199. def test_noContainerStack(self, build_volume: BuildVolume):
  200. assert build_volume._calculateExtraZClearance([]) is 0
  201. def test_withRetractionHop(self, build_volume: BuildVolume):
  202. mocked_global_stack = MagicMock(name="mocked_global_stack")
  203. mocked_extruder = MagicMock()
  204. mocked_extruder.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  205. build_volume._global_container_stack = mocked_global_stack
  206. # It should be 12 because we have the hop enabled and the hop distance is set to 12
  207. assert build_volume._calculateExtraZClearance([mocked_extruder]) == 12
  208. def test_withoutRetractionHop(self, build_volume: BuildVolume):
  209. mocked_global_stack = MagicMock(name="mocked_global_stack")
  210. mocked_extruder = MagicMock()
  211. mocked_extruder.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  212. build_volume._global_container_stack = mocked_global_stack
  213. patched_dictionary = self.setting_property_dict.copy()
  214. patched_dictionary["retraction_hop_enabled"] = {"value": False}
  215. with patch.dict(self.setting_property_dict, patched_dictionary):
  216. # It should be 12 because we have the hop enabled and the hop distance is set to 12
  217. assert build_volume._calculateExtraZClearance([mocked_extruder]) == 0
  218. class TestRebuild:
  219. def test_zeroWidthHeightDepth(self, build_volume: BuildVolume):
  220. build_volume.rebuild()
  221. assert build_volume.getMeshData() is None
  222. def test_engineIsNotRead(self, build_volume: BuildVolume):
  223. build_volume.setWidth(10)
  224. build_volume.setHeight(10)
  225. build_volume.setDepth(10)
  226. build_volume.rebuild()
  227. assert build_volume.getMeshData() is None
  228. def test_noGlobalStack(self, build_volume: BuildVolume):
  229. build_volume.setWidth(10)
  230. build_volume.setHeight(10)
  231. build_volume.setDepth(10)
  232. # Fake the the "engine is created callback"
  233. build_volume._onEngineCreated()
  234. build_volume.rebuild()
  235. assert build_volume.getMeshData() is None
  236. def test_updateBoundingBox(self, build_volume: BuildVolume):
  237. build_volume.setWidth(10)
  238. build_volume.setHeight(10)
  239. build_volume.setDepth(10)
  240. mocked_global_stack = MagicMock()
  241. build_volume._global_container_stack = mocked_global_stack
  242. build_volume.getEdgeDisallowedSize = MagicMock(return_value = 0)
  243. build_volume.updateNodeBoundaryCheck = MagicMock()
  244. # Fake the the "engine is created callback"
  245. build_volume._onEngineCreated()
  246. build_volume.rebuild()
  247. bounding_box = build_volume.getBoundingBox()
  248. assert bounding_box.minimum == Vector(-5.0, -1.0, -5.0)
  249. assert bounding_box.maximum == Vector(5.0, 10.0, 5.0)
  250. class TestUpdateMachineSizeProperties:
  251. setting_property_dict = {"machine_width": {"value": 50},
  252. "machine_depth": {"value": 100},
  253. "machine_height": {"value": 200},
  254. "machine_shape": {"value": "DERP!"}}
  255. def getPropertySideEffect(*args, **kwargs):
  256. properties = TestUpdateMachineSizeProperties.setting_property_dict.get(args[1])
  257. if properties:
  258. return properties.get(args[2])
  259. def test_noGlobalStack(self, build_volume: BuildVolume):
  260. build_volume._updateMachineSizeProperties()
  261. assert build_volume._width == 0
  262. assert build_volume._height == 0
  263. assert build_volume._depth == 0
  264. assert build_volume._shape == ""
  265. def test_happy(self, build_volume: BuildVolume):
  266. mocked_global_stack = MagicMock(name="mocked_global_stack")
  267. mocked_global_stack.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  268. build_volume._global_container_stack = mocked_global_stack
  269. build_volume._updateMachineSizeProperties()
  270. assert build_volume._width == 50
  271. assert build_volume._height == 200
  272. assert build_volume._depth == 100
  273. assert build_volume._shape == "DERP!"
  274. class TestGetEdgeDisallowedSize:
  275. setting_property_dict = {}
  276. bed_adhesion_size = 1
  277. @pytest.fixture()
  278. def build_volume(self, build_volume):
  279. build_volume._calculateBedAdhesionSize = MagicMock(return_value = 1)
  280. return build_volume
  281. def getPropertySideEffect(*args, **kwargs):
  282. properties = TestGetEdgeDisallowedSize.setting_property_dict.get(args[1])
  283. if properties:
  284. return properties.get(args[2])
  285. def createMockedStack(self):
  286. mocked_global_stack = MagicMock(name="mocked_global_stack")
  287. mocked_global_stack.getProperty = MagicMock(side_effect=self.getPropertySideEffect)
  288. return mocked_global_stack
  289. def test_noGlobalContainer(self, build_volume: BuildVolume):
  290. assert build_volume.getEdgeDisallowedSize() == 0
  291. def test_unknownAdhesion(self, build_volume: BuildVolume):
  292. build_volume._global_container_stack = self.createMockedStack()
  293. with patch("cura.Settings.ExtruderManager.ExtruderManager.getInstance"):
  294. #with pytest.raises(Exception):
  295. # Since we don't have any adhesion set, this should break.
  296. build_volume.getEdgeDisallowedSize()
  297. def test_oneAtATime(self, build_volume: BuildVolume):
  298. build_volume._global_container_stack = self.createMockedStack()
  299. with patch("cura.Settings.ExtruderManager.ExtruderManager.getInstance"):
  300. with patch.dict(self.setting_property_dict, {"print_sequence": {"value": "one_at_a_time"}}):
  301. assert build_volume.getEdgeDisallowedSize() == 0.1