def test_multipleItemsRename(self, objects_model): node1 = SceneNode() node2 = SceneNode() result = objects_model._renameNodes({"zomg": _NodeInfo(nodes_to_rename=[node1, node2])}) assert result == [node1, node2] assert node1.getName() == "zomg(1)" assert node2.getName() == "zomg(2)"
def read(self, file_name): mesh_builder = MeshBuilder() scene_node = SceneNode() self.load_file(file_name, mesh_builder, _use_numpystl = use_numpystl) mesh = mesh_builder.build() if use_numpystl: verts = mesh.getVertices() # In some cases numpy stl reads incorrectly and the result is that the Z values are all 0 # Add new error cases if you find them. if numpy.amin(verts[:, 1]) == numpy.amax(verts[:, 1]): # Something may have gone wrong in numpy stl, start over without numpy stl Logger.log("w", "All Z coordinates are the same using numpystl, trying again without numpy stl.") mesh_builder = MeshBuilder() self.load_file(file_name, mesh_builder, _use_numpystl = False) mesh = mesh_builder.build() verts = mesh.getVertices() if numpy.amin(verts[:, 1]) == numpy.amax(verts[:, 1]): Logger.log("e", "All Z coordinates are still the same without numpy stl... let's hope for the best") if mesh_builder.getVertexCount() == 0: Logger.log("d", "File did not contain valid data, unable to read.") return None # We didn't load anything. scene_node.setMeshData(mesh) Logger.log("d", "Loaded a mesh with %s vertices", mesh_builder.getVertexCount()) return scene_node
def _onSceneChangedDelayed(self, scene_node: SceneNode) -> None: # Ignore any changes that are not related to sliceable objects if not isinstance(scene_node, SceneNode) \ or not scene_node.callDecoration("isSliceable") \ or not scene_node.callDecoration("getBuildPlateNumber") == self._active_build_plate: return self._change_timer.start()
def read(self, file_name): mesh_builder = MeshBuilder() scene_node = SceneNode() if use_numpystl: self._loadWithNumpySTL(file_name, mesh_builder) else: f = open(file_name, "rb") if not self._loadBinary(mesh_builder, f): f.close() f = open(file_name, "rt") try: self._loadAscii(mesh_builder, f) except UnicodeDecodeError: return None f.close() Job.yieldThread() # Yield somewhat to ensure the GUI has time to update a bit. mesh_builder.calculateNormals(fast = True) mesh_builder.setFileName(file_name) mesh = mesh_builder.build() Logger.log("d", "Loaded a mesh with %s vertices", mesh_builder.getVertexCount()) scene_node.setMeshData(mesh) return scene_node
def initialize(self, parent: SceneNode, step=None, callback=None): parent.addChild(self) mesh_data = parent.getMeshData() if mesh_data: Logger.log( 'd', 'Compute interactive mesh from SceneNode {}'.format( parent.getName())) if mesh_data.getVertexCount() < 1000: self._interactive_mesh = makeInteractiveMesh(mesh_data) if step: self.loadStep(step) self.setOrigin() if callback: callback() else: job = AnalyzeMeshJob(mesh_data, step, callback) job.finished.connect(self._process_mesh_analysis) self._mesh_analyzing_message = Message( title=i18n_catalog.i18n("Smart Slice"), text=i18n_catalog.i18n( "Analyzing geometry - this may take a few moments"), progress=-1, dismissable=False, lifetime=0, use_inactivity_timer=False) self._mesh_analyzing_message.show() job.start() self.rootChanged.emit(self)
def read(self, file_name): mesh_builder = MeshBuilder() scene_node = SceneNode() if use_numpystl: self._loadWithNumpySTL(file_name, mesh_builder) else: f = open(file_name, "rb") if not self._loadBinary(mesh_builder, f): f.close() f = open(file_name, "rt") try: self._loadAscii(mesh_builder, f) except UnicodeDecodeError: return None f.close() Job.yieldThread() # Yield somewhat to ensure the GUI has time to update a bit. mesh_builder.calculateNormals(fast = True) mesh = mesh_builder.build() Logger.log("d", "Loaded a mesh with %s vertices", mesh_builder.getVertexCount()) scene_node.setMeshData(mesh) return scene_node
def _reloadJobFinished(self, replaced_node: SceneNode, job: ReadMeshJob) -> None: for node in job.getResult(): mesh_data = node.getMeshData() if mesh_data: replaced_node.setMeshData(mesh_data) else: Logger.log("w", "Could not find a mesh in reloaded node.")
def _read(self, file_name): """Decide if we need to use ascii or binary in order to read file""" mesh_builder = MeshBuilder() scene_node = SceneNode() self.load_file(file_name, mesh_builder, _use_numpystl = use_numpystl) mesh = mesh_builder.build() if use_numpystl: verts = mesh.getVertices() # In some cases numpy stl reads incorrectly and the result is that the Z values are all 0 # Add new error cases if you find them. if numpy.amin(verts[:, 1]) == numpy.amax(verts[:, 1]): # Something may have gone wrong in numpy stl, start over without numpy stl Logger.log("w", "All Z coordinates are the same using numpystl, trying again without numpy stl.") mesh_builder = MeshBuilder() self.load_file(file_name, mesh_builder, _use_numpystl = False) mesh = mesh_builder.build() verts = mesh.getVertices() if numpy.amin(verts[:, 1]) == numpy.amax(verts[:, 1]): Logger.log("e", "All Z coordinates are still the same without numpy stl... let's hope for the best") if mesh_builder.getVertexCount() == 0: Logger.log("d", "File did not contain valid data, unable to read.") return None # We didn't load anything. scene_node.setMeshData(mesh) Logger.log("d", "Loaded a mesh with %s vertices", mesh_builder.getVertexCount()) return scene_node
def test_singleItemRenameWithIndex(self, objects_model): node = SceneNode() objects_model._renameNodes({ "zomg": _NodeInfo(index_to_node={1: node}, nodes_to_rename=[node]) }) assert node.getName() == "zomg(2)"
def test_getConvexHullPrintingMesh(convex_hull_decorator): node = SceneNode() node.addDecorator(PrintingDecorator()) with patch("UM.Application.Application.getInstance", MagicMock(return_value=mocked_application)): convex_hull_decorator.setNode(node) convex_hull_decorator._compute2DConvexHull = MagicMock(return_value = Polygon.approximatedCircle(10)) assert convex_hull_decorator.getConvexHull() == Polygon.approximatedCircle(10)
def test_getConvexHulLBoundaryPrintingMesh(convex_hull_decorator): node = SceneNode() node.addDecorator(PrintingDecorator()) with patch("UM.Application.Application.getInstance", MagicMock(return_value=mocked_application)): convex_hull_decorator.setNode(node) # Should still be None, since print sequence is not one at a time assert convex_hull_decorator.getConvexHullBoundary() is None
def test_getConvexHullBoundaryNotPrintingMesh(convex_hull_decorator): node = SceneNode() node.addDecorator(NonPrintingDecorator()) with patch("UM.Application.Application.getInstance", MagicMock(return_value=mocked_application)): convex_hull_decorator.setNode(node) assert convex_hull_decorator.getConvexHullBoundary() is None
def read(self, file_name): self._dxf = DXFObjectReader.DXFObjectReader(open(file_name, "rt")) self._mesh = MeshData() for obj in self._dxf: if obj.getName() == "SECTION": if obj.get(2) == "ENTITIES": self._handleEntities() elif obj.get(2) == "TABLES": self._handleTables() else: Logger.log("d", "DXF: Got unknown section: %s", obj.get(2)) for obj in self._dxf: if obj.getName() == "ENDSEC": break else: Logger.log("d", "DXF: %s", obj) elif obj.getName() == "EOF": pass else: Logger.log("e", "DXF: Unexpected object: %s", obj) #self._mesh.calculateNormals() node = SceneNode() node.setMeshData(self._mesh) return node
def _checkHit(self, a: SceneNode, b: SceneNode) -> bool: """Checks if a can be printed before b :param a: node :param b: node :return: true if a can be printed before b """ if a == b: return False a_hit_hull = a.callDecoration("getConvexHullBoundary") b_hit_hull = b.callDecoration("getConvexHullHeadFull") overlap = a_hit_hull.intersectsPolygon(b_hit_hull) if overlap: return True # Adhesion areas must never overlap, regardless of printing order # This would cause over-extrusion a_hit_hull = a.callDecoration("getAdhesionArea") b_hit_hull = b.callDecoration("getAdhesionArea") overlap = a_hit_hull.intersectsPolygon(b_hit_hull) if overlap: return True else: return False
def test_relativeScale(): node = SceneNode() op = ScaleOperation(node, Vector(2, 2, 2), set_scale=True) op.redo() op2 = ScaleOperation(node, Vector(1, 2, 3), relative_scale=True) op2.redo() assert node.getScale() == Vector(3, 4, 5)
def _updateNodeListeners(self, node: SceneNode): per_mesh_stack = node.callDecoration("getStack") if per_mesh_stack: per_mesh_stack.propertyChanged.connect(self._onSettingPropertyChanged) active_extruder_changed = node.callDecoration("getActiveExtruderChangedSignal") if active_extruder_changed is not None: active_extruder_changed.connect(self._updateDisallowedAreasAndRebuild) self._updateDisallowedAreasAndRebuild()
def test_addScale(): node = SceneNode() op = ScaleOperation(node, Vector(1, 2, 3), set_scale = True) op.redo() op2 = ScaleOperation(node, Vector(1, 2, 3), add_scale = True) op2.redo() assert node.getScale() == Vector(2, 4, 6)
def test_addScale(): node = SceneNode() op = ScaleOperation(node, Vector(1, 2, 3), set_scale=True) op.redo() op2 = ScaleOperation(node, Vector(1, 2, 3), add_scale=True) op2.redo() assert node.getScale() == Vector(2, 4, 6)
def _onSceneChanged(self, scene_node: SceneNode) -> None: # Ignore any changes that are not related to sliceable objects if not isinstance(scene_node, SceneNode)\ or not scene_node.callDecoration("isSliceable")\ or not scene_node.callDecoration("getBuildPlateNumber") == self._active_build_plate: return self.setToZeroPrintInformation(self._active_build_plate)
def test_getSelectedObject(self): node_1 = SceneNode() node_2 = SceneNode() Selection.add(node_1) Selection.add(node_2) assert Selection.getSelectedObject(0) == node_1 assert Selection.getSelectedObject(1) == node_2 assert Selection.getSelectedObject(3) is None
def _subdivide(self, mesh, plane): start_time = time.time() plane_mesh_data = plane.getMeshData() plane_vertices = plane_mesh_data.getVertices() plane_face = [plane_vertices[0], plane_vertices[1], plane_vertices[2]] builders = [MeshBuilder(), MeshBuilder()] mesh_data = mesh.getMeshData() vertices = mesh_data.getVertices() indices = mesh_data.getIndices() faces = [] if indices: for index_array in indices: faces.append([ vertices[index_array[0]], vertices[index_array[1]], vertices[index_array[2]] ]) else: for i in range(0, len(vertices), 3): faces.append([vertices[i], vertices[i + 1], vertices[i + 2]]) for f in faces: intersection_type = self.check_intersection_with_triangle( plane_face, f) if intersection_type is None or ( intersection_type is not None and intersection_type[0] in [IntersectionType.Point, IntersectionType.Edge]): side = self.check_plane_side(plane_face, f) if side is not None: self.add_face_to_builder(builders[side], f) else: Logger.log("w", "Invalid face detected: " + str(f)) elif intersection_type[0] == IntersectionType.Face: self.add_face_to_builder(builders[0], f) self.add_face_to_builder(builders[1], f) elif intersection_type[0] == IntersectionType.Segment: new_faces = self.split_triangle(f, intersection_type[1]) for new_face in new_faces: self.add_face_to_builder( builders[self.check_plane_side(plane_face, new_face)], new_face) elif intersection_type[0] == IntersectionType.PointAndSegment: new_faces = self.split_triangle_in_one_segment( f, intersection_type[1]) for new_face in new_faces: self.add_face_to_builder( builders[self.check_plane_side(plane_face, new_face)], new_face) nodes = [SceneNode(), SceneNode()] for n in range(len(nodes)): builders[n].calculateNormals() nodes[n].setMeshData(builders[n].build()) nodes[n].setSelectable(True) nodes[n].setScale(mesh.getScale()) Logger.log( "w", i18n_catalog.i18n("Subdivision took %f seconds") % (time.time() - start_time)) return nodes[0], nodes[1]
def __init__(self): super().__init__() # Call super to make multiple inheritance work. self._root = SceneNode(name= "Root") self._root.setCalculateBoundingBox(False) self._connectSignalsRoot() self._active_camera = None self._ignore_scene_changes = False self._lock = threading.Lock()
def test_setSimpleScale(): node = SceneNode() op = ScaleOperation(node, Vector(1, 2, 3), set_scale=True) op.redo() assert node.getScale() == Vector(1, 2, 3) op.undo() assert node.getScale() == Vector(1, 1, 1)
def test_setSimpleScale(): node = SceneNode() op = ScaleOperation(node, Vector(1, 2, 3), set_scale = True) op.redo() assert node.getScale() == Vector(1, 2, 3) op.undo() assert node.getScale() == Vector(1, 1, 1)
def _onSceneChanged(self, source: SceneNode) -> None: if not isinstance(source, SceneNode): return # This case checks if the source node is a node that contains GCode. In this case the # current layer data is removed so the previous data is not rendered - CURA-4821 if source.callDecoration("isBlockSlicing") and source.callDecoration( "getLayerData"): self._stored_optimized_layer_data = {} build_plate_changed = set() source_build_plate_number = source.callDecoration( "getBuildPlateNumber") if source == self._scene.getRoot(): # we got the root node num_objects = self._numObjectsPerBuildPlate() for build_plate_number in list( self._last_num_objects.keys()) + list(num_objects.keys()): if build_plate_number not in self._last_num_objects or num_objects[ build_plate_number] != self._last_num_objects[ build_plate_number]: self._last_num_objects[build_plate_number] = num_objects[ build_plate_number] build_plate_changed.add(build_plate_number) else: # we got a single scenenode if not source.callDecoration("isGroup"): mesh_data = source.getMeshData() if mesh_data is None or mesh_data.getVertices() is None: return # There are some SceneNodes that do not have any build plate associated, then do not add to the list. if source_build_plate_number is not None: build_plate_changed.add(source_build_plate_number) if not build_plate_changed: return if self._tool_active: # do it later, each source only has to be done once if source not in self._postponed_scene_change_sources: self._postponed_scene_change_sources.append(source) return self.stopSlicing() for build_plate_number in build_plate_changed: if build_plate_number not in self._build_plates_to_be_sliced: self._build_plates_to_be_sliced.append(build_plate_number) self.printDurationMessage.emit(source_build_plate_number, {}, []) self.processingProgress.emit(0.0) self.setState(BackendState.NotStarted) # if not self._use_timer: # With manually having to slice, we want to clear the old invalid layer data. self._clearLayerData(build_plate_changed) self._invokeSlice()
def test_getConvexHullPrintingMesh(convex_hull_decorator): node = SceneNode() node.addDecorator(PrintingDecorator()) with patch("UM.Application.Application.getInstance", MagicMock(return_value=mocked_application)): convex_hull_decorator.setNode(node) convex_hull_decorator._compute2DConvexHull = MagicMock( return_value=Polygon.approximatedCircle(10)) assert convex_hull_decorator.getConvexHull() == Polygon.approximatedCircle( 10)
def test_selectionCount(self): assert self.proxy.selectionCount == 0 node_1 = SceneNode() Selection.add(node_1) assert self.proxy.selectionCount == 1 node_2 = SceneNode() Selection.add(node_2) assert self.proxy.selectionCount == 2
def test_getSelectionCenter(self): node_1 = SceneNode() node_1.getBoundingBox = MagicMock(return_value = AxisAlignedBox(Vector(0, 0, 0), Vector(10, 20, 30))) Selection.add(node_1) assert Selection.getSelectionCenter() == Vector(5, 10, 15) node_2 = SceneNode() node_2.getBoundingBox = MagicMock(return_value=AxisAlignedBox(Vector(0, 0, 0), Vector(20, 30, 40))) Selection.add(node_2) assert Selection.getSelectionCenter() == Vector(10, 15, 20)
def test_clearSelection(self): node_1 = SceneNode() node_2 = SceneNode() Selection.add(node_1) Selection.add(node_2) # Ensure that the objects we want selected are selected assert Selection.getAllSelectedObjects() == [node_1, node_2] Selection.clear() assert Selection.getAllSelectedObjects() == []
def findNodePlacement(self, node: SceneNode, offset_shape_arr: ShapeArray, hull_shape_arr: ShapeArray, step=1): best_spot = self.bestSpot(hull_shape_arr, start_prio=self._last_priority, step=step) x, y = best_spot.x, best_spot.y # Save the last priority. self._last_priority = best_spot.priority # Ensure that the object is above the build platform node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator) if node.getBoundingBox(): center_y = node.getWorldPosition().y - node.getBoundingBox().bottom else: center_y = 0 if x is not None: # We could find a place node.setPosition(Vector(x, center_y, y)) found_spot = True self.place(x, y, offset_shape_arr) # place the object in arranger else: Logger.log("d", "Could not find spot!"), found_spot = False node.setPosition(Vector(200, center_y, 100)) return found_spot
def test_getConvexHulLBoundaryPrintingMeshOneAtATime(convex_hull_decorator): node = SceneNode() node.addDecorator(PrintingDecorator()) with patch("UM.Application.Application.getInstance", MagicMock(return_value=mocked_application)): convex_hull_decorator.setNode(node) convex_hull_decorator._global_stack = MagicMock() convex_hull_decorator._global_stack.getProperty = MagicMock(return_value = "one_at_a_time") # In this test we don't care for the result of the function, just that the convex hull computation is called. convex_hull_decorator._compute2DConvexHull = MagicMock() convex_hull_decorator.getConvexHullBoundary() convex_hull_decorator._compute2DConvexHull.assert_called_once_with()
def test_SimpleRedoUndo(): node = SceneNode() parent_node = SceneNode() operation = AddSceneNodeOperation(node, parent_node) operation.redo() assert node.getParent() == parent_node operation.undo() assert node.getParent() is None
def _reloadJobFinished(self, replaced_node: SceneNode, job: ReadMeshJob) -> None: """Triggered when reloading has finished. This then puts the resulting mesh data in the node. """ for node in job.getResult(): mesh_data = node.getMeshData() if mesh_data: replaced_node.setMeshData(mesh_data) else: Logger.log("w", "Could not find a mesh in reloaded node.")
def test_UndoRedoWithSelection(): node = SceneNode() parent_node = SceneNode() Selection.add(node) operation = AddSceneNodeOperation(node, parent_node) operation.undo() assert not Selection.isSelected(node) operation.redo() assert Selection.isSelected(node)
def test_compute2DConvexHullMeshData(convex_hull_decorator): node = SceneNode() mb = MeshBuilder() mb.addCube(10,10,10) node.setMeshData(mb.build()) convex_hull_decorator._getSettingProperty = MagicMock(return_value = 0) with patch("UM.Application.Application.getInstance", MagicMock(return_value=mocked_application)): convex_hull_decorator.setNode(node) assert convex_hull_decorator._compute2DConvexHull() == Polygon([[5.0,-5.0], [-5.0,-5.0], [-5.0,5.0], [5.0,5.0]])
def setUp(self): # Called before the first testfunction is executed self._scene = Scene() self._scene_object = SceneNode() self._scene_object2 = SceneNode() self._scene_object.addChild(self._scene_object2) self._scene.getRoot().addChild(self._scene_object) temp_matrix = Matrix() temp_matrix.setByTranslation(Vector(10, 10, 10)) self._scene_object2.setLocalTransformation(deepcopy(temp_matrix)) temp_matrix.setByScaleFactor(0.5) self._scene_object.setLocalTransformation(temp_matrix)
def setUp(self): # Called before the first testfunction is executed self._scene = Scene() self._scene_object = SceneNode() self._scene_object2 = SceneNode() self._scene_object.addChild(self._scene_object2) self._scene.getRoot().addChild(self._scene_object) temp_matrix = Matrix() temp_matrix.setByTranslation(Vector(10,10,10)) self._scene_object2.setLocalTransformation(deepcopy(temp_matrix)) temp_matrix.setByScaleFactor(0.5) self._scene_object.setLocalTransformation(temp_matrix)
def test_ignoreSceneChanges(): scene = Scene() scene.sceneChanged.emit = MagicMock() scene.setIgnoreSceneChanges(ignore_scene_changes=True) root = scene.getRoot() root.addChild(SceneNode()) assert scene.sceneChanged.emit.call_count == 0 scene.setIgnoreSceneChanges(ignore_scene_changes=False) root.addChild(SceneNode()) assert scene.sceneChanged.emit.call_count == 2
def test_removeChildren(self): node1 = SceneNode() node2 = SceneNode() node1.addChild(node2) assert node1.hasChildren() node1.removeAllChildren() assert not node1.hasChildren()
def test_scaleWorld(self): node1 = SceneNode() node2 = SceneNode(node1) node2.scale(Vector(1.5,1.,1.)) node2.translate(Vector(10,10,10)) self.assertEqual(node2.getWorldPosition(), Vector(15,10,10)) node2.scale(Vector(1.5,1,1)) self.assertEqual(node2.getWorldPosition(), Vector(15,10,10))
def __init__(self, node: SceneNode, mesh_data: MeshData, name: str = "") -> None: super().__init__() self._node = node self._old_mesh_data = node.getMeshData() self._old_name = node.getName() self._new_mesh_data = mesh_data self._new_name = name if mesh_data.getVertices != None: self.redo()
def test_getConvexHulLBoundaryPrintingMeshOneAtATime(convex_hull_decorator): node = SceneNode() node.addDecorator(PrintingDecorator()) with patch("UM.Application.Application.getInstance", MagicMock(return_value=mocked_application)): convex_hull_decorator.setNode(node) convex_hull_decorator._global_stack = MagicMock() convex_hull_decorator._global_stack.getProperty = MagicMock( return_value="one_at_a_time") # In this test we don't care for the result of the function, just that the convex hull computation is called. convex_hull_decorator._compute2DConvexHull = MagicMock() convex_hull_decorator.getConvexHullBoundary() convex_hull_decorator._compute2DConvexHull.assert_called_once_with()
def __init__(self) -> None: super().__init__() # Call super to make multiple inheritance work. from UM.Scene.SceneNode import SceneNode self._root = SceneNode(name="Root") self._root.setCalculateBoundingBox(False) self._connectSignalsRoot() self._active_camera = None # type: Optional[Camera] self._ignore_scene_changes = False self._lock = threading.Lock() # Watching file for changes. self._file_watcher = QFileSystemWatcher() self._file_watcher.fileChanged.connect(self._onFileChanged)
def run(self): objectIdMap = {} new_node = SceneNode() ## Put all nodes in a dict identified by ID for node in DepthFirstIterator(self._scene.getRoot()): if type(node) is SceneNode and node.getMeshData(): if hasattr(node.getMeshData(), "layerData"): self._scene.getRoot().removeChild(node) else: objectIdMap[id(node)] = node settings = Application.getInstance().getActiveMachine() layerHeight = settings.getSettingValueByKey("layer_height") for object in self._message.objects: try: node = objectIdMap[object.id] except KeyError: continue mesh = MeshData() layerData = LayerData.LayerData() for layer in object.layers: for polygon in layer.polygons: points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly. points = numpy.asarray(points, dtype=numpy.float32) points /= 1000 points = numpy.insert(points, 1, layer.id * layerHeight, axis = 1) points[:,2] *= -1 if not settings.getSettingValueByKey("machine_center_is_zero"): center = [settings.getSettingValueByKey("machine_width") / 2, 0.0, -settings.getSettingValueByKey("machine_depth") / 2] points -= numpy.array(center) #points = numpy.pad(points, ((0,0), (0,1)), "constant", constant_values=(0.0, 1.0)) #inverse = node.getWorldTransformation().getInverse().getData() #points = points.dot(inverse) #points = points[:,0:3] layerData.addPolygon(layer.id, polygon.type, points) # We are done processing all the layers we got from the engine, now create a mesh out of the data layerData.build() mesh.layerData = layerData new_node.setMeshData(mesh) new_node.setParent(self._scene.getRoot())
def _onSceneChanged(self, source: SceneNode) -> None: if not isinstance(source, SceneNode): return # This case checks if the source node is a node that contains GCode. In this case the # current layer data is removed so the previous data is not rendered - CURA-4821 if source.callDecoration("isBlockSlicing") and source.callDecoration("getLayerData"): self._stored_optimized_layer_data = {} build_plate_changed = set() source_build_plate_number = source.callDecoration("getBuildPlateNumber") if source == self._scene.getRoot(): # we got the root node num_objects = self._numObjectsPerBuildPlate() for build_plate_number in list(self._last_num_objects.keys()) + list(num_objects.keys()): if build_plate_number not in self._last_num_objects or num_objects[build_plate_number] != self._last_num_objects[build_plate_number]: self._last_num_objects[build_plate_number] = num_objects[build_plate_number] build_plate_changed.add(build_plate_number) else: # we got a single scenenode if not source.callDecoration("isGroup"): mesh_data = source.getMeshData() if mesh_data is None or mesh_data.getVertices() is None: return # There are some SceneNodes that do not have any build plate associated, then do not add to the list. if source_build_plate_number is not None: build_plate_changed.add(source_build_plate_number) if not build_plate_changed: return if self._tool_active: # do it later, each source only has to be done once if source not in self._postponed_scene_change_sources: self._postponed_scene_change_sources.append(source) return self.stopSlicing() for build_plate_number in build_plate_changed: if build_plate_number not in self._build_plates_to_be_sliced: self._build_plates_to_be_sliced.append(build_plate_number) self.printDurationMessage.emit(source_build_plate_number, {}, []) self.processingProgress.emit(0.0) self.setState(BackendState.NotStarted) # if not self._use_timer: # With manually having to slice, we want to clear the old invalid layer data. self._clearLayerData(build_plate_changed) self._invokeSlice()
def _shouldNodeBeHandled(self, node: SceneNode) -> bool: is_group = bool(node.callDecoration("isGroup")) if not node.callDecoration("isSliceable") and not is_group: return False parent = node.getParent() if parent and parent.callDecoration("isGroup"): return False # Grouped nodes don't need resetting as their parent (the group) is resetted) node_build_plate_number = node.callDecoration("getBuildPlateNumber") if Application.getInstance().getPreferences().getValue("view/filter_current_build_plate") and node_build_plate_number != self._build_plate_number: return False return True
def findNodePlacement(self, node: SceneNode, offset_shape_arr: ShapeArray, hull_shape_arr: ShapeArray, step = 1): best_spot = self.bestSpot( hull_shape_arr, start_prio = self._last_priority, step = step) x, y = best_spot.x, best_spot.y # Save the last priority. self._last_priority = best_spot.priority # Ensure that the object is above the build platform node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator) bbox = node.getBoundingBox() if bbox: center_y = node.getWorldPosition().y - bbox.bottom else: center_y = 0 if x is not None: # We could find a place node.setPosition(Vector(x, center_y, y)) found_spot = True self.place(x, y, offset_shape_arr) # place the object in arranger else: Logger.log("d", "Could not find spot!"), found_spot = False node.setPosition(Vector(200, center_y, 100)) return found_spot
def test_getAllChildren(self): parent_node = SceneNode() child_node_1 = SceneNode() child_node_2 = SceneNode() parent_node.addChild(child_node_1) parent_node.addChild(child_node_2) child_1_of_child_node_1 = SceneNode() child_2_of_child_node_1 = SceneNode() child_node_1.addChild(child_1_of_child_node_1) child_node_1.addChild(child_2_of_child_node_1) child_1_of_child_node_2 = SceneNode() child_node_2.addChild(child_1_of_child_node_2) assert parent_node.getAllChildren() == [child_node_1, child_node_2, child_1_of_child_node_1, child_2_of_child_node_1, child_1_of_child_node_2]
def multiplyObject(self, object_id, count): node = self.getController().getScene().findObject(object_id) if node: op = GroupedOperation() for i in range(count): new_node = SceneNode() new_node.setMeshData(node.getMeshData()) new_node.setScale(node.getScale()) new_node.translate(Vector((i + 1) * node.getBoundingBox().width, 0, 0)) new_node.setSelectable(True) op.addOperation(AddSceneNodeOperation(new_node, node.getParent())) op.push()
def test_addRemoveDouble(self): # Adding a child that's already a child of a node should not cause issues. Same for trying to remove one that isn't a child node_1 = SceneNode() node_2 = SceneNode() # Should work node_1.addChild(node_2) # Should still work! node_1.addChild(node_2) # This has already been tested somewhere else, so no problems are expected node_1.removeChild(node_2) # Doing it again shouldn't break. node_1.removeChild(node_2)
def groupSelected(self): group_node = SceneNode() group_decorator = GroupDecorator() group_node.addDecorator(group_decorator) group_node.setParent(self.getController().getScene().getRoot()) center = Selection.getSelectionCenter() group_node.setPosition(center) group_node.setCenterPosition(center) for node in Selection.getAllSelectedObjects(): world = node.getWorldPosition() node.setParent(group_node) node.setPosition(world - center) for node in group_node.getChildren(): Selection.remove(node) Selection.add(group_node)
def __init__(self): super().__init__() # Call super to make multiple inheritance work. self._root = SceneNode() self._root.setCalculateBoundingBox(False) self._connectSignalsRoot() self._active_camera = None self._lock = threading.Lock()
def test_scale(self): node = SceneNode() self.assertEqual(node.getScale(), Vector(1, 1, 1)) node.scale(Vector(1.5, 1.5, 1.5)) self.assertEqual(node.getScale(), Vector(1.5, 1.5, 1.5)) node.scale(Vector(1.5, 1.5, 1.5)) self.assertEqual(node.getScale(), Vector(2.25, 2.25, 2.25))
def test_rotate(self): node = SceneNode() self.assertEqual(node.getOrientation(), Quaternion()) node.rotate(Quaternion.fromAngleAxis(math.pi / 4, Vector.Unit_Z)) self.assertEqual(node.getOrientation(), Quaternion.fromAngleAxis(math.pi / 4, Vector.Unit_Z)) node.rotate(Quaternion.fromAngleAxis(math.pi / 4, Vector.Unit_Z)) self.assertEqual(node.getOrientation(), Quaternion.fromAngleAxis(math.pi / 2, Vector.Unit_Z))
def test_translate(self): node = SceneNode() self.assertEqual(node.getPosition(), Vector(0, 0, 0)) node.translate(Vector(0, 0, 10)) self.assertEqual(node.getPosition(), Vector(0, 0, 10)) node.translate(Vector(0, 0, 10)) self.assertEqual(node.getPosition(), Vector(0, 0, 20))