def run(self): if not self._node: return ## If the scene node is a group, use the hull of the children to calculate its hull. if self._node.callDecoration("isGroup"): hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32)) for child in self._node.getChildren(): child_hull = child.callDecoration("getConvexHull") if child_hull: hull.setPoints(numpy.append(hull.getPoints(), child_hull.getPoints(), axis = 0)) if hull.getPoints().size < 3: self._node.callDecoration("setConvexHull", None) self._node.callDecoration("setConvexHullJob", None) return else: if not self._node.getMeshData(): return mesh = self._node.getMeshData() vertex_data = mesh.getTransformed(self._node.getWorldTransformation()).getVertices() hull = Polygon(numpy.rint(vertex_data[:, [0, 2]]).astype(int)) # First, calculate the normal convex hull around the points hull = hull.getConvexHull() #print("hull: " , self._node.callDecoration("isGroup"), " " , hull.getPoints()) # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull. hull = hull.getMinkowskiHull(Polygon(numpy.array([[-1, -1], [-1, 1], [1, 1], [1, -1]], numpy.float32))) hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot()) self._node.callDecoration("setConvexHullNode", hull_node) self._node.callDecoration("setConvexHull", hull) self._node.callDecoration("setConvexHullJob", None)
def run(self): if not self._node: return ## If the scene node is a group, use the hull of the children to calculate its hull. if self._node.callDecoration("isGroup"): hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32)) for child in self._node.getChildren(): child_hull = child.callDecoration("getConvexHull") if child_hull: hull.setPoints(numpy.append(hull.getPoints(), child_hull.getPoints(), axis = 0)) if hull.getPoints().size < 3: self._node.callDecoration("setConvexHull", None) self._node.callDecoration("setConvexHullJob", None) return Job.yieldThread() else: if not self._node.getMeshData(): return mesh = self._node.getMeshData() vertex_data = mesh.getTransformed(self._node.getWorldTransformation()).getVertices() # Don't use data below 0. TODO; We need a better check for this as this gives poor results for meshes with long edges. vertex_data = vertex_data[vertex_data[:,1]>0] hull = Polygon(numpy.rint(vertex_data[:, [0, 2]]).astype(int)) # First, calculate the normal convex hull around the points hull = hull.getConvexHull() # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull. # This is done because of rounding errors. hull = hull.getMinkowskiHull(Polygon(numpy.array([[-1, -1], [-1, 1], [1, 1], [1, -1]], numpy.float32))) profile = Application.getInstance().getMachineManager().getActiveProfile() if profile: if profile.getSettingValue("print_sequence") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"): # Printing one at a time and it's not an object in a group self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull)) head_hull = hull.getMinkowskiHull(Polygon(numpy.array(profile.getSettingValue("machine_head_with_fans_polygon"),numpy.float32))) self._node.callDecoration("setConvexHullHead", head_hull) hull = hull.getMinkowskiHull(Polygon(numpy.array(profile.getSettingValue("machine_head_polygon"),numpy.float32))) else: self._node.callDecoration("setConvexHullHead", None) hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot()) self._node.callDecoration("setConvexHullNode", hull_node) self._node.callDecoration("setConvexHull", hull) self._node.callDecoration("setConvexHullJob", None) if self._node.getParent().callDecoration("isGroup"): job = self._node.getParent().callDecoration("getConvexHullJob") if job: job.cancel() self._node.getParent().callDecoration("setConvexHull", None) hull_node = self._node.getParent().callDecoration("getConvexHullNode") if hull_node: hull_node.setParent(None)
def run(self): if not self._node or not self._node.getMeshData(): return mesh = self._node.getMeshData() vertexData = mesh.getTransformed(self._node.getWorldTransformation()).getVertices() hull = Polygon(numpy.rint(vertexData[:, [0, 2]]).astype(int)) # First, calculate the normal convex hull around the points hull = hull.getConvexHull() # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull. hull = hull.getMinkowskiHull(Polygon(numpy.array([[-1, -1], [-1, 1], [1, 1], [1, -1]], numpy.float32))) hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot()) self._node._convex_hull = hull delattr(self._node, "_convex_hull_job")
def _compute2DConvexHull(self) -> Optional[Polygon]: if self._node is None: return None if self._node.callDecoration("isGroup"): points = numpy.zeros((0, 2), dtype=numpy.int32) for child in self._node.getChildren(): child_hull = child.callDecoration("_compute2DConvexHull") if child_hull: try: points = numpy.append(points, child_hull.getPoints(), axis=0) except ValueError: pass if points.size < 3: return None child_polygon = Polygon(points) # Check the cache if child_polygon == self._2d_convex_hull_group_child_polygon: return self._2d_convex_hull_group_result convex_hull = child_polygon.getConvexHull( ) #First calculate the normal convex hull around the points. offset_hull = self._offsetHull( convex_hull) #Then apply the offset from the settings. # Store the result in the cache self._2d_convex_hull_group_child_polygon = child_polygon self._2d_convex_hull_group_result = offset_hull return offset_hull else: convex_hull = Polygon([]) offset_hull = Polygon([]) mesh = self._node.getMeshData() if mesh is None: return Polygon( [] ) # Node has no mesh data, so just return an empty Polygon. world_transform = self._node.getWorldTransformation(copy=True) # Check the cache if mesh is self._2d_convex_hull_mesh and world_transform == self._2d_convex_hull_mesh_world_transform: return self._offsetHull(self._2d_convex_hull_mesh_result) vertex_data = mesh.getConvexHullTransformedVertices( world_transform) # Don't use data below 0. # TODO; We need a better check for this as this gives poor results for meshes with long edges. # Do not throw away vertices: the convex hull may be too small and objects can collide. # vertex_data = vertex_data[vertex_data[:,1] >= -0.01] if vertex_data is not None and len( vertex_data ) >= 4: # type: ignore # mypy and numpy don't play along well just yet. # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices # This is done to greatly speed up further convex hull calculations as the convex hull # becomes much less complex when dealing with highly detailed models. vertex_data = numpy.round(vertex_data, 1) vertex_data = vertex_data[:, [ 0, 2 ]] # Drop the Y components to project to 2D. # Grab the set of unique points. # # This basically finds the unique rows in the array by treating them as opaque groups of bytes # which are as long as the 2 float64s in each row, and giving this view to numpy.unique() to munch. # See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array vertex_byte_view = numpy.ascontiguousarray(vertex_data).view( numpy.dtype( (numpy.void, vertex_data.dtype.itemsize * vertex_data.shape[1]))) _, idx = numpy.unique(vertex_byte_view, return_index=True) vertex_data = vertex_data[ idx] # Select the unique rows by index. hull = Polygon(vertex_data) if len(vertex_data) >= 3: convex_hull = hull.getConvexHull() offset_hull = self._offsetHull(convex_hull) # Store the result in the cache self._2d_convex_hull_mesh = mesh self._2d_convex_hull_mesh_world_transform = world_transform self._2d_convex_hull_mesh_result = convex_hull return offset_hull
def _compute2DConvexHull(self): if self._node.callDecoration("isGroup"): points = numpy.zeros((0, 2), dtype=numpy.int32) for child in self._node.getChildren(): child_hull = child.callDecoration("_compute2DConvexHull") if child_hull: points = numpy.append(points, child_hull.getPoints(), axis = 0) if points.size < 3: return None child_polygon = Polygon(points) # Check the cache if child_polygon == self._2d_convex_hull_group_child_polygon: return self._2d_convex_hull_group_result # First, calculate the normal convex hull around the points convex_hull = child_polygon.getConvexHull() # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull. # This is done because of rounding errors. rounded_hull = self._roundHull(convex_hull) # Store the result in the cache self._2d_convex_hull_group_child_polygon = child_polygon self._2d_convex_hull_group_result = rounded_hull return rounded_hull else: rounded_hull = None mesh = None world_transform = None if self._node.getMeshData(): mesh = self._node.getMeshData() world_transform = self._node.getWorldTransformation() # Check the cache if mesh is self._2d_convex_hull_mesh and world_transform == self._2d_convex_hull_mesh_world_transform: return self._2d_convex_hull_mesh_result vertex_data = mesh.getConvexHullTransformedVertices(world_transform) # Don't use data below 0. # TODO; We need a better check for this as this gives poor results for meshes with long edges. # Do not throw away vertices: the convex hull may be too small and objects can collide. # vertex_data = vertex_data[vertex_data[:,1] >= -0.01] if len(vertex_data) >= 4: # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices # This is done to greatly speed up further convex hull calculations as the convex hull # becomes much less complex when dealing with highly detailed models. vertex_data = numpy.round(vertex_data, 1) vertex_data = vertex_data[:, [0, 2]] # Drop the Y components to project to 2D. # Grab the set of unique points. # # This basically finds the unique rows in the array by treating them as opaque groups of bytes # which are as long as the 2 float64s in each row, and giving this view to numpy.unique() to munch. # See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array vertex_byte_view = numpy.ascontiguousarray(vertex_data).view( numpy.dtype((numpy.void, vertex_data.dtype.itemsize * vertex_data.shape[1]))) _, idx = numpy.unique(vertex_byte_view, return_index=True) vertex_data = vertex_data[idx] # Select the unique rows by index. hull = Polygon(vertex_data) if len(vertex_data) >= 4: # First, calculate the normal convex hull around the points convex_hull = hull.getConvexHull() # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull. # This is done because of rounding errors. rounded_hull = convex_hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32))) # Store the result in the cache self._2d_convex_hull_mesh = mesh self._2d_convex_hull_mesh_world_transform = world_transform self._2d_convex_hull_mesh_result = rounded_hull return rounded_hull
def run(self): if not self._node: return ## If the scene node is a group, use the hull of the children to calculate its hull. if self._node.callDecoration("isGroup"): hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32)) for child in self._node.getChildren(): child_hull = child.callDecoration("getConvexHull") if child_hull: hull.setPoints( numpy.append(hull.getPoints(), child_hull.getPoints(), axis=0)) if hull.getPoints().size < 3: self._node.callDecoration("setConvexHull", None) self._node.callDecoration("setConvexHullJob", None) return Job.yieldThread() else: if not self._node.getMeshData(): return mesh = self._node.getMeshData() vertex_data = mesh.getTransformed( self._node.getWorldTransformation()).getVertices() # Don't use data below 0. # TODO; We need a better check for this as this gives poor results for meshes with long edges. vertex_data = vertex_data[vertex_data[:, 1] >= 0] # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices # This is done to greatly speed up further convex hull calculations as the convex hull # becomes much less complex when dealing with highly detailed models. vertex_data = numpy.round(vertex_data, 1) vertex_data = vertex_data[:, [ 0, 2 ]] # Drop the Y components to project to 2D. # Grab the set of unique points. # # This basically finds the unique rows in the array by treating them as opaque groups of bytes # which are as long as the 2 float64s in each row, and giving this view to numpy.unique() to munch. # See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array vertex_byte_view = numpy.ascontiguousarray(vertex_data).view( numpy.dtype( (numpy.void, vertex_data.dtype.itemsize * vertex_data.shape[1]))) _, idx = numpy.unique(vertex_byte_view, return_index=True) vertex_data = vertex_data[idx] # Select the unique rows by index. hull = Polygon(vertex_data) # First, calculate the normal convex hull around the points hull = hull.getConvexHull() # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull. # This is done because of rounding errors. hull = hull.getMinkowskiHull( Polygon( numpy.array( [[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32))) global_stack = Application.getInstance().getGlobalContainerStack() if global_stack: if global_stack.getProperty( "print_sequence", "value") == "one_at_a_time" and not self._node.getParent( ).callDecoration("isGroup"): # Printing one at a time and it's not an object in a group self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull)) head_and_fans = Polygon( numpy.array( global_stack.getProperty( "machine_head_with_fans_polygon", "value"), numpy.float32)) # Full head hull is used to actually check the order. full_head_hull = hull.getMinkowskiHull(head_and_fans) self._node.callDecoration("setConvexHullHeadFull", full_head_hull) mirrored = copy.deepcopy(head_and_fans) mirrored.mirror([0, 0], [0, 1]) #Mirror horizontally. mirrored.mirror([0, 0], [1, 0]) #Mirror vertically. head_and_fans = head_and_fans.intersectionConvexHulls(mirrored) # Min head hull is used for the push free min_head_hull = hull.getMinkowskiHull(head_and_fans) self._node.callDecoration("setConvexHullHead", min_head_hull) hull = hull.getMinkowskiHull( Polygon( numpy.array( global_stack.getProperty("machine_head_polygon", "value"), numpy.float32))) else: self._node.callDecoration("setConvexHullHead", None) if self._node.getParent( ) is None: # Node was already deleted before job is done. self._node.callDecoration("setConvexHullNode", None) self._node.callDecoration("setConvexHull", None) self._node.callDecoration("setConvexHullJob", None) return hull_node = ConvexHullNode.ConvexHullNode( self._node, hull, Application.getInstance().getController().getScene().getRoot()) self._node.callDecoration("setConvexHullNode", hull_node) self._node.callDecoration("setConvexHull", hull) self._node.callDecoration("setConvexHullJob", None) if self._node.getParent() and self._node.getParent().callDecoration( "isGroup"): job = self._node.getParent().callDecoration("getConvexHullJob") if job: job.cancel() self._node.getParent().callDecoration("setConvexHull", None) hull_node = self._node.getParent().callDecoration( "getConvexHullNode") if hull_node: hull_node.setParent(None)
def _compute2DConvexHull(self): if self._node.callDecoration("isGroup"): points = numpy.zeros((0, 2), dtype=numpy.int32) for child in self._node.getChildren(): child_hull = child.callDecoration("_compute2DConvexHull") if child_hull: points = numpy.append(points, child_hull.getPoints(), axis = 0) if points.size < 3: return None child_polygon = Polygon(points) # Check the cache if child_polygon == self._2d_convex_hull_group_child_polygon: return self._2d_convex_hull_group_result # First, calculate the normal convex hull around the points convex_hull = child_polygon.getConvexHull() # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull. # This is done because of rounding errors. rounded_hull = self._roundHull(convex_hull) # Store the result in the cache self._2d_convex_hull_group_child_polygon = child_polygon self._2d_convex_hull_group_result = rounded_hull return rounded_hull else: rounded_hull = None mesh = None world_transform = None if self._node.getMeshData(): mesh = self._node.getMeshData() world_transform = self._node.getWorldTransformation() # Check the cache if mesh is self._2d_convex_hull_mesh and world_transform == self._2d_convex_hull_mesh_world_transform: return self._2d_convex_hull_mesh_result vertex_data = mesh.getConvexHullTransformedVertices(world_transform) # Don't use data below 0. # TODO; We need a better check for this as this gives poor results for meshes with long edges. # Do not throw away vertices: the convex hull may be too small and objects can collide. # vertex_data = vertex_data[vertex_data[:,1] >= -0.01] if len(vertex_data) >= 4: # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices # This is done to greatly speed up further convex hull calculations as the convex hull # becomes much less complex when dealing with highly detailed models. vertex_data = numpy.round(vertex_data, 1) vertex_data = vertex_data[:, [0, 2]] # Drop the Y components to project to 2D. # Grab the set of unique points. # # This basically finds the unique rows in the array by treating them as opaque groups of bytes # which are as long as the 2 float64s in each row, and giving this view to numpy.unique() to munch. # See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array vertex_byte_view = numpy.ascontiguousarray(vertex_data).view( numpy.dtype((numpy.void, vertex_data.dtype.itemsize * vertex_data.shape[1]))) _, idx = numpy.unique(vertex_byte_view, return_index=True) vertex_data = vertex_data[idx] # Select the unique rows by index. hull = Polygon(vertex_data) if len(vertex_data) >= 4: # First, calculate the normal convex hull around the points convex_hull = hull.getConvexHull() # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull. # This is done because of rounding errors. rounded_hull = convex_hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32))) # Store the result in the cache self._2d_convex_hull_mesh = mesh self._2d_convex_hull_mesh_world_transform = world_transform self._2d_convex_hull_mesh_result = rounded_hull return rounded_hull
def run(self): if not self._node: return ## If the scene node is a group, use the hull of the children to calculate its hull. if self._node.callDecoration("isGroup"): hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32)) for child in self._node.getChildren(): child_hull = child.callDecoration("getConvexHull") if child_hull: hull.setPoints(numpy.append(hull.getPoints(), child_hull.getPoints(), axis = 0)) if hull.getPoints().size < 3: self._node.callDecoration("setConvexHull", None) self._node.callDecoration("setConvexHullJob", None) return Job.yieldThread() else: if not self._node.getMeshData(): return mesh = self._node.getMeshData() vertex_data = mesh.getTransformed(self._node.getWorldTransformation()).getVertices() # Don't use data below 0. # TODO; We need a better check for this as this gives poor results for meshes with long edges. vertex_data = vertex_data[vertex_data[:,1] >= 0] # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices # This is done to greatly speed up further convex hull calculations as the convex hull # becomes much less complex when dealing with highly detailed models. vertex_data = numpy.round(vertex_data, 1) duplicates = (vertex_data[:,0] == vertex_data[:,1]) | (vertex_data[:,1] == vertex_data[:,2]) | (vertex_data[:,0] == vertex_data[:,2]) vertex_data = numpy.delete(vertex_data, numpy.where(duplicates), axis = 0) hull = Polygon(vertex_data[:, [0, 2]]) # First, calculate the normal convex hull around the points hull = hull.getConvexHull() # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull. # This is done because of rounding errors. hull = hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32))) profile = Application.getInstance().getMachineManager().getWorkingProfile() if profile: if profile.getSettingValue("print_sequence") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"): # Printing one at a time and it's not an object in a group self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull)) head_and_fans = Polygon(numpy.array(profile.getSettingValue("machine_head_with_fans_polygon"), numpy.float32)) # Full head hull is used to actually check the order. full_head_hull = hull.getMinkowskiHull(head_and_fans) self._node.callDecoration("setConvexHullHeadFull", full_head_hull) mirrored = copy.deepcopy(head_and_fans) mirrored.mirror([0, 0], [0, 1]) #Mirror horizontally. mirrored.mirror([0, 0], [1, 0]) #Mirror vertically. head_and_fans = head_and_fans.intersectionConvexHulls(mirrored) # Min head hull is used for the push free min_head_hull = hull.getMinkowskiHull(head_and_fans) self._node.callDecoration("setConvexHullHead", min_head_hull) hull = hull.getMinkowskiHull(Polygon(numpy.array(profile.getSettingValue("machine_head_polygon"),numpy.float32))) else: self._node.callDecoration("setConvexHullHead", None) if self._node.getParent() is None: # Node was already deleted before job is done. self._node.callDecoration("setConvexHullNode",None) self._node.callDecoration("setConvexHull", None) self._node.callDecoration("setConvexHullJob", None) return hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot()) self._node.callDecoration("setConvexHullNode", hull_node) self._node.callDecoration("setConvexHull", hull) self._node.callDecoration("setConvexHullJob", None) if self._node.getParent() and self._node.getParent().callDecoration("isGroup"): job = self._node.getParent().callDecoration("getConvexHullJob") if job: job.cancel() self._node.getParent().callDecoration("setConvexHull", None) hull_node = self._node.getParent().callDecoration("getConvexHullNode") if hull_node: hull_node.setParent(None)
def run(self): if not self._node: return ## If the scene node is a group, use the hull of the children to calculate its hull. if self._node.callDecoration("isGroup"): hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32)) for child in self._node.getChildren(): child_hull = child.callDecoration("getConvexHull") if child_hull: hull.setPoints( numpy.append(hull.getPoints(), child_hull.getPoints(), axis=0)) if hull.getPoints().size < 3: self._node.callDecoration("setConvexHull", None) self._node.callDecoration("setConvexHullJob", None) return Job.yieldThread() else: if not self._node.getMeshData(): return mesh = self._node.getMeshData() vertex_data = mesh.getTransformed( self._node.getWorldTransformation()).getVertices() # Don't use data below 0. # TODO; We need a better check for this as this gives poor results for meshes with long edges. vertex_data = vertex_data[vertex_data[:, 1] >= 0] # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices # This is done to greatly speed up further convex hull calculations as the convex hull # becomes much less complex when dealing with highly detailed models. vertex_data = numpy.round(vertex_data, 1) duplicates = (vertex_data[:, 0] == vertex_data[:, 1]) | ( vertex_data[:, 1] == vertex_data[:, 2]) | ( vertex_data[:, 0] == vertex_data[:, 2]) vertex_data = numpy.delete(vertex_data, numpy.where(duplicates), axis=0) hull = Polygon(vertex_data[:, [0, 2]]) # First, calculate the normal convex hull around the points hull = hull.getConvexHull() # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull. # This is done because of rounding errors. hull = hull.getMinkowskiHull( Polygon( numpy.array( [[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32))) profile = Application.getInstance().getMachineManager( ).getWorkingProfile() if profile: if profile.getSettingValue( "print_sequence" ) == "one_at_a_time" and not self._node.getParent().callDecoration( "isGroup"): # Printing one at a time and it's not an object in a group self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull)) head_and_fans = Polygon( numpy.array( profile.getSettingValue( "machine_head_with_fans_polygon"), numpy.float32)) # Full head hull is used to actually check the order. full_head_hull = hull.getMinkowskiHull(head_and_fans) self._node.callDecoration("setConvexHullHeadFull", full_head_hull) mirrored = copy.deepcopy(head_and_fans) mirrored.mirror([0, 0], [0, 1]) #Mirror horizontally. mirrored.mirror([0, 0], [1, 0]) #Mirror vertically. head_and_fans = head_and_fans.intersectionConvexHulls(mirrored) # Min head hull is used for the push free min_head_hull = hull.getMinkowskiHull(head_and_fans) self._node.callDecoration("setConvexHullHead", min_head_hull) hull = hull.getMinkowskiHull( Polygon( numpy.array( profile.getSettingValue("machine_head_polygon"), numpy.float32))) else: self._node.callDecoration("setConvexHullHead", None) if self._node.getParent( ) is None: # Node was already deleted before job is done. self._node.callDecoration("setConvexHullNode", None) self._node.callDecoration("setConvexHull", None) self._node.callDecoration("setConvexHullJob", None) return hull_node = ConvexHullNode.ConvexHullNode( self._node, hull, Application.getInstance().getController().getScene().getRoot()) self._node.callDecoration("setConvexHullNode", hull_node) self._node.callDecoration("setConvexHull", hull) self._node.callDecoration("setConvexHullJob", None) if self._node.getParent() and self._node.getParent().callDecoration( "isGroup"): job = self._node.getParent().callDecoration("getConvexHullJob") if job: job.cancel() self._node.getParent().callDecoration("setConvexHull", None) hull_node = self._node.getParent().callDecoration( "getConvexHullNode") if hull_node: hull_node.setParent(None)
def _compute2DConvexHull(self) -> Optional[Polygon]: if self._node is None: return None if self._node.callDecoration("isGroup"): points = numpy.zeros((0, 2), dtype=numpy.int32) for child in self._node.getChildren(): child_hull = child.callDecoration("_compute2DConvexHull") if child_hull: try: points = numpy.append(points, child_hull.getPoints(), axis = 0) except ValueError: pass if points.size < 3: return None child_polygon = Polygon(points) # Check the cache if child_polygon == self._2d_convex_hull_group_child_polygon: return self._2d_convex_hull_group_result convex_hull = child_polygon.getConvexHull() #First calculate the normal convex hull around the points. offset_hull = self._offsetHull(convex_hull) #Then apply the offset from the settings. # Store the result in the cache self._2d_convex_hull_group_child_polygon = child_polygon self._2d_convex_hull_group_result = offset_hull return offset_hull else: offset_hull = Polygon([]) mesh = self._node.getMeshData() if mesh is None: return Polygon([]) # Node has no mesh data, so just return an empty Polygon. world_transform = self._node.getWorldTransformation() # Check the cache if mesh is self._2d_convex_hull_mesh and world_transform == self._2d_convex_hull_mesh_world_transform: return self._2d_convex_hull_mesh_result vertex_data = mesh.getConvexHullTransformedVertices(world_transform) # Don't use data below 0. # TODO; We need a better check for this as this gives poor results for meshes with long edges. # Do not throw away vertices: the convex hull may be too small and objects can collide. # vertex_data = vertex_data[vertex_data[:,1] >= -0.01] if len(vertex_data) >= 4: # type: ignore # mypy and numpy don't play along well just yet. # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices # This is done to greatly speed up further convex hull calculations as the convex hull # becomes much less complex when dealing with highly detailed models. vertex_data = numpy.round(vertex_data, 1) vertex_data = vertex_data[:, [0, 2]] # Drop the Y components to project to 2D. # Grab the set of unique points. # # This basically finds the unique rows in the array by treating them as opaque groups of bytes # which are as long as the 2 float64s in each row, and giving this view to numpy.unique() to munch. # See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array vertex_byte_view = numpy.ascontiguousarray(vertex_data).view( numpy.dtype((numpy.void, vertex_data.dtype.itemsize * vertex_data.shape[1]))) _, idx = numpy.unique(vertex_byte_view, return_index = True) vertex_data = vertex_data[idx] # Select the unique rows by index. hull = Polygon(vertex_data) if len(vertex_data) >= 3: convex_hull = hull.getConvexHull() offset_hull = self._offsetHull(convex_hull) # Store the result in the cache self._2d_convex_hull_mesh = mesh self._2d_convex_hull_mesh_world_transform = world_transform self._2d_convex_hull_mesh_result = offset_hull return offset_hull
def run(self): if not self._node: return ## If the scene node is a group, use the hull of the children to calculate its hull. if self._node.callDecoration("isGroup"): hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32)) for child in self._node.getChildren(): child_hull = child.callDecoration("getConvexHull") if child_hull: hull.setPoints(numpy.append(hull.getPoints(), child_hull.getPoints(), axis = 0)) if hull.getPoints().size < 3: self._node.callDecoration("setConvexHull", None) self._node.callDecoration("setConvexHullJob", None) return Job.yieldThread() else: if not self._node.getMeshData(): return mesh = self._node.getMeshData() vertex_data = mesh.getTransformed(self._node.getWorldTransformation()).getVertices() # Don't use data below 0. # TODO; We need a better check for this as this gives poor results for meshes with long edges. vertex_data = vertex_data[vertex_data[:,1] >= 0] # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices # This is done to greatly speed up further convex hull calculations as the convex hull # becomes much less complex when dealing with highly detailed models. vertex_data = numpy.round(vertex_data, 1) vertex_data = vertex_data[:, [0, 2]] # Drop the Y components to project to 2D. # Grab the set of unique points. # # This basically finds the unique rows in the array by treating them as opaque groups of bytes # which are as long as the 2 float64s in each row, and giving this view to numpy.unique() to munch. # See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array vertex_byte_view = numpy.ascontiguousarray(vertex_data).view(numpy.dtype((numpy.void, vertex_data.dtype.itemsize * vertex_data.shape[1]))) _, idx = numpy.unique(vertex_byte_view, return_index=True) vertex_data = vertex_data[idx] # Select the unique rows by index. hull = Polygon(vertex_data) # First, calculate the normal convex hull around the points hull = hull.getConvexHull() # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull. # This is done because of rounding errors. hull = hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32))) global_stack = Application.getInstance().getGlobalContainerStack() if global_stack: if global_stack.getProperty("print_sequence", "value")== "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"): # Printing one at a time and it's not an object in a group self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull)) head_and_fans = Polygon(numpy.array(global_stack.getProperty("machine_head_with_fans_polygon", "value"), numpy.float32)) # Full head hull is used to actually check the order. full_head_hull = hull.getMinkowskiHull(head_and_fans) self._node.callDecoration("setConvexHullHeadFull", full_head_hull) mirrored = copy.deepcopy(head_and_fans) mirrored.mirror([0, 0], [0, 1]) #Mirror horizontally. mirrored.mirror([0, 0], [1, 0]) #Mirror vertically. head_and_fans = head_and_fans.intersectionConvexHulls(mirrored) # Min head hull is used for the push free min_head_hull = hull.getMinkowskiHull(head_and_fans) self._node.callDecoration("setConvexHullHead", min_head_hull) hull = hull.getMinkowskiHull(Polygon(numpy.array(global_stack.getProperty("machine_head_polygon","value"),numpy.float32))) else: self._node.callDecoration("setConvexHullHead", None) if self._node.getParent() is None: # Node was already deleted before job is done. self._node.callDecoration("setConvexHullNode",None) self._node.callDecoration("setConvexHull", None) self._node.callDecoration("setConvexHullJob", None) return hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot()) self._node.callDecoration("setConvexHullNode", hull_node) self._node.callDecoration("setConvexHull", hull) self._node.callDecoration("setConvexHullJob", None) if self._node.getParent() and self._node.getParent().callDecoration("isGroup"): job = self._node.getParent().callDecoration("getConvexHullJob") if job: job.cancel() self._node.getParent().callDecoration("setConvexHull", None) hull_node = self._node.getParent().callDecoration("getConvexHullNode") if hull_node: hull_node.setParent(None)