def _createEraserMesh(self, parent: CuraSceneNode, position: Vector): node = CuraSceneNode() node.setName("Eraser") node.setSelectable(True) mesh = MeshBuilder() mesh.addCube(10,10,10) node.setMeshData(mesh.build()) active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) stack = node.callDecoration("getStack") # created by SettingOverrideDecorator that is automatically added to CuraSceneNode settings = stack.getTop() definition = stack.getSettingDefinition("anti_overhang_mesh") new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", True) new_instance.resetState() # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) op = GroupedOperation() # First add node to the scene at the correct position/scale, before parenting, so the eraser mesh does not get scaled with the parent op.addOperation(AddSceneNodeOperation(node, self._controller.getScene().getRoot())) op.addOperation(SetParentOperation(node, parent)) op.push() node.setPosition(position, CuraSceneNode.TransformSpace.World) Application.getInstance().getController().getScene().sceneChanged.emit(node)
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 __init__(self, node, hull, thickness, parent = None): super().__init__(parent) self.setCalculateBoundingBox(False) self._original_parent = parent # Color of the drawn convex hull if Application.getInstance().hasGui(): self._color = Color(*Application.getInstance().getTheme().getColor("convex_hull").getRgb()) else: self._color = Color(0, 0, 0) # The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting. self._mesh_height = 0.1 self._thickness = thickness # The node this mesh is "watching" self._node = node self._convex_hull_head_mesh = None self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged) self._onNodeDecoratorsChanged(self._node) self._hull = hull if self._hull: hull_mesh_builder = MeshBuilder() if hull_mesh_builder.addConvexPolygonExtrusion( self._hull.getPoints()[::-1], # bottom layer is reversed self._mesh_height-thickness, self._mesh_height, color=self._color): hull_mesh = hull_mesh_builder.build() self.setMeshData(hull_mesh)
def _onNodeDecoratorsChanged(self, node): convex_hull_head = self._node.callDecoration("getConvexHullHead") if convex_hull_head: convex_hull_head_builder = MeshBuilder() convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height - self._thickness) self._convex_hull_head_mesh = convex_hull_head_builder.build() if not node: return
def createMeshOrJumps(self, make_mesh): builder = MeshBuilder() for polygon in self._polygons: if make_mesh and (polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType): continue if not make_mesh and not (polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType): continue poly_color = polygon.getColor() points = numpy.copy(polygon.data) if polygon.type == Polygon.InfillType or polygon.type == Polygon.SkinType or polygon.type == Polygon.SupportInfillType: points[:,1] -= 0.01 if polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType: points[:,1] += 0.01 # Calculate normals for the entire polygon using numpy. normals = numpy.copy(points) normals[:,1] = 0.0 # We are only interested in 2D normals # Calculate the edges between points. # The call to numpy.roll shifts the entire array by one so that # we end up subtracting each next point from the current, wrapping # around. This gives us the edges from the next point to the current # point. normals[:] = normals[:] - numpy.roll(normals, -1, axis = 0) # Calculate the length of each edge using standard Pythagoras lengths = numpy.sqrt(normals[:,0] ** 2 + normals[:,2] ** 2) # The normal of a 2D vector is equal to its x and y coordinates swapped # and then x inverted. This code does that. normals[:,[0, 2]] = normals[:,[2, 0]] normals[:,0] *= -1 # Normalize the normals. normals[:,0] /= lengths normals[:,2] /= lengths # Scale all by the line width of the polygon so we can easily offset. normals *= (polygon.lineWidth / 2) #TODO: Use numpy magic to perform the vertex creation to speed up things. for i in range(len(points)): start = points[i - 1] end = points[i] normal = normals[i - 1] point1 = Vector(data = start - normal) point2 = Vector(data = start + normal) point3 = Vector(data = end + normal) point4 = Vector(data = end - normal) builder.addQuad(point1, point2, point3, point4, color = poly_color) return builder.getData()
def read(self, file_name): try: self.defs = {} self.shapes = [] tree = ET.parse(file_name) xml_root = tree.getroot() if xml_root.tag != "X3D": return None scale = 1000 # Default X3D unit it one meter, while Cura's is one millimeters if xml_root[0].tag == "head": for head_node in xml_root[0]: if head_node.tag == "unit" and head_node.attrib.get("category") == "length": scale *= float(head_node.attrib["conversionFactor"]) break xml_scene = xml_root[1] else: xml_scene = xml_root[0] if xml_scene.tag != "Scene": return None self.transform = Matrix() self.transform.setByScaleFactor(scale) self.index_base = 0 # Traverse the scene tree, populate the shapes list self.processChildNodes(xml_scene) if self.shapes: builder = MeshBuilder() builder.setVertices(numpy.concatenate([shape.verts for shape in self.shapes])) builder.setIndices(numpy.concatenate([shape.faces for shape in self.shapes])) builder.calculateNormals() builder.setFileName(file_name) mesh_data = builder.build() # Manually try and get the extents of the mesh_data. This should prevent nasty NaN issues from # leaving the reader. mesh_data.getExtents() node = SceneNode() node.setMeshData(mesh_data) node.setSelectable(True) node.setName(file_name) else: return None except Exception: Logger.logException("e", "Exception in X3D reader") return None return 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 run(self) -> None: layer_data = None for node in DepthFirstIterator(self._scene.getRoot()): # type: ignore layer_data = node.callDecoration("getLayerData") if layer_data: break if self._cancel or not layer_data: return layer_mesh = MeshBuilder() for i in range(self._solid_layers): layer_number = self._layer_number - i if layer_number < 0: continue try: layer = layer_data.getLayer(layer_number).createMesh() except Exception: Logger.logException( "w", "An exception occurred while creating layer mesh.") return if not layer or layer.getVertices() is None: continue layer_mesh.addIndices(layer_mesh.getVertexCount() + layer.getIndices()) layer_mesh.addVertices(layer.getVertices()) # Scale layer color by a brightness factor based on the current layer number # This will result in a range of 0.5 - 1.0 to multiply colors by. brightness = numpy.ones( (1, 4), dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0 brightness[0, 3] = 1.0 layer_mesh.addColors(layer.getColors() * brightness) if self._cancel: return Job.yieldThread() if self._cancel: return Job.yieldThread() jump_mesh = layer_data.getLayer(self._layer_number).createJumps() if not jump_mesh or jump_mesh.getVertices() is None: jump_mesh = None self.setResult({"layers": layer_mesh.build(), "jumps": jump_mesh})
def test_getTransformedMeshdata(self): node = SceneNode() node.translate(Vector(10, 0, 0)) builder = MeshBuilder() builder.addVertex(10, 20, 20) node.setMeshData(builder.build()) transformed_mesh = node.getMeshDataTransformed() transformed_vertex = transformed_mesh.getVertices()[0] assert transformed_vertex[0] == 20 assert transformed_vertex[1] == 20 assert transformed_vertex[2] == 20
def test_getExtents(): # Create a cube mesh at position 0,0,0 builder = MeshBuilder() builder.addCube(20, 20, 20) mesh_data = builder.build() extents = mesh_data.getExtents() assert extents.width == 20 assert extents.height == 20 assert extents.depth == 20 assert extents.maximum == Vector(10, 10, 10) assert extents.minimum == Vector(-10, -10, -10)
def _onNodeDecoratorsChanged(self, node): self._color = Color(35, 35, 35, 0.5) convex_hull_head = self._node.callDecoration("getConvexHullHead") if convex_hull_head: convex_hull_head_builder = MeshBuilder() convex_hull_head_builder.addConvexPolygon( convex_hull_head.getPoints(), self._mesh_height - self._thickness) self._convex_hull_head_mesh = convex_hull_head_builder.build() if not node: return
def test_readBinary(application): reader = STLReader.STLReader() binary_path = os.path.join(test_path, "simpleTestCubeBinary.stl") result = reader.read(binary_path) if STLReader.use_numpystl: # If the system the test runs on supporst numpy stl, we should also check the non numpy stl option. f = open(binary_path, "rb") mesh_builder = MeshBuilder() reader._loadBinary(mesh_builder, f) mesh_builder.calculateNormals(fast=True) assert mesh_builder.getVertexCount() != 0 assert result
def createHullMesh(self, hull_points): # Input checking. if len(hull_points) < 3: return None mesh_builder = MeshBuilder() point_first = Vector(hull_points[0][0], self._mesh_height, hull_points[0][1]) point_previous = Vector(hull_points[1][0], self._mesh_height, hull_points[1][1]) for point in hull_points[2:]: # Add the faces in the order of a triangle fan. point_new = Vector(point[0], self._mesh_height, point[1]) mesh_builder.addFace(point_first, point_previous, point_new, color = self._color) point_previous = point_new # Prepare point_previous for the next triangle. return mesh_builder.getData()
def test_readASCII(): reader = STLReader.STLReader() ascii_path = os.path.join(test_path, "simpleTestCubeASCII.stl") result = reader.read(ascii_path) assert result if STLReader.use_numpystl: # If the system the test runs on supporst numpy stl, we should also check the non numpy stl option. f = open(ascii_path, "rt") mesh_builder = MeshBuilder() reader._loadAscii(mesh_builder, f) mesh_builder.calculateNormals(fast=True) assert mesh_builder.getVertexCount() != 0
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_addVertices(): builder = MeshBuilder() builder.addVertices(numpy.zeros(3)) assert builder.getVertexCount() == 3 builder.addVertices(numpy.zeros(3)) assert builder.getVertexCount() == 6
def test_setUVCoordinates(): builder = MeshBuilder() builder.setVertices(numpy.zeros((10 * 3, 3), dtype=numpy.float32)) builder.setVertexUVCoordinates(5, 20, 22) assert builder.hasUVCoordinates() assert builder.getUVCoordinates()[5, 0] == 20 assert builder.getUVCoordinates()[5, 1] == 22
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 _onNodeDecoratorsChanged(self, node): self._color = Color(*Application.getInstance().getTheme().getColor( "convex_hull").getRgb()) convex_hull_head = self._node.callDecoration("getConvexHullHead") if convex_hull_head: convex_hull_head_builder = MeshBuilder() convex_hull_head_builder.addConvexPolygon( convex_hull_head.getPoints(), self._mesh_height - self._thickness) self._convex_hull_head_mesh = convex_hull_head_builder.build() if not node: return
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 createHullMesh(self, hull_points): # Input checking. if len(hull_points) < 3: return None mesh_builder = MeshBuilder() point_first = Vector(hull_points[0][0], self._mesh_height, hull_points[0][1]) point_previous = Vector(hull_points[1][0], self._mesh_height, hull_points[1][1]) for point in hull_points[2:]: # Add the faces in the order of a triangle fan. point_new = Vector(point[0], self._mesh_height, point[1]) mesh_builder.addFace(point_first, point_previous, point_new, color = self._color) point_previous = point_new # Prepare point_previous for the next triangle. return mesh_builder.build()
def test_readBinary(application): reader = STLReader.STLReader(application) binary_path = os.path.join(test_path, "simpleTestCubeBinary.stl") result = reader.read(binary_path) if STLReader.use_numpystl: # If the system the test runs on supporst numpy stl, we should also check the non numpy stl option. f = open(binary_path, "rb") mesh_builder = MeshBuilder() reader._loadBinary(mesh_builder, f) mesh_builder.calculateNormals(fast=True) assert mesh_builder.getVertexCount() != 0 assert result
def test_readASCII(application): reader = STLReader.STLReader() ascii_path = os.path.join(test_path, "simpleTestCubeASCII.stl") result = reader.read(ascii_path) assert result if STLReader.use_numpystl: # If the system the test runs on supports numpy stl, we should also check the non numpy stl option. f = open(ascii_path, "rt", encoding = "utf-8") mesh_builder = MeshBuilder() reader._loadAscii(mesh_builder, f) mesh_builder.calculateNormals(fast=True) assert mesh_builder.getVertexCount() != 0
def createMesh(self): builder = MeshBuilder() for polygon in self._polygons: poly_color = polygon.getColor() points = numpy.copy(polygon.data) if polygon.type == Polygon.InfillType or polygon.type == Polygon.SkinType or polygon.type == Polygon.SupportInfillType: points[:,1] -= 0.01 # Calculate normals for the entire polygon using numpy. normals = numpy.copy(points) normals[:,1] = 0.0 # We are only interested in 2D normals # Calculate the edges between points. # The call to numpy.roll shifts the entire array by one so that # we end up subtracting each next point from the current, wrapping # around. This gives us the edges from the next point to the current # point. normals[:] = normals[:] - numpy.roll(normals, -1, axis = 0) # Calculate the length of each edge using standard Pythagoras lengths = numpy.sqrt(normals[:,0] ** 2 + normals[:,2] ** 2) # The normal of a 2D vector is equal to its x and y coordinates swapped # and then x inverted. This code does that. normals[:,[0, 2]] = normals[:,[2, 0]] normals[:,0] *= -1 # Normalize the normals. normals[:,0] /= lengths normals[:,2] /= lengths # Scale all by the line width of the polygon so we can easily offset. normals *= (polygon.lineWidth / 2) #TODO: Use numpy magic to perform the vertex creation to speed up things. for i in range(len(points)): start = points[i - 1] end = points[i] normal = normals[i - 1] point1 = Vector(data = start - normal) point2 = Vector(data = start + normal) point3 = Vector(data = end + normal) point4 = Vector(data = end - normal) builder.addQuad(point1, point2, point3, point4, color = poly_color) return builder.getData()
def test_getSetType(): builder = MeshBuilder() builder.setType(MeshType.faces) assert builder.getType() == MeshType.faces # Should have no effect builder.setType("ZOMG") assert builder.getType() == MeshType.faces
def test_reserveVertexCount(): builder = MeshBuilder() assert builder.getVertices() is None builder.addVertex(1, 2, 3) builder.reserveVertexCount(10) # Reserving face count should reset the verts assert builder.getVertexCount() == 0
def test_getExtentsTransposed(): # Create a cube mesh at position 0,0,0 builder = MeshBuilder() builder.addCube(20, 20, 20) mesh_data = builder.build() transformation_matrix = Matrix() transformation_matrix.setByTranslation(Vector(10, 10, 10)) extents = mesh_data.getExtents(transformation_matrix) assert extents.width == 20 assert extents.height == 20 assert extents.depth == 20 assert extents.maximum == Vector(20, 20, 20) assert extents.minimum == Vector(0, 0, 0)
def __init__(self, node: SceneNode, hull: Optional[Polygon], thickness: float, parent: Optional[SceneNode] = None) -> None: super().__init__(parent) self.setCalculateBoundingBox(False) self._original_parent = parent # Color of the drawn convex hull if not Application.getInstance().getIsHeadLess(): theme = QtApplication.getInstance().getTheme() if theme: self._color = Color(*theme.getColor("convex_hull").getRgb()) else: self._color = Color(0, 0, 0) else: self._color = Color(0, 0, 0) # The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting. self._mesh_height = 0.1 self._thickness = thickness # The node this mesh is "watching" self._node = node # Area of the head + fans for display as a shadow on the buildplate self._convex_hull_head_mesh = None # type: Optional[MeshData] self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged) self._onNodeDecoratorsChanged(self._node) self._hull = hull if self._hull: hull_mesh_builder = MeshBuilder() if hull_mesh_builder.addConvexPolygonExtrusion( self._hull.getPoints()[::-1], # bottom layer is reversed self._mesh_height - thickness, self._mesh_height, color=self._color): hull_mesh = hull_mesh_builder.build() self.setMeshData(hull_mesh)
def test_reserveFaceAndVertexCount(): builder = MeshBuilder() builder.addVertex(1, 2, 3) builder.reserveFaceAndVertexCount(200, 20) # Reserving face count should reset the verts assert builder.getVertexCount() == 0
def test_render(): mocked_shader = MagicMock() with patch("UM.View.GL.OpenGL.OpenGL.getInstance"): render_batch = RenderBatch(mocked_shader) # Render without a camera shouldn't cause any effect. render_batch.render(None) assert mocked_shader.bind.call_count == 0 # Rendering with a camera should cause the shader to be bound and released (even if the batch is empty) mocked_camera = MagicMock() mocked_camera.getWorldTransformation = MagicMock(return_value=Matrix()) mocked_camera.getViewProjectionMatrix = MagicMock(return_value=Matrix()) with patch("UM.View.GL.OpenGLContext.OpenGLContext.properties"): render_batch.render(mocked_camera) assert mocked_shader.bind.call_count == 1 assert mocked_shader.release.call_count == 1 # Actualy render with an item in the batch mb = MeshBuilder() mb.addPyramid(10, 10, 10, color=Color(0.0, 1.0, 0.0, 1.0)) mb.calculateNormals() mesh_data = mb.build() render_batch.addItem(Matrix(), mesh_data, {}) with patch("UM.View.GL.OpenGL.OpenGL.getInstance"): with patch("UM.View.GL.OpenGLContext.OpenGLContext.properties"): render_batch.render(mocked_camera) assert mocked_shader.bind.call_count == 2 assert mocked_shader.release.call_count == 2
def test_addFaceWithNormals(): builder = MeshBuilder() builder.addFaceWithNormals(0, 0, 0, 1, 0, 0, 10, 0, 0, 0, 1, 0, 10, 10, 0, 0, 0, 1) assert builder.getVertexCount() == 3 assert builder.getFaceCount() == 1 assert builder.hasNormals()
def test_addLineWithColor(): builder = MeshBuilder() builder.addLine(Vector(0, 0, 0), Vector(10, 11, 12), Color(1.0, 0.5, 0.25)) assert builder.getColors()[0][0] == 1.0 assert builder.getColors()[0][1] == 0.5 assert builder.getColors()[0][2] == 0.25 assert builder.getColors()[1][0] == 1.0 assert builder.getColors()[1][1] == 0.5 assert builder.getColors()[1][2] == 0.25
def createMeshOrJumps(self, make_mesh): builder = MeshBuilder() for polygon in self._polygons: if make_mesh and (polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType): continue if not make_mesh and not (polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType): continue poly_color = polygon.getColor() points = numpy.copy(polygon.data) if polygon.type == Polygon.InfillType or polygon.type == Polygon.SkinType or polygon.type == Polygon.SupportInfillType: points[:, 1] -= 0.01 if polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType: points[:, 1] += 0.01 normals = polygon.getNormals() # Scale all by the line width of the polygon so we can easily offset. normals *= (polygon.lineWidth / 2) #TODO: Use numpy magic to perform the vertex creation to speed up things. for i in range(len(points)): start = points[i - 1] end = points[i] normal = normals[i - 1] point1 = Vector(data=start - normal) point2 = Vector(data=start + normal) point3 = Vector(data=end + normal) point4 = Vector(data=end - normal) builder.addQuad(point1, point2, point3, point4, color=poly_color) return builder.getData()
def test_addLine(): builder = MeshBuilder() builder.addLine(Vector(0, 0, 0), Vector(10, 11, 12)) assert builder.getVertexCount() == 2 assert builder.getVertex(1)[0] == 10 assert builder.getVertex(1)[1] == 11 assert builder.getVertex(1)[2] == 12
def test_compute2DConvexHullMeshDataGrouped(convex_hull_decorator): parent_node = SceneNode() parent_node.addDecorator(GroupDecorator()) node = SceneNode() parent_node.addChild(node) 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(parent_node) with patch("cura.Settings.ExtruderManager.ExtruderManager.getInstance"): copied_decorator = copy.deepcopy(convex_hull_decorator) copied_decorator._getSettingProperty = MagicMock(return_value=0) node.addDecorator(copied_decorator) assert convex_hull_decorator._compute2DConvexHull() == Polygon([[-5.0,5.0], [5.0,5.0], [5.0,-5.0], [-5.0,-5.0]])
def run(self): layer_data = None for node in DepthFirstIterator(self._scene.getRoot()): layer_data = node.callDecoration("getLayerData") if layer_data: break if self._cancel or not layer_data: return layer_mesh = MeshBuilder() for i in range(self._solid_layers): layer_number = self._layer_number - i if layer_number < 0: continue try: layer = layer_data.getLayer(layer_number).createMesh() except Exception: Logger.logException("w", "An exception occurred while creating layer mesh.") return if not layer or layer.getVertices() is None: continue layer_mesh.addIndices(layer_mesh.getVertexCount() + layer.getIndices()) layer_mesh.addVertices(layer.getVertices()) # Scale layer color by a brightness factor based on the current layer number # This will result in a range of 0.5 - 1.0 to multiply colors by. brightness = numpy.ones((1, 4), dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0 brightness[0, 3] = 1.0 layer_mesh.addColors(layer.getColors() * brightness) if self._cancel: return Job.yieldThread() if self._cancel: return Job.yieldThread() jump_mesh = layer_data.getLayer(self._layer_number).createJumps() if not jump_mesh or jump_mesh.getVertices() is None: jump_mesh = None self.setResult({"layers": layer_mesh.build(), "jumps": jump_mesh})
def _createEraserMesh(self): node = CuraSceneNode() node.setName("Eraser") node.setSelectable(True) mesh = MeshBuilder() mesh.addCube(10, 10, 10) node.setMeshData(mesh.build()) # Place the cube in the platform. Do it manually so it works if the "automatic drop models" is OFF move_vector = Vector(0, 5, 0) node.setPosition(move_vector) active_build_plate = Application.getInstance().getMultiBuildPlateModel( ).activeBuildPlate node.addDecorator(SettingOverrideDecorator()) node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) stack = node.callDecoration( "getStack" ) #Don't try to get the active extruder since it may be None anyway. if not stack: node.addDecorator(SettingOverrideDecorator()) stack = node.callDecoration("getStack") settings = stack.getTop() if not (settings.getInstance("anti_overhang_mesh") and settings.getProperty("anti_overhang_mesh", "value")): definition = stack.getSettingDefinition("anti_overhang_mesh") new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", True) new_instance.resetState( ) # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) scene = self._controller.getScene() op = AddSceneNodeOperation(node, scene.getRoot()) op.push() Application.getInstance().getController().getScene().sceneChanged.emit( node)
def test_setCenterPosition(self): node = SceneNode() child_node = SceneNode() node.addChild(child_node) child_node.setCenterPosition = MagicMock() builder = MeshBuilder() builder.addVertex(10, 20, 20) node.setMeshData(builder.build()) node.setCenterPosition(Vector(-10, 0, 0)) transformed_mesh = node.getMeshData() transformed_vertex = transformed_mesh.getVertices()[0] assert transformed_vertex[0] == 20 assert transformed_vertex[1] == 20 assert transformed_vertex[2] == 20 child_node.setCenterPosition.assert_called_once_with(Vector(-10, 0, 0))
def _createCube(self, size, maxs, height, dep): mesh = MeshBuilder() # Intial Comment from Ultimaker B.V. I have never try to verify this point # Can't use MeshBuilder.addCube() because that does not get per-vertex normals # Per-vertex normals require duplication of vertices s = size / 2 sm = maxs / 2 l = height s_inf=math.tan(math.radians(dep))*l+s if sm>s and dep!=0: l_max=(sm-s) / math.tan(math.radians(dep)) else : l_max=l # Difference between Cone and Cone + max base size if l_max<l and l_max>0: nbv=40 verts = [ # 10 faces with 4 corners each [-sm, -l_max, sm], [-s, s, s], [ s, s, s], [ sm, -l_max, sm], [-s, s, -s], [-sm, -l_max, -sm], [ sm, -l_max, -sm], [ s, s, -s], [-sm, -l, sm], [-sm, -l_max, sm], [ sm, -l_max, sm], [ sm, -l, sm], [-sm, -l_max, -sm], [-sm, -l, -sm], [ sm, -l, -sm], [ sm, -l_max, -sm], [ sm, -l, -sm], [-sm, -l, -sm], [-sm, -l, sm], [ sm, -l, sm], [-s, s, -s], [ s, s, -s], [ s, s, s], [-s, s, s], [-sm, -l, sm], [-sm, -l, -sm], [-sm, -l_max, -sm], [-sm, -l_max, sm], [ sm, -l, -sm], [ sm, -l, sm], [ sm, -l_max, sm], [ sm, -l_max, -sm], [-sm, -l_max, sm], [-sm, -l_max, -sm], [-s, s, -s], [-s, s, s], [ sm, -l_max, -sm], [ sm, -l_max, sm], [ s, s, s], [ s, s, -s] ] else: nbv=24 verts = [ # 6 faces with 4 corners each [-s_inf, -l, s_inf], [-s, s, s], [ s, s, s], [ s_inf, -l, s_inf], [-s, s, -s], [-s_inf, -l, -s_inf], [ s_inf, -l, -s_inf], [ s, s, -s], [ s_inf, -l, -s_inf], [-s_inf, -l, -s_inf], [-s_inf, -l, s_inf], [ s_inf, -l, s_inf], [-s, s, -s], [ s, s, -s], [ s, s, s], [-s, s, s], [-s_inf, -l, s_inf], [-s_inf, -l, -s_inf], [-s, s, -s], [-s, s, s], [ s_inf, -l, -s_inf], [ s_inf, -l, s_inf], [ s, s, s], [ s, s, -s] ] mesh.setVertices(numpy.asarray(verts, dtype=numpy.float32)) indices = [] for i in range(0, nbv, 4): # All 6 quads (12 triangles) indices.append([i, i+2, i+1]) indices.append([i, i+3, i+2]) mesh.setIndices(numpy.asarray(indices, dtype=numpy.int32)) mesh.calculateNormals() return mesh
def drawSelection(self): if self._tri is None: return ph = self._connector.propertyHandler if ph._selection_mode is SelectionMode.AnchorMode: self.setFace(ph._anchoredTris) else: self.setFace(ph._loadedTris) # Construct Edges using MeshBuilder Cubes mb = MeshBuilder() for tri in self._tri: mb.addFace(tri.v1, tri.v2, tri.v3, color=self._selected_color) if self._connector.propertyHandler._selection_mode == SelectionMode.LoadMode: self.paintArrow(self._tri, mb) # Add to Cura Scene self.setSolidMesh(mb.build())
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 test_render(): mocked_shader = MagicMock() with patch("UM.View.GL.OpenGL.OpenGL.getInstance"): render_batch = RenderBatch(mocked_shader) # Render without a camera shouldn't cause any effect. render_batch.render(None) assert mocked_shader.bind.call_count == 0 # Rendering with a camera should cause the shader to be bound and released (even if the batch is empty) mocked_camera = MagicMock() mocked_camera.getWorldTransformation = MagicMock(return_value = Matrix()) mocked_camera.getViewProjectionMatrix = MagicMock(return_value=Matrix()) with patch("UM.View.GL.OpenGLContext.OpenGLContext.properties"): render_batch.render(mocked_camera) assert mocked_shader.bind.call_count == 1 assert mocked_shader.release.call_count == 1 # Actualy render with an item in the batch mb = MeshBuilder() mb.addPyramid(10, 10, 10, color=Color(0.0, 1.0, 0.0, 1.0)) mb.calculateNormals() mesh_data = mb.build() render_batch.addItem(Matrix(), mesh_data, {}) with patch("UM.View.GL.OpenGL.OpenGL.getInstance"): with patch("UM.View.GL.OpenGLContext.OpenGLContext.properties"): render_batch.render(mocked_camera) assert mocked_shader.bind.call_count == 2 assert mocked_shader.release.call_count == 2
def _getAxisMesh(self, node): mb = MeshBuilder() mb.addCube(width=self.axis_width, height=self.axis_height, depth=self.axis_width, color=self.YAxisColor, center=Vector(0, self.axis_height / 2, 0)) mb.addCube(width=self.axis_width, height=self.axis_width, depth=self.axis_height, color=self.ZAxisColor, center=Vector(0, 0, self.axis_height / 2)) mb.addCube(width=self.axis_height, height=self.axis_width, depth=self.axis_width, color=self.XAxisColor, center=Vector(self.axis_height / 2, 0, 0)) return mb.build().getTransformed(node.getWorldTransformation())
def test_compute2DConvexHullMeshDataGrouped(convex_hull_decorator): parent_node = SceneNode() parent_node.addDecorator(GroupDecorator()) node = SceneNode() parent_node.addChild(node) 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(parent_node) with patch( "cura.Settings.ExtruderManager.ExtruderManager.getInstance"): copied_decorator = copy.deepcopy(convex_hull_decorator) copied_decorator._getSettingProperty = MagicMock(return_value=0) node.addDecorator(copied_decorator) assert convex_hull_decorator._compute2DConvexHull() == Polygon( [[-5.0, 5.0], [5.0, 5.0], [5.0, -5.0], [-5.0, -5.0]])
def createMeshOrJumps(self, make_mesh): builder = MeshBuilder() for polygon in self._polygons: if make_mesh and (polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType): continue if not make_mesh and not (polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType): continue poly_color = polygon.getColor() points = numpy.copy(polygon.data) if polygon.type == LayerPolygon.InfillType or polygon.type == LayerPolygon.SkinType or polygon.type == LayerPolygon.SupportInfillType: points[:,1] -= 0.01 if polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType: points[:,1] += 0.01 normals = polygon.getNormals() # Scale all by the line width of the polygon so we can easily offset. normals *= (polygon.lineWidth / 2) #TODO: Use numpy magic to perform the vertex creation to speed up things. for i in range(len(points)): start = points[i - 1] end = points[i] normal = normals[i - 1] point1 = Vector(data = start - normal) point2 = Vector(data = start + normal) point3 = Vector(data = end + normal) point4 = Vector(data = end - normal) builder.addQuad(point1, point2, point3, point4, color = poly_color) return builder.getData()
def createMeshOrJumps(self, make_mesh): builder = MeshBuilder() line_count = 0 if make_mesh: for polygon in self._polygons: line_count += polygon.meshLineCount else: for polygon in self._polygons: line_count += polygon.jumpCount # Reserve the neccesary space for the data upfront builder.reserveFaceAndVertexCount(2 * line_count, 4 * line_count) for polygon in self._polygons: # Filter out the types of lines we are not interesed in depending on whether we are drawing the mesh or the jumps. index_mask = numpy.logical_not(polygon.jumpMask) if make_mesh else polygon.jumpMask # Create an array with rows [p p+1] and only keep those we whant to draw based on make_mesh points = numpy.concatenate((polygon.data[:-1], polygon.data[1:]), 1)[index_mask.ravel()] # Line types of the points we want to draw line_types = polygon.types[index_mask] # Shift the z-axis according to previous implementation. if make_mesh: points[polygon.isInfillOrSkinType(line_types), 1::3] -= 0.01 else: points[:, 1::3] += 0.01 # Create an array with normals and tile 2 copies to match size of points variable normals = numpy.tile( polygon.getNormals()[index_mask.ravel()], (1, 2)) # Scale all normals by the line width of the current line so we can easily offset. normals *= (polygon.lineWidths[index_mask.ravel()] / 2) # Create 4 points to draw each line segment, points +- normals results in 2 points each. # After this we reshape to one point per line. f_points = numpy.concatenate((points-normals, points+normals), 1).reshape((-1, 3)) # __index_pattern defines which points to use to draw the two faces for each lines egment, the following linesegment is offset by 4 f_indices = ( self.__index_pattern + numpy.arange(0, 4 * len(normals), 4, dtype=numpy.int32).reshape((-1, 1)) ).reshape((-1, 3)) f_colors = numpy.repeat(polygon.mapLineTypeToColor(line_types), 4, 0) builder.addFacesWithColor(f_points, f_indices, f_colors) return builder.build()
def _createCube(self, size): mesh = MeshBuilder() # Can't use MeshBuilder.addCube() because that does not get per-vertex normals # Per-vertex normals require duplication of vertices s = size / 2 verts = [ # 6 faces with 4 corners each [-s, -s, s], [-s, s, s], [ s, s, s], [ s, -s, s], [-s, s, -s], [-s, -s, -s], [ s, -s, -s], [ s, s, -s], [ s, -s, -s], [-s, -s, -s], [-s, -s, s], [ s, -s, s], [-s, s, -s], [ s, s, -s], [ s, s, s], [-s, s, s], [-s, -s, s], [-s, -s, -s], [-s, s, -s], [-s, s, s], [ s, -s, -s], [ s, -s, s], [ s, s, s], [ s, s, -s] ] mesh.setVertices(numpy.asarray(verts, dtype=numpy.float32)) indices = [] for i in range(0, 24, 4): # All 6 quads (12 triangles) indices.append([i, i+2, i+1]) indices.append([i, i+3, i+2]) mesh.setIndices(numpy.asarray(indices, dtype=numpy.int32)) mesh.calculateNormals() return mesh
def rebuild(self): if not self._width or not self._height or not self._depth: return min_w = -self._width / 2 max_w = self._width / 2 min_h = 0.0 max_h = self._height min_d = -self._depth / 2 max_d = self._depth / 2 mb = MeshBuilder() # Outline 'cube' of the build volume mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) self.setMeshData(mb.build()) mb = MeshBuilder() mb.addQuad( Vector(min_w, min_h - 0.2, min_d), Vector(max_w, min_h - 0.2, min_d), Vector(max_w, min_h - 0.2, max_d), Vector(min_w, min_h - 0.2, max_d) ) for n in range(0, 6): v = mb.getVertex(n) mb.setVertexUVCoordinates(n, v[0], v[2]) self._grid_mesh = mb.build() disallowed_area_height = 0.1 disallowed_area_size = 0 if self._disallowed_areas: mb = MeshBuilder() color = Color(0.0, 0.0, 0.0, 0.15) for polygon in self._disallowed_areas: points = polygon.getPoints() first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) for point in points: new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height, self._clamp(point[1], min_d, max_d)) mb.addFace(first, previous_point, new_point, color = color) previous_point = new_point # Find the largest disallowed area to exclude it from the maximum scale bounds. # This is a very nasty hack. This pretty much only works for UM machines. # This disallowed area_size needs a -lot- of rework at some point in the future: TODO if numpy.min(points[:, 1]) >= 0: # This filters out all areas that have points to the left of the centre. This is done to filter the skirt area. size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1])) else: size = 0 disallowed_area_size = max(size, disallowed_area_size) self._disallowed_area_mesh = mb.build() else: self._disallowed_area_mesh = None if self._prime_tower_area: mb = MeshBuilder() color = Color(1.0, 0.0, 0.0, 0.5) points = self._prime_tower_area.getPoints() first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) for point in points: new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height, self._clamp(point[1], min_d, max_d)) mb.addFace(first, previous_point, new_point, color=color) previous_point = new_point self._prime_tower_area_mesh = mb.build() else: self._prime_tower_area_mesh = None self._volume_aabb = AxisAlignedBox( minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h - self._raft_thickness, max_d)) bed_adhesion_size = 0.0 container_stack = Application.getInstance().getGlobalContainerStack() if container_stack: bed_adhesion_size = self._getBedAdhesionSize(container_stack) # As this works better for UM machines, we only add the disallowed_area_size for the z direction. # This is probably wrong in all other cases. TODO! # The +1 and -1 is added as there is always a bit of extra room required to work properly. scale_to_max_bounds = AxisAlignedBox( minimum = Vector(min_w + bed_adhesion_size + 1, min_h, min_d + disallowed_area_size - bed_adhesion_size + 1), maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness, max_d - disallowed_area_size + bed_adhesion_size - 1) ) Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
def rebuild(self): if self._width == 0 or self._height == 0 or self._depth == 0: return min_w = -self._width / 2 max_w = self._width / 2 min_h = 0.0 max_h = self._height min_d = -self._depth / 2 max_d = self._depth / 2 mb = MeshBuilder() mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) self.setMeshData(mb.getData()) mb = MeshBuilder() mb.addQuad( Vector(min_w, min_h - 0.2, min_d), Vector(max_w, min_h - 0.2, min_d), Vector(max_w, min_h - 0.2, max_d), Vector(min_w, min_h - 0.2, max_d) ) self._grid_mesh = mb.getData() for n in range(0, 6): v = self._grid_mesh.getVertex(n) self._grid_mesh.setVertexUVCoordinates(n, v[0], v[2]) disallowed_area_height = 0.1 disallowed_area_size = 0 if self._disallowed_areas: mb = MeshBuilder() color = Color(0.0, 0.0, 0.0, 0.15) for polygon in self._disallowed_areas: points = polygon.getPoints() first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) for point in points: new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height, self._clamp(point[1], min_d, max_d)) mb.addFace(first, previous_point, new_point, color = color) previous_point = new_point # Find the largest disallowed area to exclude it from the maximum scale bounds size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1])) disallowed_area_size = max(size, disallowed_area_size) self._disallowed_area_mesh = mb.getData() else: self._disallowed_area_mesh = None self._aabb = AxisAlignedBox(minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h, max_d)) skirt_size = 0.0 profile = Application.getInstance().getMachineManager().getActiveProfile() if profile: skirt_size = self._getSkirtSize(profile) scale_to_max_bounds = AxisAlignedBox( minimum = Vector(min_w + skirt_size, min_h, min_d + skirt_size + disallowed_area_size), maximum = Vector(max_w - skirt_size, max_h, max_d - skirt_size - disallowed_area_size) ) Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
def __init__(self, parent = None): super().__init__(parent) self._handle_width = 8 self._handle_height = 14 self._handle_position = 20 mb = MeshBuilder() mb.addPyramid( width = self._handle_width, height = self._handle_height, depth = self._handle_width, center = Vector(0, self._handle_position, 0), color = ToolHandle.YAxisColor ) mb.addPyramid( width = self._handle_width, height = self._handle_height, depth = self._handle_width, center = Vector(0, -self._handle_position, 0), color = ToolHandle.YAxisColor, axis = Vector.Unit_X, angle = 180 ) mb.addPyramid( width = self._handle_width, height = self._handle_height, depth = self._handle_width, center = Vector(self._handle_position, 0, 0), color = ToolHandle.XAxisColor, axis = Vector.Unit_Z, angle = 90 ) mb.addPyramid( width = self._handle_width, height = self._handle_height, depth = self._handle_width, center = Vector(-self._handle_position, 0, 0), color = ToolHandle.XAxisColor, axis = Vector.Unit_Z, angle = -90 ) mb.addPyramid( width = self._handle_width, height = self._handle_height, depth = self._handle_width, center = Vector(0, 0, -self._handle_position), color = ToolHandle.ZAxisColor, axis = Vector.Unit_X, angle = 90 ) mb.addPyramid( width = self._handle_width, height = self._handle_height, depth = self._handle_width, center = Vector(0, 0, self._handle_position), color = ToolHandle.ZAxisColor, axis = Vector.Unit_X, angle = -90 ) self.setSolidMesh(mb.getData()) self.setSelectionMesh(mb.getData())
def rebuild(self): if not self._width or not self._height or not self._depth: return min_w = -self._width / 2 max_w = self._width / 2 min_h = 0.0 max_h = self._height min_d = -self._depth / 2 max_d = self._depth / 2 z_fight_distance = 0.2 # Distance between buildplate and disallowed area meshes to prevent z-fighting if self._shape != "elliptic": # Outline 'cube' of the build volume mb = MeshBuilder() mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) self.setMeshData(mb.build()) # Build plate grid mesh mb = MeshBuilder() mb.addQuad( Vector(min_w, min_h - z_fight_distance, min_d), Vector(max_w, min_h - z_fight_distance, min_d), Vector(max_w, min_h - z_fight_distance, max_d), Vector(min_w, min_h - z_fight_distance, max_d) ) for n in range(0, 6): v = mb.getVertex(n) mb.setVertexUVCoordinates(n, v[0], v[2]) self._grid_mesh = mb.build() else: # Bottom and top 'ellipse' of the build volume aspect = 1.0 scale_matrix = Matrix() if self._width != 0: # Scale circular meshes by aspect ratio if width != height aspect = self._height / self._width scale_matrix.compose(scale = Vector(1, 1, aspect)) mb = MeshBuilder() mb.addArc(max_w, Vector.Unit_Y, center = (0, min_h - z_fight_distance, 0), color = self.VolumeOutlineColor) mb.addArc(max_w, Vector.Unit_Y, center = (0, max_h, 0), color = self.VolumeOutlineColor) self.setMeshData(mb.build().getTransformed(scale_matrix)) # Build plate grid mesh mb = MeshBuilder() mb.addVertex(0, min_h - z_fight_distance, 0) mb.addArc(max_w, Vector.Unit_Y, center = Vector(0, min_h - z_fight_distance, 0)) sections = mb.getVertexCount() - 1 # Center point is not an arc section indices = [] for n in range(0, sections - 1): indices.append([0, n + 2, n + 1]) mb.addIndices(numpy.asarray(indices, dtype = numpy.int32)) mb.calculateNormals() for n in range(0, mb.getVertexCount()): v = mb.getVertex(n) mb.setVertexUVCoordinates(n, v[0], v[2] * aspect) self._grid_mesh = mb.build().getTransformed(scale_matrix) # Indication of the machine origin if self._global_container_stack.getProperty("machine_center_is_zero", "value"): origin = (Vector(min_w, min_h, min_d) + Vector(max_w, min_h, max_d)) / 2 else: origin = Vector(min_w, min_h, max_d) mb = MeshBuilder() mb.addCube( width = self._origin_line_length, height = self._origin_line_width, depth = self._origin_line_width, center = origin + Vector(self._origin_line_length / 2, 0, 0), color = self.XAxisColor ) mb.addCube( width = self._origin_line_width, height = self._origin_line_length, depth = self._origin_line_width, center = origin + Vector(0, self._origin_line_length / 2, 0), color = self.YAxisColor ) mb.addCube( width = self._origin_line_width, height = self._origin_line_width, depth = self._origin_line_length, center = origin - Vector(0, 0, self._origin_line_length / 2), color = self.ZAxisColor ) self._origin_mesh = mb.build() disallowed_area_height = 0.1 disallowed_area_size = 0 if self._disallowed_areas: mb = MeshBuilder() color = Color(0.0, 0.0, 0.0, 0.15) for polygon in self._disallowed_areas: points = polygon.getPoints() first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) for point in points: new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height, self._clamp(point[1], min_d, max_d)) mb.addFace(first, previous_point, new_point, color = color) previous_point = new_point # Find the largest disallowed area to exclude it from the maximum scale bounds. # This is a very nasty hack. This pretty much only works for UM machines. # This disallowed area_size needs a -lot- of rework at some point in the future: TODO if numpy.min(points[:, 1]) >= 0: # This filters out all areas that have points to the left of the centre. This is done to filter the skirt area. size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1])) else: size = 0 disallowed_area_size = max(size, disallowed_area_size) self._disallowed_area_mesh = mb.build() else: self._disallowed_area_mesh = None if self._error_areas: mb = MeshBuilder() for error_area in self._error_areas: color = Color(1.0, 0.0, 0.0, 0.5) points = error_area.getPoints() first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) for point in points: new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height, self._clamp(point[1], min_d, max_d)) mb.addFace(first, previous_point, new_point, color=color) previous_point = new_point self._error_mesh = mb.build() else: self._error_mesh = None self._volume_aabb = AxisAlignedBox( minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h - self._raft_thickness, max_d)) bed_adhesion_size = self._getEdgeDisallowedSize() # As this works better for UM machines, we only add the disallowed_area_size for the z direction. # This is probably wrong in all other cases. TODO! # The +1 and -1 is added as there is always a bit of extra room required to work properly. scale_to_max_bounds = AxisAlignedBox( minimum = Vector(min_w + bed_adhesion_size + 1, min_h, min_d + disallowed_area_size - bed_adhesion_size + 1), maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness, max_d - disallowed_area_size + bed_adhesion_size - 1) ) Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
def read(self, file_name): result = SceneNode() # The base object of 3mf is a zipped archive. archive = zipfile.ZipFile(file_name, "r") try: root = ET.parse(archive.open("3D/3dmodel.model")) # There can be multiple objects, try to load all of them. objects = root.findall("./3mf:resources/3mf:object", self._namespaces) if len(objects) == 0: Logger.log("w", "No objects found in 3MF file %s, either the file is corrupt or you are using an outdated format", file_name) return None for entry in objects: mesh_builder = MeshBuilder() node = SceneNode() vertex_list = [] #for vertex in entry.mesh.vertices.vertex: for vertex in entry.findall(".//3mf:vertex", self._namespaces): vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")]) Job.yieldThread() triangles = entry.findall(".//3mf:triangle", self._namespaces) mesh_builder.reserveFaceCount(len(triangles)) #for triangle in object.mesh.triangles.triangle: for triangle in triangles: v1 = int(triangle.get("v1")) v2 = int(triangle.get("v2")) v3 = int(triangle.get("v3")) mesh_builder.addFaceByPoints(vertex_list[v1][0], vertex_list[v1][1], vertex_list[v1][2], vertex_list[v2][0], vertex_list[v2][1], vertex_list[v2][2], vertex_list[v3][0], vertex_list[v3][1], vertex_list[v3][2]) Job.yieldThread() # Rotate the model; We use a different coordinate frame. rotation = Matrix() rotation.setByRotationAxis(-0.5 * math.pi, Vector(1,0,0)) #TODO: We currently do not check for normals and simply recalculate them. mesh_builder.calculateNormals() node.setMeshData(mesh_builder.build().getTransformed(rotation)) node.setSelectable(True) transformations = root.findall("./3mf:build/3mf:item[@objectid='{0}']".format(entry.get("id")), self._namespaces) transformation = transformations[0] if transformations else None if transformation is not None and transformation.get("transform"): splitted_transformation = transformation.get("transform").split() ## Transformation is saved as: ## M00 M01 M02 0.0 ## M10 M11 M12 0.0 ## M20 M21 M22 0.0 ## M30 M31 M32 1.0 ## We switch the row & cols as that is how everyone else uses matrices! temp_mat = Matrix() # Rotation & Scale temp_mat._data[0,0] = splitted_transformation[0] temp_mat._data[1,0] = splitted_transformation[1] temp_mat._data[2,0] = splitted_transformation[2] temp_mat._data[0,1] = splitted_transformation[3] temp_mat._data[1,1] = splitted_transformation[4] temp_mat._data[2,1] = splitted_transformation[5] temp_mat._data[0,2] = splitted_transformation[6] temp_mat._data[1,2] = splitted_transformation[7] temp_mat._data[2,2] = splitted_transformation[8] # Translation temp_mat._data[0,3] = splitted_transformation[9] temp_mat._data[1,3] = splitted_transformation[10] temp_mat._data[2,3] = splitted_transformation[11] node.setTransformation(temp_mat) result.addChild(node) Job.yieldThread() #If there is more then one object, group them. if len(objects) > 1: group_decorator = GroupDecorator() result.addDecorator(group_decorator) except Exception as e: Logger.log("e" ,"exception occured in 3mf reader: %s" , e) return result
def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size, image_color_invert): scene_node = SceneNode() mesh = MeshBuilder() img = QImage(file_name) if img.isNull(): Logger.log("e", "Image is corrupt.") return None width = max(img.width(), 2) height = max(img.height(), 2) aspect = height / width if img.width() < 2 or img.height() < 2: img = img.scaled(width, height, Qt.IgnoreAspectRatio) base_height = max(base_height, 0) peak_height = max(peak_height, -base_height) xz_size = max(xz_size, 1) scale_vector = Vector(xz_size, peak_height, xz_size) if width > height: scale_vector = scale_vector.set(z=scale_vector.z * aspect) elif height > width: scale_vector = scale_vector.set(x=scale_vector.x / aspect) if width > max_size or height > max_size: scale_factor = max_size / width if height > width: scale_factor = max_size / height width = int(max(round(width * scale_factor), 2)) height = int(max(round(height * scale_factor), 2)) img = img.scaled(width, height, Qt.IgnoreAspectRatio) width_minus_one = width - 1 height_minus_one = height - 1 Job.yieldThread() texel_width = 1.0 / (width_minus_one) * scale_vector.x texel_height = 1.0 / (height_minus_one) * scale_vector.z height_data = numpy.zeros((height, width), dtype=numpy.float32) for x in range(0, width): for y in range(0, height): qrgb = img.pixel(x, y) avg = float(qRed(qrgb) + qGreen(qrgb) + qBlue(qrgb)) / (3 * 255) height_data[y, x] = avg Job.yieldThread() if image_color_invert: height_data = 1 - height_data for _ in range(0, blur_iterations): copy = numpy.pad(height_data, ((1, 1), (1, 1)), mode= "edge") height_data += copy[1:-1, 2:] height_data += copy[1:-1, :-2] height_data += copy[2:, 1:-1] height_data += copy[:-2, 1:-1] height_data += copy[2:, 2:] height_data += copy[:-2, 2:] height_data += copy[2:, :-2] height_data += copy[:-2, :-2] height_data /= 9 Job.yieldThread() height_data *= scale_vector.y height_data += base_height heightmap_face_count = 2 * height_minus_one * width_minus_one total_face_count = heightmap_face_count + (width_minus_one * 2) * (height_minus_one * 2) + 2 mesh.reserveFaceCount(total_face_count) # initialize to texel space vertex offsets. # 6 is for 6 vertices for each texel quad. heightmap_vertices = numpy.zeros((width_minus_one * height_minus_one, 6, 3), dtype = numpy.float32) heightmap_vertices = heightmap_vertices + numpy.array([[ [0, base_height, 0], [0, base_height, texel_height], [texel_width, base_height, texel_height], [texel_width, base_height, texel_height], [texel_width, base_height, 0], [0, base_height, 0] ]], dtype = numpy.float32) offsetsz, offsetsx = numpy.mgrid[0: height_minus_one, 0: width - 1] offsetsx = numpy.array(offsetsx, numpy.float32).reshape(-1, 1) * texel_width offsetsz = numpy.array(offsetsz, numpy.float32).reshape(-1, 1) * texel_height # offsets for each texel quad heightmap_vertex_offsets = numpy.concatenate([offsetsx, numpy.zeros((offsetsx.shape[0], offsetsx.shape[1]), dtype=numpy.float32), offsetsz], 1) heightmap_vertices += heightmap_vertex_offsets.repeat(6, 0).reshape(-1, 6, 3) # apply height data to y values heightmap_vertices[:, 0, 1] = heightmap_vertices[:, 5, 1] = height_data[:-1, :-1].reshape(-1) heightmap_vertices[:, 1, 1] = height_data[1:, :-1].reshape(-1) heightmap_vertices[:, 2, 1] = heightmap_vertices[:, 3, 1] = height_data[1:, 1:].reshape(-1) heightmap_vertices[:, 4, 1] = height_data[:-1, 1:].reshape(-1) heightmap_indices = numpy.array(numpy.mgrid[0:heightmap_face_count * 3], dtype=numpy.int32).reshape(-1, 3) mesh._vertices[0:(heightmap_vertices.size // 3), :] = heightmap_vertices.reshape(-1, 3) mesh._indices[0:(heightmap_indices.size // 3), :] = heightmap_indices mesh._vertex_count = heightmap_vertices.size // 3 mesh._face_count = heightmap_indices.size // 3 geo_width = width_minus_one * texel_width geo_height = height_minus_one * texel_height # bottom mesh.addFaceByPoints(0, 0, 0, 0, 0, geo_height, geo_width, 0, geo_height) mesh.addFaceByPoints(geo_width, 0, geo_height, geo_width, 0, 0, 0, 0, 0) # north and south walls for n in range(0, width_minus_one): x = n * texel_width nx = (n + 1) * texel_width hn0 = height_data[0, n] hn1 = height_data[0, n + 1] hs0 = height_data[height_minus_one, n] hs1 = height_data[height_minus_one, n + 1] mesh.addFaceByPoints(x, 0, 0, nx, 0, 0, nx, hn1, 0) mesh.addFaceByPoints(nx, hn1, 0, x, hn0, 0, x, 0, 0) mesh.addFaceByPoints(x, 0, geo_height, nx, 0, geo_height, nx, hs1, geo_height) mesh.addFaceByPoints(nx, hs1, geo_height, x, hs0, geo_height, x, 0, geo_height) # west and east walls for n in range(0, height_minus_one): y = n * texel_height ny = (n + 1) * texel_height hw0 = height_data[n, 0] hw1 = height_data[n + 1, 0] he0 = height_data[n, width_minus_one] he1 = height_data[n + 1, width_minus_one] mesh.addFaceByPoints(0, 0, y, 0, 0, ny, 0, hw1, ny) mesh.addFaceByPoints(0, hw1, ny, 0, hw0, y, 0, 0, y) mesh.addFaceByPoints(geo_width, 0, y, geo_width, 0, ny, geo_width, he1, ny) mesh.addFaceByPoints(geo_width, he1, ny, geo_width, he0, y, geo_width, 0, y) mesh.calculateNormals(fast=True) scene_node.setMeshData(mesh.build()) return scene_node
def _createNodeFromObject(self, object, name = ""): node = SceneNode() node.setName(name) mesh_builder = MeshBuilder() vertex_list = [] components = object.find(".//3mf:components", self._namespaces) if components: for component in components: id = component.get("objectid") new_object = self._root.find("./3mf:resources/3mf:object[@id='{0}']".format(id), self._namespaces) new_node = self._createNodeFromObject(new_object, self._base_name + "_" + str(id)) node.addChild(new_node) transform = component.get("transform") if transform is not None: new_node.setTransformation(self._createMatrixFromTransformationString(transform)) # for vertex in entry.mesh.vertices.vertex: for vertex in object.findall(".//3mf:vertex", self._namespaces): vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")]) Job.yieldThread() xml_settings = list(object.findall(".//cura:setting", self._namespaces)) # Add the setting override decorator, so we can add settings to this node. if xml_settings: node.addDecorator(SettingOverrideDecorator()) global_container_stack = Application.getInstance().getGlobalContainerStack() # Ensure the correct next container for the SettingOverride decorator is set. if global_container_stack: multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1 # Ensure that all extruder data is reset if not multi_extrusion: default_stack_id = global_container_stack.getId() else: default_stack = ExtruderManager.getInstance().getExtruderStack(0) if default_stack: default_stack_id = default_stack.getId() else: default_stack_id = global_container_stack.getId() node.callDecoration("setActiveExtruder", default_stack_id) # Get the definition & set it definition = QualityManager.getInstance().getParentMachineDefinition(global_container_stack.getBottom()) node.callDecoration("getStack").getTop().setDefinition(definition) setting_container = node.callDecoration("getStack").getTop() for setting in xml_settings: setting_key = setting.get("key") setting_value = setting.text # Extruder_nr is a special case. if setting_key == "extruder_nr": extruder_stack = ExtruderManager.getInstance().getExtruderStack(int(setting_value)) if extruder_stack: node.callDecoration("setActiveExtruder", extruder_stack.getId()) else: Logger.log("w", "Unable to find extruder in position %s", setting_value) continue setting_container.setProperty(setting_key,"value", setting_value) if len(node.getChildren()) > 0: group_decorator = GroupDecorator() node.addDecorator(group_decorator) triangles = object.findall(".//3mf:triangle", self._namespaces) mesh_builder.reserveFaceCount(len(triangles)) for triangle in triangles: v1 = int(triangle.get("v1")) v2 = int(triangle.get("v2")) v3 = int(triangle.get("v3")) mesh_builder.addFaceByPoints(vertex_list[v1][0], vertex_list[v1][1], vertex_list[v1][2], vertex_list[v2][0], vertex_list[v2][1], vertex_list[v2][2], vertex_list[v3][0], vertex_list[v3][1], vertex_list[v3][2]) Job.yieldThread() # TODO: We currently do not check for normals and simply recalculate them. mesh_builder.calculateNormals() mesh_builder.setFileName(name) mesh_data = mesh_builder.build() if len(mesh_data.getVertices()): node.setMeshData(mesh_data) node.setSelectable(True) return node
def buildMesh(self): mb = MeshBuilder() #SOLIDMESH -> LINES if self.YAxis in self._enabled_axis: mb.addCube( width = self._line_width, height = self._line_length, depth = self._line_width, center = Vector(0, self._handle_position/2, 0), color = self._y_axis_color ) if self.XAxis in self._enabled_axis: mb.addCube( width = self._line_length, height = self._line_width, depth = self._line_width, center = Vector(self._handle_position/2, 0, 0), color = self._x_axis_color ) if self.ZAxis in self._enabled_axis: mb.addCube( width = self._line_width, height = self._line_width, depth = self._line_length, center = Vector(0, 0, self._handle_position/2), color = self._z_axis_color ) #SOLIDMESH -> HANDLES if self.YAxis in self._enabled_axis: mb.addPyramid( width = self._handle_width, height = self._handle_height, depth = self._handle_width, center = Vector(0, self._handle_position, 0), color = self._y_axis_color ) if self.XAxis in self._enabled_axis: mb.addPyramid( width = self._handle_width, height = self._handle_height, depth = self._handle_width, center = Vector(self._handle_position, 0, 0), color = self._x_axis_color, axis = Vector.Unit_Z, angle = 90 ) if self.ZAxis in self._enabled_axis: mb.addPyramid( width = self._handle_width, height = self._handle_height, depth = self._handle_width, center = Vector(0, 0, self._handle_position), color = self._z_axis_color, axis = Vector.Unit_X, angle = -90 ) self.setSolidMesh(mb.build()) mb = MeshBuilder() #SELECTIONMESH -> LINES if self.YAxis in self._enabled_axis: mb.addCube( width = self._active_line_width, height = self._active_line_length, depth = self._active_line_width, center = Vector(0, self._active_handle_position/2, 0), color = self._y_axis_color ) if self.XAxis in self._enabled_axis: mb.addCube( width = self._active_line_length, height = self._active_line_width, depth = self._active_line_width, center = Vector(self._active_handle_position/2, 0, 0), color = self._x_axis_color ) if self.ZAxis in self._enabled_axis: mb.addCube( width = self._active_line_width, height = self._active_line_width, depth = self._active_line_length, center = Vector(0, 0, self._active_handle_position/2), color = self._z_axis_color ) #SELECTIONMESH -> HANDLES mb.addCube( width = self._active_handle_width, height = self._active_handle_width, depth = self._active_handle_width, center = Vector(0, 0, 0), color = ToolHandle.AllAxisSelectionColor ) mb.addCube( width = self._active_handle_width, height = self._active_handle_width, depth = self._active_handle_width, center = Vector(0, self._active_handle_position, 0), color = ToolHandle.YAxisSelectionColor ) mb.addCube( width = self._active_handle_width, height = self._active_handle_width, depth = self._active_handle_width, center = Vector(self._active_handle_position, 0, 0), color = ToolHandle.XAxisSelectionColor ) mb.addCube( width = self._active_handle_width, height = self._active_handle_width, depth = self._active_handle_width, center = Vector(0, 0, self._active_handle_position), color = ToolHandle.ZAxisSelectionColor ) self.setSelectionMesh(mb.build())
def buildMesh(self): #SOLIDMESH mb = MeshBuilder() mb.addDonut( inner_radius = self._inner_radius, outer_radius = self._outer_radius, width = self._line_width, color = self._z_axis_color ) mb.addDonut( inner_radius = self._inner_radius, outer_radius = self._outer_radius, width = self._line_width, axis = Vector.Unit_X, angle = math.pi / 2, color = self._y_axis_color ) mb.addDonut( inner_radius = self._inner_radius, outer_radius = self._outer_radius, width = self._line_width, axis = Vector.Unit_Y, angle = math.pi / 2, color = self._x_axis_color ) self.setSolidMesh(mb.build()) #SELECTIONMESH mb = MeshBuilder() mb.addDonut( inner_radius = self._active_inner_radius, outer_radius = self._active_outer_radius, width = self._active_line_width, color = ToolHandle.ZAxisSelectionColor ) mb.addDonut( inner_radius = self._active_inner_radius, outer_radius = self._active_outer_radius, width = self._active_line_width, axis = Vector.Unit_X, angle = math.pi / 2, color = ToolHandle.YAxisSelectionColor ) mb.addDonut( inner_radius = self._active_inner_radius, outer_radius = self._active_outer_radius, width = self._active_line_width, axis = Vector.Unit_Y, angle = math.pi / 2, color = ToolHandle.XAxisSelectionColor ) self.setSelectionMesh(mb.build())
def _convertSavitarNodeToUMNode(self, savitar_node): self._object_count += 1 node_name = "Object %s" % self._object_count active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate um_node = CuraSceneNode() # This adds a SettingOverrideDecorator um_node.addDecorator(BuildPlateDecorator(active_build_plate)) um_node.setName(node_name) transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation()) um_node.setTransformation(transformation) mesh_builder = MeshBuilder() data = numpy.fromstring(savitar_node.getMeshData().getFlatVerticesAsBytes(), dtype=numpy.float32) vertices = numpy.resize(data, (int(data.size / 3), 3)) mesh_builder.setVertices(vertices) mesh_builder.calculateNormals(fast=True) mesh_data = mesh_builder.build() if len(mesh_data.getVertices()): um_node.setMeshData(mesh_data) for child in savitar_node.getChildren(): child_node = self._convertSavitarNodeToUMNode(child) if child_node: um_node.addChild(child_node) if um_node.getMeshData() is None and len(um_node.getChildren()) == 0: return None settings = savitar_node.getSettings() # Add the setting override decorator, so we can add settings to this node. if settings: global_container_stack = Application.getInstance().getGlobalContainerStack() # Ensure the correct next container for the SettingOverride decorator is set. if global_container_stack: default_stack = ExtruderManager.getInstance().getExtruderStack(0) if default_stack: um_node.callDecoration("setActiveExtruder", default_stack.getId()) # Get the definition & set it definition_id = getMachineDefinitionIDForQualitySearch(global_container_stack.definition) um_node.callDecoration("getStack").getTop().setDefinition(definition_id) setting_container = um_node.callDecoration("getStack").getTop() for key in settings: setting_value = settings[key] # Extruder_nr is a special case. if key == "extruder_nr": extruder_stack = ExtruderManager.getInstance().getExtruderStack(int(setting_value)) if extruder_stack: um_node.callDecoration("setActiveExtruder", extruder_stack.getId()) else: Logger.log("w", "Unable to find extruder in position %s", setting_value) continue setting_container.setProperty(key, "value", setting_value) if len(um_node.getChildren()) > 0 and um_node.getMeshData() is None: group_decorator = GroupDecorator() um_node.addDecorator(group_decorator) um_node.setSelectable(True) if um_node.getMeshData(): # Assuming that all nodes with mesh data are printable objects # affects (auto) slicing sliceable_decorator = SliceableObjectDecorator() um_node.addDecorator(sliceable_decorator) return um_node
def calculateBoundingBoxMesh(self): aabb = self.getBoundingBox() if aabb: bounding_box_mesh = MeshBuilder() rtf = aabb.maximum lbb = aabb.minimum bounding_box_mesh.addVertex(rtf.x, rtf.y, rtf.z) # Right - Top - Front bounding_box_mesh.addVertex(lbb.x, rtf.y, rtf.z) # Left - Top - Front bounding_box_mesh.addVertex(lbb.x, rtf.y, rtf.z) # Left - Top - Front bounding_box_mesh.addVertex(lbb.x, lbb.y, rtf.z) # Left - Bottom - Front bounding_box_mesh.addVertex(lbb.x, lbb.y, rtf.z) # Left - Bottom - Front bounding_box_mesh.addVertex(rtf.x, lbb.y, rtf.z) # Right - Bottom - Front bounding_box_mesh.addVertex(rtf.x, lbb.y, rtf.z) # Right - Bottom - Front bounding_box_mesh.addVertex(rtf.x, rtf.y, rtf.z) # Right - Top - Front bounding_box_mesh.addVertex(rtf.x, rtf.y, lbb.z) # Right - Top - Back bounding_box_mesh.addVertex(lbb.x, rtf.y, lbb.z) # Left - Top - Back bounding_box_mesh.addVertex(lbb.x, rtf.y, lbb.z) # Left - Top - Back bounding_box_mesh.addVertex(lbb.x, lbb.y, lbb.z) # Left - Bottom - Back bounding_box_mesh.addVertex(lbb.x, lbb.y, lbb.z) # Left - Bottom - Back bounding_box_mesh.addVertex(rtf.x, lbb.y, lbb.z) # Right - Bottom - Back bounding_box_mesh.addVertex(rtf.x, lbb.y, lbb.z) # Right - Bottom - Back bounding_box_mesh.addVertex(rtf.x, rtf.y, lbb.z) # Right - Top - Back bounding_box_mesh.addVertex(rtf.x, rtf.y, rtf.z) # Right - Top - Front bounding_box_mesh.addVertex(rtf.x, rtf.y, lbb.z) # Right - Top - Back bounding_box_mesh.addVertex(lbb.x, rtf.y, rtf.z) # Left - Top - Front bounding_box_mesh.addVertex(lbb.x, rtf.y, lbb.z) # Left - Top - Back bounding_box_mesh.addVertex(lbb.x, lbb.y, rtf.z) # Left - Bottom - Front bounding_box_mesh.addVertex(lbb.x, lbb.y, lbb.z) # Left - Bottom - Back bounding_box_mesh.addVertex(rtf.x, lbb.y, rtf.z) # Right - Bottom - Front bounding_box_mesh.addVertex(rtf.x, lbb.y, lbb.z) # Right - Bottom - Back self._bounding_box_mesh = bounding_box_mesh.build()