import numpy as np import unittest from unittest.mock import MagicMock import sys from pathlib import Path # Add workspace directory to sys.path to resolve root package name (NodeSlicer) PROJECT_ROOT = Path(__file__).resolve().parent.parent sys.path.append(str(PROJECT_ROOT.parent)) # Define dummy classes to avoid metaclass conflicts class DummyNodeSocket: pass class DummyNode: pass class DummyObject: pass class DummyMesh: pass class DummyNodeSocketShader: pass class DummyNodeCustomGroup: pass class DummyNodeTree: pass class DummyOperator: pass class DummyMenu: pass class DummyPanel: pass class DummyPropertyGroup: pass mock_types = MagicMock() mock_types.NodeSocket = DummyNodeSocket mock_types.Node = DummyNode mock_types.Object = DummyObject mock_types.Mesh = DummyMesh mock_types.NodeSocketShader = DummyNodeSocketShader mock_types.NodeCustomGroup = DummyNodeCustomGroup mock_types.NodeTree = DummyNodeTree mock_types.Operator = DummyOperator mock_types.Menu = DummyMenu mock_types.Panel = DummyPanel mock_types.PropertyGroup = DummyPropertyGroup # Mock bpy and its subpackages before importing core modules mock_bpy = MagicMock() mock_bpy.context.scene = MagicMock() mock_bpy.context.scene.unit_settings.scale_length = 0.001 mock_bpy.types = mock_types sys.modules['bpy'] = mock_bpy sys.modules['bpy.types'] = mock_types sys.modules['bpy.props'] = MagicMock() sys.modules['bpy.utils'] = MagicMock() sys.modules['bpy.utils.previews'] = MagicMock() sys.modules['bpy.app'] = MagicMock() sys.modules['bpy.app.handlers'] = MagicMock() # Import the actual classes to test from NodeSlicer.core.nodeslicer.nodes.mesh import SLICENODE_ArrangeMesh # pyright: ignore[reportMissingImports] from NodeSlicer.infra.blender.blender_mesh_capture import SlicingGroup, SlicingCollection, SlicingObject # pyright: ignore[reportMissingImports] class TestArrangeMesh(unittest.TestCase): def test_arrange_mesh_simple(self): # Create a SlicingObject with a single triangle so = SlicingObject() so.name = "part1" so.mesh = np.array([[[10.0, 10.0, 0.0], [20.0, 10.0, 0.0], [15.0, 20.0, 0.0], [0.0, 0.0, 1.0]]], dtype=np.float64) # Create SlicingCollection sc = SlicingCollection([], "coll1") sc.objects = [so] # Create SlicingGroup sg = SlicingGroup([]) sg.collections = {"coll1": sc} # Mock node inputs resolution node = SLICENODE_ArrangeMesh() node.method = 'NFP' # We override resolve to simulate inputs def mock_resolve(fieldname): if fieldname == "Slicing Group": return sg elif fieldname == "Bed X": return 250.0 elif fieldname == "Bed Y": return 250.0 elif fieldname == "Spacing": return 2.0 return None node.resolve = mock_resolve # Evaluate output result_group = node.evaluate_output("Slicing Group") self.assertIsNotNone(result_group) self.assertIn("coll1", result_group.collections) arranged_sc = result_group.collections["coll1"] # The bounding box should now be centered on the 250x250 bed self.assertEqual(arranged_sc.min_x, 120.0) self.assertEqual(arranged_sc.min_y, 120.0) self.assertEqual(arranged_sc.max_x, 130.0) self.assertEqual(arranged_sc.max_y, 130.0) # Check normal remains unchanged self.assertTrue(np.allclose(arranged_sc.objects[0].mesh[0, 3, :], [0.0, 0.0, 1.0])) def test_arrange_mesh_bl(self): so = SlicingObject() so.name = "part1" so.mesh = np.array([[[10.0, 10.0, 0.0], [20.0, 10.0, 0.0], [15.0, 20.0, 0.0], [0.0, 0.0, 1.0]]], dtype=np.float64) sc = SlicingCollection([], "coll1") sc.objects = [so] sg = SlicingGroup([]) sg.collections = {"coll1": sc} node = SLICENODE_ArrangeMesh() node.method = 'BL' def mock_resolve(fieldname): if fieldname == "Slicing Group": return sg elif fieldname == "Bed X": return 250.0 elif fieldname == "Bed Y": return 250.0 elif fieldname == "Spacing": return 2.0 return None node.resolve = mock_resolve result_group = node.evaluate_output("Slicing Group") self.assertIsNotNone(result_group) arranged_sc = result_group.collections["coll1"] self.assertGreaterEqual(arranged_sc.min_x, 0.0) self.assertGreaterEqual(arranged_sc.min_y, 0.0) def test_arrange_mesh_djd(self): so = SlicingObject() so.name = "part1" so.mesh = np.array([[[10.0, 10.0, 0.0], [20.0, 10.0, 0.0], [15.0, 20.0, 0.0], [0.0, 0.0, 1.0]]], dtype=np.float64) sc = SlicingCollection([], "coll1") sc.objects = [so] sg = SlicingGroup([]) sg.collections = {"coll1": sc} node = SLICENODE_ArrangeMesh() node.method = 'DJD' def mock_resolve(fieldname): if fieldname == "Slicing Group": return sg elif fieldname == "Bed X": return 250.0 elif fieldname == "Bed Y": return 250.0 elif fieldname == "Spacing": return 2.0 return None node.resolve = mock_resolve result_group = node.evaluate_output("Slicing Group") self.assertIsNotNone(result_group) arranged_sc = result_group.collections["coll1"] self.assertEqual(arranged_sc.min_x, 120.0) self.assertEqual(arranged_sc.min_y, 120.0) if __name__ == '__main__': unittest.main()