def getAllCameras(self) -> List[Camera]: cameras = [] for node in BreadthFirstIterator(self._root): if isinstance(node, Camera): cameras.append(node) return cameras
def _onChangeTimerFinished(self): if not self._enabled: return root = self._controller.getScene().getRoot() # Keep a list of nodes that are moving. We use this so that we don't move two intersecting objects in the # same direction. transformed_nodes = [] # We try to shuffle all the nodes to prevent "locked" situations, where iteration B inverts iteration A. # By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve. nodes = list(BreadthFirstIterator(root)) # Only check nodes inside build area. nodes = [ node for node in nodes if (hasattr(node, "_outside_buildarea") and not node._outside_buildarea) ] random.shuffle(nodes) for node in nodes: if node is root or not issubclass( type(node), SceneNode) or node.getBoundingBox() is None: continue bbox = node.getBoundingBox() # Move it downwards if bottom is above platform move_vector = Vector() if Preferences.getInstance().getValue( "physics/automatic_drop_down") and not ( node.getParent() and node.getParent().callDecoration( "isGroup")) and node.isEnabled( ): #If an object is grouped, don't move it down z_offset = node.callDecoration( "getZOffset") if node.getDecorator( ZOffsetDecorator.ZOffsetDecorator) else 0 move_vector = move_vector.set(y=-bbox.bottom + z_offset) # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) # only push away objects if this node is a printing mesh if not node.callDecoration( "isNonPrintingMesh") and Preferences.getInstance( ).getValue("physics/automatic_push_free"): # Check for collisions between convex hulls for other_node in BreadthFirstIterator(root): # Ignore root, ourselves and anything that is not a normal SceneNode. if other_node is root or not issubclass( type(other_node), SceneNode ) or other_node is node or other_node.callDecoration( "getBuildPlateNumber") != node.callDecoration( "getBuildPlateNumber"): continue # Ignore collisions of a group with it's own children if other_node in node.getAllChildren( ) or node in other_node.getAllChildren(): continue # Ignore collisions within a group if other_node.getParent() and node.getParent() and ( other_node.getParent().callDecoration("isGroup") is not None or node.getParent().callDecoration("isGroup") is not None): continue # Ignore nodes that do not have the right properties set. if not other_node.callDecoration( "getConvexHull") or not other_node.getBoundingBox( ): continue if other_node in transformed_nodes: continue # Other node is already moving, wait for next pass. if other_node.callDecoration("isNonPrintingMesh"): continue overlap = (0, 0) # Start loop with no overlap current_overlap_checks = 0 # Continue to check the overlap until we no longer find one. while overlap and current_overlap_checks < self._max_overlap_checks: current_overlap_checks += 1 head_hull = node.callDecoration("getConvexHullHead") if head_hull: # One at a time intersection. overlap = head_hull.translate( move_vector.x, move_vector.z).intersectsPolygon( other_node.callDecoration("getConvexHull")) if not overlap: other_head_hull = other_node.callDecoration( "getConvexHullHead") if other_head_hull: overlap = node.callDecoration( "getConvexHull").translate( move_vector.x, move_vector.z).intersectsPolygon( other_head_hull) if overlap: # Moving ensured that overlap was still there. Try anew! move_vector = move_vector.set( x=move_vector.x + overlap[0] * self._move_factor, z=move_vector.z + overlap[1] * self._move_factor) else: # Moving ensured that overlap was still there. Try anew! move_vector = move_vector.set( x=move_vector.x + overlap[0] * self._move_factor, z=move_vector.z + overlap[1] * self._move_factor) else: own_convex_hull = node.callDecoration( "getConvexHull") other_convex_hull = other_node.callDecoration( "getConvexHull") if own_convex_hull and other_convex_hull: overlap = own_convex_hull.translate( move_vector.x, move_vector.z).intersectsPolygon( other_convex_hull) if overlap: # Moving ensured that overlap was still there. Try anew! temp_move_vector = move_vector.set( x=move_vector.x + overlap[0] * self._move_factor, z=move_vector.z + overlap[1] * self._move_factor) # if the distance between two models less than 2mm then try to find a new factor if abs(temp_move_vector.x - overlap[0] ) < self._minimum_gap and abs( temp_move_vector.y - overlap[1]) < self._minimum_gap: temp_x_factor = ( abs(overlap[0]) + self._minimum_gap ) / overlap[0] if overlap[ 0] != 0 else 0 # find x move_factor, like (3.4 + 2) / 3.4 = 1.58 temp_y_factor = ( abs(overlap[1]) + self._minimum_gap ) / overlap[1] if overlap[ 1] != 0 else 0 # find y move_factor temp_scale_factor = temp_x_factor if abs( temp_x_factor) > abs( temp_y_factor ) else temp_y_factor move_vector = move_vector.set( x=move_vector.x + overlap[0] * temp_scale_factor, z=move_vector.z + overlap[1] * temp_scale_factor) else: move_vector = temp_move_vector else: # This can happen in some cases if the object is not yet done with being loaded. # Simply waiting for the next tick seems to resolve this correctly. overlap = None if not Vector.Null.equals(move_vector, epsilon=1e-5): transformed_nodes.append(node) op = PlatformPhysicsOperation.PlatformPhysicsOperation( node, move_vector) op.push() # After moving, we have to evaluate the boundary checks for nodes build_volume = Application.getInstance().getBuildVolume() build_volume.updateNodeBoundaryCheck()
def _meshNodes(nodes): for root in nodes: yield from filter( lambda child: type(child) is SceneNode and child.getMeshData(), BreadthFirstIterator(root))
def _onChangeTimerFinished(self): if not self._enabled: return root = self._controller.getScene().getRoot() for node in BreadthFirstIterator(root): if node is root or type(node) is not SceneNode or node.getBoundingBox() is None: continue bbox = node.getBoundingBox() # Ignore intersections with the bottom build_volume_bounding_box = self._build_volume.getBoundingBox().set(bottom=-9001) node._outside_buildarea = False # Mark the node as outside the build volume if the bounding box test fails. if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection: node._outside_buildarea = True # Move it downwards if bottom is above platform move_vector = Vector() if not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0 if bbox.bottom > 0: move_vector = move_vector.set(y=-bbox.bottom + z_offset) elif bbox.bottom < z_offset: move_vector = move_vector.set(y=(-bbox.bottom) - z_offset) #if not Float.fuzzyCompare(bbox.bottom, 0.0): # pass#move_vector.setY(-bbox.bottom) # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) node.callDecoration("recomputeConvexHull") if Preferences.getInstance().getValue("physics/automatic_push_free"): # Check for collisions between convex hulls for other_node in BreadthFirstIterator(root): # Ignore root, ourselves and anything that is not a normal SceneNode. if other_node is root or type(other_node) is not SceneNode or other_node is node: continue # Ignore colissions of a group with it's own children if other_node in node.getAllChildren() or node in other_node.getAllChildren(): continue # Ignore colissions within a group if other_node.getParent().callDecoration("isGroup") is not None or node.getParent().callDecoration("isGroup") is not None: continue #if node.getParent().callDecoration("isGroup") is other_node.getParent().callDecoration("isGroup"): # continue # Ignore nodes that do not have the right properties set. if not other_node.callDecoration("getConvexHull") or not other_node.getBoundingBox(): continue # Check to see if the bounding boxes intersect. If not, we can ignore the node as there is no way the hull intersects. #if node.getBoundingBox().intersectsBox(other_node.getBoundingBox()) == AxisAlignedBox.IntersectionResult.NoIntersection: # continue # Get the overlap distance for both convex hulls. If this returns None, there is no intersection. try: head_hull = node.callDecoration("getConvexHullHead") if head_hull: overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHull")) if not overlap: other_head_hull = other_node.callDecoration("getConvexHullHead") if other_head_hull: overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_head_hull) else: overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_node.callDecoration("getConvexHull")) except: overlap = None #It can sometimes occur that the calculated convex hull has no size, in which case there is no overlap. if overlap is None: continue move_vector = move_vector.set(x=overlap[0] * 1.1, z=overlap[1] * 1.1) convex_hull = node.callDecoration("getConvexHull") if convex_hull: if not convex_hull.isValid(): return # Check for collisions between disallowed areas and the object for area in self._build_volume.getDisallowedAreas(): overlap = convex_hull.intersectsPolygon(area) if overlap is None: continue node._outside_buildarea = True if not Vector.Null.equals(move_vector, epsilon=1e-5): op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector) op.push()
def requestWrite(self, node, file_name=None, filter_by_machine=False): filter_by_machine = True # This plugin is indended to be used by machine (regardless of what it was told to do) if self._writing: raise OutputDeviceError.DeviceBusyError() file_formats = Application.getInstance().getMeshFileHandler( ).getSupportedFileTypesWrite() #Formats supported by this application. if filter_by_machine: machine_file_formats = Application.getInstance().getMachineManager( ).getActiveMachineInstance().getMachineDefinition().getFileFormats( ) file_formats = list( filter( lambda file_format: file_format["mime_type"] in machine_file_formats, file_formats) ) #Take the intersection between file_formats and machine_file_formats. if len(file_formats) == 0: Logger.log("e", "There are no file formats available to write with!") raise OutputDeviceError.WriteRequestFailedError() writer = Application.getInstance().getMeshFileHandler( ).getWriterByMimeType( file_formats[0] ["mime_type"]) #Just take the first file format available. extension = file_formats[0]["extension"] if file_name == None: for n in BreadthFirstIterator(node): if n.getMeshData(): file_name = n.getName() if file_name: break if not file_name: Logger.log( "e", "Could not determine a proper file name when trying to write to %s, aborting", self.getName()) raise OutputDeviceError.WriteRequestFailedError() if extension: #Not empty string. extension = "." + extension file_name = os.path.join(self.getId(), os.path.splitext(file_name)[0] + extension) try: Logger.log("d", "Writing to %s", file_name) stream = open(file_name, "wt") job = WriteMeshJob(writer, stream, node, MeshWriter.OutputMode.TextMode) job.setFileName(file_name) job.progress.connect(self._onProgress) job.finished.connect(self._onFinished) message = Message( catalog.i18nc( "@info:progress", "Saving to Removable Drive <filename>{0}</filename>"). format(self.getName()), 0, False, -1) message.show() self.writeStarted.emit(self) job._message = message self._writing = True job.start() except PermissionError as e: Logger.log("e", "Permission denied when trying to write to %s: %s", file_name, str(e)) raise OutputDeviceError.PermissionDeniedError() from e except OSError as e: Logger.log("e", "Operating system would not let us write to %s: %s", file_name, str(e)) raise OutputDeviceError.WriteRequestFailedError() from e
def findCamera(self, name: str) -> Optional[Camera]: for node in BreadthFirstIterator(self._root): if isinstance(node, Camera) and node.getName() == name: return node return None
def _onChangeTimerFinished(self): if not self._enabled: return root = self._controller.getScene().getRoot() for node in BreadthFirstIterator(root): if node is root or type(node) is not SceneNode: continue bbox = node.getBoundingBox() if not bbox or not bbox.isValid(): self._change_timer.start() continue build_volume_bounding_box = copy.deepcopy( self._build_volume.getBoundingBox()) build_volume_bounding_box.setBottom( -9001) # Ignore intersections with the bottom # Mark the node as outside the build volume if the bounding box test fails. if build_volume_bounding_box.intersectsBox( bbox ) != AxisAlignedBox.IntersectionResult.FullIntersection: node._outside_buildarea = True else: node._outside_buildarea = False # Move it downwards if bottom is above platform move_vector = Vector() if bbox.bottom > 0: move_vector.setY(-bbox.bottom) #if not Float.fuzzyCompare(bbox.bottom, 0.0): # pass#move_vector.setY(-bbox.bottom) # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) if not node.callDecoration("getConvexHull"): if not node.callDecoration("getConvexHullJob"): job = ConvexHullJob.ConvexHullJob(node) job.start() node.callDecoration("setConvexHullJob", job) elif Selection.isSelected(node): pass elif Preferences.getInstance().getValue( "physics/automatic_push_free"): # Check for collisions between convex hulls for other_node in BreadthFirstIterator(root): # Ignore root, ourselves and anything that is not a normal SceneNode. if other_node is root or type( other_node) is not SceneNode or other_node is node: continue # Ignore colissions of a group with it's own children if other_node in node.getAllChildren( ) or node in other_node.getAllChildren(): continue # Ignore colissions within a group if other_node.getParent().callDecoration( "isGroup") is not None: if node.getParent().callDecoration( "isGroup") is other_node.getParent( ).callDecoration("isGroup"): continue # Ignore nodes that do not have the right properties set. if not other_node.callDecoration( "getConvexHull") or not other_node.getBoundingBox( ): continue # Check to see if the bounding boxes intersect. If not, we can ignore the node as there is no way the hull intersects. #if node.getBoundingBox().intersectsBox(other_node.getBoundingBox()) == AxisAlignedBox.IntersectionResult.NoIntersection: # continue # Get the overlap distance for both convex hulls. If this returns None, there is no intersection. try: overlap = node.callDecoration( "getConvexHull").intersectsPolygon( other_node.callDecoration("getConvexHull")) except: overlap = None #It can sometimes occur that the caclulated convex hull has no size, in which case there is no overlap. if overlap is None: continue move_vector.setX(overlap[0] * 1.1) move_vector.setZ(overlap[1] * 1.1) convex_hull = node.callDecoration("getConvexHull") if convex_hull: if not convex_hull.isValid(): return # Check for collisions between disallowed areas and the object for area in self._build_volume.getDisallowedAreas(): overlap = convex_hull.intersectsPolygon(area) if overlap is None: continue node._outside_buildarea = True if move_vector != Vector(): op = PlatformPhysicsOperation.PlatformPhysicsOperation( node, move_vector) op.push()
def findObject(self, object_id: int) -> Optional["SceneNode"]: for node in BreadthFirstIterator(self._root): # type: ignore if id(node) == object_id: return node return None
def _constructSupport(self, buffer: QImage) -> None: depth_pass = PickingPass( buffer.width(), buffer.height() ) #Instead of using the picking pass to pick for us, we need to bulk-pick digits so do this in Numpy. depth_pass.render() depth_image = depth_pass.getOutput() camera = CuraApplication.getInstance().getController().getScene( ).getActiveCamera() #to_support = qimage2ndarray.raw_view(buffer) #to_support= _qimageview(_qt.QImage(buffer)) to_support = self._raw_view(buffer) #depth = qimage2ndarray.recarray_view(depth_image) depth = self._recarray_view(depth_image) depth.a = 0 #Discard alpha channel. depth = depth.view(dtype=_np.int32).astype( _np.float32 ) / 1000 #Conflate the R, G and B channels to one 24-bit (cast to 32) float. Divide by 1000 to get mm. support_positions_2d = _np.array( _np.where(_np.bitwise_and(to_support == 255, depth < 16777)) ) #All the 2D coordinates on the screen where we want support. The 16777 is for points that don't land on a model. support_depths = _np.take( depth, support_positions_2d[0, :] * depth.shape[1] + support_positions_2d[1, :]) #The depth at those pixels. support_positions_2d = support_positions_2d.transpose( ) #We want rows with pixels, not columns with pixels. if len(support_positions_2d) == 0: Logger.log( "i", "Support was not drawn on the surface of any objects. Not creating support." ) return support_positions_2d[:, [0, 1]] = support_positions_2d[:, [ 1, 0 ]] #Swap columns to get OpenGL's coordinate system. camera_viewport = _np.array( [camera.getViewportWidth(), camera.getViewportHeight()]) support_positions_2d = support_positions_2d * 2.0 / camera_viewport - 1.0 #Scale to view coordinates (range -1 to 1). inverted_projection = _np.linalg.inv( camera.getProjectionMatrix().getData()) transformation = camera.getWorldTransformation().getData() transformation[:, 1] = -transformation[:, 1] #Invert Z to get OpenGL's coordinate system. #For each pixel, get the near and far plane. near = _np.ndarray((support_positions_2d.shape[0], 4)) near.fill(1) near[0:support_positions_2d.shape[0], 0:support_positions_2d.shape[1]] = support_positions_2d near[:, 2].fill(-1) near = _np.dot(inverted_projection, near.transpose()) near = _np.dot(transformation, near) near = near[0:3] / near[3] far = _np.ndarray((support_positions_2d.shape[0], 4)) far.fill(1) far[0:support_positions_2d.shape[0], 0:support_positions_2d.shape[1]] = support_positions_2d far = _np.dot(inverted_projection, far.transpose()) far = _np.dot(transformation, far) far = far[0:3] / far[3] #Direction is from near plane pixel to far plane pixel, normalised. direction = near - far direction /= _np.linalg.norm(direction, axis=0) #Final position is in the direction of the pixel, moving with <depth> mm away from the camera position. support_positions_3d = ( support_depths - 1 ) * direction #We want the support to appear just before the surface, not behind the surface, so - 1. support_positions_3d = support_positions_3d.transpose() camera_position_data = camera.getPosition().getData() support_positions_3d = support_positions_3d + camera_position_data #Create the vertices for the 3D mesh. #This mesh consists of a diamond-shape for each position that we traced. n = support_positions_3d.shape[0] Logger.log( "i", "Adding support in {num_pixels} locations.".format(num_pixels=n)) vertices = support_positions_3d.copy().astype(_np.float32) vertices = _np.resize(vertices, (n * 6, support_positions_3d.shape[1] )) #Resize will repeat all coordinates 6 times. #For each position, create a diamond shape around the position with 6 vertices. vertices[ n * 0:n * 1, 0] -= support_depths * 0.001 * self.globule_size #First corner (-x, +y). vertices[n * 0:n * 1, 2] += support_depths * 0.001 * self.globule_size vertices[ n * 1:n * 2, 0] += support_depths * 0.001 * self.globule_size #Second corner (+x, +y). vertices[n * 1:n * 2, 2] += support_depths * 0.001 * self.globule_size vertices[ n * 2:n * 3, 0] -= support_depths * 0.001 * self.globule_size #Third corner (-x, -y). vertices[n * 2:n * 3, 2] -= support_depths * 0.001 * self.globule_size vertices[ n * 3:n * 4, 0] += support_depths * 0.001 * self.globule_size #Fourth corner (+x, -y) vertices[n * 3:n * 4, 2] -= support_depths * 0.001 * self.globule_size vertices[n * 4:n * 5, 1] += support_depths * 0.001 * self.globule_size #Top side. vertices[ n * 5:n * 6, 1] -= support_depths * 0.001 * self.globule_size #Bottom side. #Create the faces of the diamond. indices = _np.arange(n, dtype=_np.int32) indices = _np.kron(indices, _np.ones( (3, 1))).astype(_np.int32).transpose() indices = _np.resize( indices, (n * 8, 3) ) #Creates 8 triangles using 3 times the same vertex, for each position: [[0, 0, 0], [1, 1, 1], ... , [0, 0, 0], [1, 1, 1], ... ] #indices[n * 0: n * 1, 0] += n * 0 #First corner. indices[n * 0:n * 1, 1] += n * 1 #Second corner. indices[n * 0:n * 1, 2] += n * 4 #Top side. indices[n * 1:n * 2, 0] += n * 1 #Second corner. indices[n * 1:n * 2, 1] += n * 3 #Fourth corner. indices[n * 1:n * 2, 2] += n * 4 #Top side. indices[n * 2:n * 3, 0] += n * 3 #Fourth corner. indices[n * 2:n * 3, 1] += n * 2 #Third corner. indices[n * 2:n * 3, 2] += n * 4 #Top side. indices[n * 3:n * 4, 0] += n * 2 #Third corner. #indices[n * 3: n * 4, 1] += n * 0 #First corner. indices[n * 3:n * 4, 2] += n * 4 #Top side. indices[n * 4:n * 5, 0] += n * 1 #Second corner. #indices[n * 4: n * 5, 1] += n * 0 #First corner. indices[n * 4:n * 5, 2] += n * 5 #Bottom side. indices[n * 5:n * 6, 0] += n * 3 #Fourth corner. indices[n * 5:n * 6, 1] += n * 1 #Second corner. indices[n * 5:n * 6, 2] += n * 5 #Bottom side. indices[n * 6:n * 7, 0] += n * 2 #Third corner. indices[n * 6:n * 7, 1] += n * 3 #Fourth corner. indices[n * 6:n * 7, 2] += n * 5 #Bottom side. #indices[n * 7: n * 8, 0] += n * 0 #First corner. indices[n * 7:n * 8, 1] += n * 2 #Third corner. indices[n * 7:n * 8, 2] += n * 5 #Bottom side. builder = MeshBuilder() builder.addVertices(vertices) builder.addIndices(indices) #Create the scene node. scene = CuraApplication.getInstance().getController().getScene() new_node = CuraSceneNode(parent=scene.getRoot(), name="BrushSupport") new_node.setSelectable(False) new_node.setMeshData(builder.build()) new_node.addDecorator( BuildPlateDecorator(CuraApplication.getInstance(). getMultiBuildPlateModel().activeBuildPlate)) new_node.addDecorator(SliceableObjectDecorator()) operation = GroupedOperation() #Figure out which mesh this piece of support belongs to. #TODO: You can draw support in one stroke over multiple meshes. The support would belong to an arbitrary one of these. selection_pass = CuraApplication.getInstance().getRenderer( ).getRenderPass("selection") parent_id = selection_pass.getIdAtPosition( support_positions_2d[0][0], support_positions_2d[0] [1]) #Find the selection under the first support pixel. parent_node = scene.getRoot() if not parent_id: Logger.log("d", "Can't link custom support to any scene node.") else: for node in BreadthFirstIterator(scene.getRoot()): if id(node) == parent_id: parent_node = node break #Add the appropriate per-object settings. stack = new_node.callDecoration( "getStack" ) #Created by SettingOverrideDecorator that is automatically added to CuraSceneNode. settings = stack.getTop() support_mesh_instance = SettingInstance( stack.getSettingDefinition("support_mesh"), settings) support_mesh_instance.setProperty("value", True) support_mesh_instance.resetState() settings.addInstance(support_mesh_instance) drop_down_instance = SettingInstance( stack.getSettingDefinition("support_mesh_drop_down"), settings) drop_down_instance.setProperty("value", True) drop_down_instance.resetState() settings.addInstance(drop_down_instance) #Add the scene node to the scene (and allow for undo). operation.addOperation( AddSceneNodeOperation(new_node, scene.getRoot()) ) #Set the parent to root initially, then change the parent, so that we don't have to alter the transformation. operation.addOperation(SetParentOperation(new_node, parent_node)) operation.push() scene.sceneChanged.emit(new_node)
def findObject(self, object_id): for node in BreadthFirstIterator(self._root): if id(node) == object_id: return node return None
def _findCamera(self, name): for node in BreadthFirstIterator(self._root): if isinstance(node, Camera) and node.getName() == name: return node
def _findCamera(self, name): for node in BreadthFirstIterator(self._root): if type(node) is Camera and node.getName() == name: return node
def write(self, stream, node, mode=MeshWriter.OutputMode.BinaryMode): nodes = [] for n in BreadthFirstIterator(node): if type(n) is not SceneNode or not n.getMeshData(): continue nodes.append(n) if not len(nodes): return False archive = zipfile.ZipFile(stream, "w", compression=zipfile.ZIP_DEFLATED) try: model_file = zipfile.ZipInfo("3D/3dmodel.model") content_types_file = zipfile.ZipInfo("[Content_Types].xml") model = ET.Element('model', unit="millimeter", xmlns=self._namespaces["3mf"]) resources = ET.SubElement(model, "resources") build = ET.SubElement(model, "build") for index, n in enumerate(nodes): object = ET.SubElement(resources, "object", id=str(index + 1), type="model") mesh = ET.SubElement(object, "mesh") mesh_data = n.getMeshData() vertices = ET.SubElement(mesh, "vertices") verts = mesh_data.getVertices() if mesh_data.hasIndices(): for face in mesh_data.getIndices(): v1 = verts[face[0]] v2 = verts[face[1]] v3 = verts[face[2]] xml_vertex1 = ET.SubElement(vertices, "vertex", x=str(v1[0]), y=str(v1[2]), z=str(v1[1])) xml_vertex2 = ET.SubElement(vertices, "vertex", x=str(v2[0]), y=str(v2[2]), z=str(v2[1])) xml_vertex3 = ET.SubElement(vertices, "vertex", x=str(v3[0]), y=str(v3[2]), z=str(v3[1])) triangles = ET.SubElement(mesh, "triangles") for face in mesh_data.getIndices(): triangle = ET.SubElement(triangles, "triangle", v1=str(face[0]), v2=str(face[1]), v3=str(face[2])) else: for vert in verts: xml_vertex = ET.SubElement(vertices, "vertex", x=str(vert[0]), y=str(vert[0]), z=str(vert[0])) transformation_string = self._convertMatrixToString( node.getWorldTransformation()) if transformation_string != self._convertMatrixToString( Matrix()): item = ET.SubElement(build, "item", objectid=str(index + 1), transform=transformation_string) else: item = ET.SubElement( build, "item", objectid=str(index + 1)) #, transform = transformation_string) archive.writestr( model_file, b'<?xml version="1.0" encoding="UTF-8"?> \n' + ET.tostring(model)) archive.writestr(content_types_file, "") except Exception as e: print("Error writing zip file", e) return False finally: archive.close() return True
def _onChangeTimerFinished(self): if not self._enabled: return root = self._controller.getScene().getRoot() # Keep a list of nodes that are moving. We use this so that we don't move two intersecting objects in the # same direction. transformed_nodes = [] group_nodes = [] for node in BreadthFirstIterator(root): if node is root or type( node) is not SceneNode or node.getBoundingBox() is None: continue bbox = node.getBoundingBox() # Ignore intersections with the bottom build_volume_bounding_box = self._build_volume.getBoundingBox() if build_volume_bounding_box: # It's over 9000! build_volume_bounding_box = build_volume_bounding_box.set( bottom=-9001) else: # No bounding box. This is triggered when running Cura from command line with a model for the first time # In that situation there is a model, but no machine (and therefore no build volume. return node._outside_buildarea = False # Mark the node as outside the build volume if the bounding box test fails. if build_volume_bounding_box.intersectsBox( bbox ) != AxisAlignedBox.IntersectionResult.FullIntersection: node._outside_buildarea = True if node.callDecoration("isGroup"): group_nodes.append(node) # Keep list of affected group_nodes # Move it downwards if bottom is above platform move_vector = Vector() if Preferences.getInstance().getValue( "physics/automatic_drop_down") and not ( node.getParent() and node.getParent().callDecoration("isGroup") ): #If an object is grouped, don't move it down z_offset = node.callDecoration( "getZOffset") if node.getDecorator( ZOffsetDecorator.ZOffsetDecorator) else 0 move_vector = move_vector.set(y=-bbox.bottom + z_offset) # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) if Preferences.getInstance().getValue( "physics/automatic_push_free"): # Check for collisions between convex hulls for other_node in BreadthFirstIterator(root): # Ignore root, ourselves and anything that is not a normal SceneNode. if other_node is root or type( other_node) is not SceneNode or other_node is node: continue # Ignore collisions of a group with it's own children if other_node in node.getAllChildren( ) or node in other_node.getAllChildren(): continue # Ignore collisions within a group if other_node.getParent().callDecoration( "isGroup") is not None or node.getParent( ).callDecoration("isGroup") is not None: continue # Ignore nodes that do not have the right properties set. if not other_node.callDecoration( "getConvexHull") or not other_node.getBoundingBox( ): continue if other_node in transformed_nodes: continue # Other node is already moving, wait for next pass. overlap = (0, 0) # Start loop with no overlap current_overlap_checks = 0 # Continue to check the overlap until we no longer find one. while overlap and current_overlap_checks < self._max_overlap_checks: current_overlap_checks += 1 head_hull = node.callDecoration("getConvexHullHead") if head_hull: # One at a time intersection. overlap = head_hull.translate( move_vector.x, move_vector.z).intersectsPolygon( other_node.callDecoration("getConvexHull")) if not overlap: other_head_hull = other_node.callDecoration( "getConvexHullHead") if other_head_hull: overlap = node.callDecoration( "getConvexHull").translate( move_vector.x, move_vector.z).intersectsPolygon( other_head_hull) if overlap: # Moving ensured that overlap was still there. Try anew! move_vector = move_vector.set( x=move_vector.x + overlap[0] * self._move_factor, z=move_vector.z + overlap[1] * self._move_factor) else: # Moving ensured that overlap was still there. Try anew! move_vector = move_vector.set( x=move_vector.x + overlap[0] * self._move_factor, z=move_vector.z + overlap[1] * self._move_factor) else: own_convex_hull = node.callDecoration( "getConvexHull") other_convex_hull = other_node.callDecoration( "getConvexHull") if own_convex_hull and other_convex_hull: overlap = own_convex_hull.translate( move_vector.x, move_vector.z).intersectsPolygon( other_convex_hull) if overlap: # Moving ensured that overlap was still there. Try anew! move_vector = move_vector.set( x=move_vector.x + overlap[0] * self._move_factor, z=move_vector.z + overlap[1] * self._move_factor) else: # This can happen in some cases if the object is not yet done with being loaded. # Simply waiting for the next tick seems to resolve this correctly. overlap = None convex_hull = node.callDecoration("getConvexHull") if convex_hull: if not convex_hull.isValid(): return # Check for collisions between disallowed areas and the object for area in self._build_volume.getDisallowedAreas(): overlap = convex_hull.intersectsPolygon(area) if overlap is None: continue node._outside_buildarea = True if not Vector.Null.equals(move_vector, epsilon=1e-5): transformed_nodes.append(node) op = PlatformPhysicsOperation.PlatformPhysicsOperation( node, move_vector) op.push() # Group nodes should override the _outside_buildarea property of their children. for group_node in group_nodes: for child_node in group_node.getAllChildren(): child_node._outside_buildarea = group_node._outside_buildarea
def _meshNodes(nodes): for root in nodes: yield from filter( lambda child: isinstance(child, SceneNode) and child.isSelectable() and child.getMeshData(), BreadthFirstIterator(root) )
def _pixelSelection(self, event): # Find a node id by looking at a pixel value at the requested location if self._selection_pass: if Selection.getFaceSelectMode(): item_id = id(Selection.getSelectedObject(0)) else: item_id = self._selection_pass.getIdAtPosition( event.x, event.y) else: Logger.log( "w", "Selection pass is None. getRenderPass('selection') returned None" ) return False if not item_id and not self._shift_is_active: if Selection.hasSelection(): Selection.clearFace() Selection.clear() return True return False # Nothing was selected before and the user didn't click on an object. # Find the scene-node which matches the node-id for node in BreadthFirstIterator(self._scene.getRoot()): if id(node) != item_id: continue if self._isNodeInGroup(node): is_selected = Selection.isSelected( self._findTopGroupNode(node)) else: is_selected = Selection.isSelected(node) if self._shift_is_active: if is_selected: # Deselect the SceneNode and its siblings in a group if node.getParent(): if self._ctrl_is_active or not self._isNodeInGroup( node): Selection.remove(node) else: Selection.remove(self._findTopGroupNode(node)) return True else: # Select the SceneNode and its siblings in a group if node.getParent(): if self._ctrl_is_active or not self._isNodeInGroup( node): Selection.add(node) else: Selection.add(self._findTopGroupNode(node)) return True else: if Selection.getFaceSelectMode(): face_id = self._selection_pass.getFaceIdAtPosition( event.x, event.y) if face_id >= 0: Selection.toggleFace(node, face_id) else: Selection.clear() Selection.clearFace() if not is_selected or Selection.getCount() > 1: # Select only the SceneNode and its siblings in a group Selection.clear() if node.getParent(): if self._ctrl_is_active or not self._isNodeInGroup( node): Selection.add(node) else: Selection.add(self._findTopGroupNode(node)) return True elif self._isNodeInGroup(node) and self._ctrl_is_active: Selection.clear() Selection.add(node) return True return False