def translate(self, delta): """ Returns a new Box that has the same size as this Box and a translated position as specified by delta. @param delta: The distance to the position of this Box. @return: A new Box as specified above. """ return Box(tools.vector_add(self.get_position(), delta), self.get_size())
def scroll_smartly(self, content_box, viewport_box, orientation, max_scroll, axis_map=None): ''' Returns a new viewport position when reading forwards using the given orientation. If there is no space left to go, the empty list is returned. Note that all params are lists of ints (except max_scroll which might also contain floats) where each index corresponds to one dimension. The lower the index, the faster the corresponding position changes when reading. If you need to override this behavior, use the optional axis_map. @param content_box: The Box of the content to display. @param viewport_box: The viewport Box we are looking through. @param orientation: The orientation which shows where "forward" points to. Either 1 (towards larger values in this dimension when reading) or -1 (towards smaller values in this dimension when reading). Note that you can emulate "reading backwards" by flipping the sign of this argument. @param max_scroll: The maximum number of pixels to scroll in one step. (Floats allowed.) @param axis_map: The index of the dimension to modify. @return: A new viewport_position if you can read further or the empty list if there is nothing left to read. ''' # Translate content and viewport so that content position equals origin offset = content_box.get_position() content_size = content_box.get_size() viewport_position = tools.vector_sub(viewport_box.get_position(), offset) viewport_size = viewport_box.get_size() # Remap axes if axis_map is not None: content_size, viewport_size, viewport_position, orientation, \ max_scroll = Scrolling._map_remap_axes([content_size, viewport_size, viewport_position, orientation, max_scroll], axis_map) result = list(viewport_position) carry = True reset_all_axes = False for i in range(len(content_size)): invisible_size = content_size[i] - viewport_size[i] o = orientation[i] # Find a nice starting point if o == 1: if viewport_position[i] < 0: result[i] = 0 carry = False if viewport_position[i] <= -viewport_size[i]: reset_all_axes = True break else: # o == -1 if viewport_position[i] > invisible_size: result[i] = invisible_size carry = False if viewport_position[i] > content_size[i]: reset_all_axes = True break if reset_all_axes: # We don't see anything at all because we are somewhere way before # the content box. Let's go to it. for i in range(len(content_size)): invisible_size = content_size[i] - viewport_size[i] o = orientation[i] if o == 1: result[i] = 0 else: # o == -1 result[i] = invisible_size # This code is somewhat similar to a simple ripple-carry adder. if carry: for i in range(len(content_size)): invisible_size = content_size[i] - viewport_size[i] o = orientation[i] ms = min(max_scroll[i], invisible_size) # Let's calculate the grid we want to snap to. if ms != 0: steps_to_take = int(math.ceil(float(invisible_size) / ms)) if ms == 0 or steps_to_take >= invisible_size: # special case: We MUST go forward by at least 1 pixel. if o >= 0: result[i] += 1 carry = result[i] > invisible_size if carry: result[i] = 0 continue else: result[i] -= 1 carry = result[i] < 0 if carry: result[i] = invisible_size continue break # If orientation is -1, we need to round half up instead of # half down. positions = self._cached_bs(invisible_size, steps_to_take, o == -1) # Where are we now (according to the grid)? index = tools.bin_search(positions, viewport_position[i]) if index < 0: # We're somewhere between two valid grid points, so # let's go to the next one. index = ~index if o >= 0: # index tends to be greater, so we need to go back # manually, if needed. index -= 1 # Let's go to where we're headed for. index += o carry = index < 0 or index >= len(positions) if carry: # There is no space left in this dimension, so let's go # back in this one and one step forward in the next one. result[i] = 0 if o > 0 else invisible_size else: # We found a valid grid point in this dimension, so let's # stop here. result[i] = positions[index] break if carry: # No space left. return [] # Undo axis remapping, if any if axis_map is not None: result = Scrolling._remap_axes(result, Scrolling._inverse_axis_map(axis_map)) return tools.vector_add(result, offset)
def scroll_smartly(self, content_box, viewport_box, orientation, max_scroll, axis_map=None): """ Returns a new viewport position when reading forwards using the given orientation. If there is no space left to go, the empty list is returned. Note that all params are lists of ints (except max_scroll which might also contain floats) where each index corresponds to one dimension. The lower the index, the faster the corresponding position changes when reading. If you need to override this behavior, use the optional axis_map. @param content_box: The Box of the content to display. @param viewport_box: The viewport Box we are looking through. @param orientation: The orientation which shows where "forward" points to. Either 1 (towards larger values in this dimension when reading) or -1 (towards smaller values in this dimension when reading). Note that you can emulate "reading backwards" by flipping the sign of this argument. @param max_scroll: The maximum number of pixels to scroll in one step. (Floats allowed.) @param axis_map: The index of the dimension to modify. @return: A new viewport_position if you can read further or the empty list if there is nothing left to read. """ # Translate content and viewport so that content position equals origin offset = content_box.get_position() content_size = content_box.get_size() viewport_position = tools.vector_sub(viewport_box.get_position(), offset) viewport_size = viewport_box.get_size() # Remap axes if axis_map is not None: content_size, viewport_size, viewport_position, orientation, \ max_scroll = Scrolling._map_remap_axes([content_size, viewport_size, viewport_position, orientation, max_scroll], axis_map) result = list(viewport_position) carry = True reset_all_axes = False for i in range(len(content_size)): invisible_size = content_size[i] - viewport_size[i] o = orientation[i] # Find a nice starting point if o == 1: if viewport_position[i] < 0: result[i] = 0 carry = False if viewport_position[i] <= -viewport_size[i]: reset_all_axes = True break else: # o == -1 if viewport_position[i] > invisible_size: result[i] = invisible_size carry = False if viewport_position[i] > content_size[i]: reset_all_axes = True break if reset_all_axes: # We don't see anything at all because we are somewhere way before # the content box. Let's go to it. for i in range(len(content_size)): invisible_size = content_size[i] - viewport_size[i] o = orientation[i] if o == 1: result[i] = 0 else: # o == -1 result[i] = invisible_size # This code is somewhat similar to a simple ripple-carry adder. if carry: for i in range(len(content_size)): invisible_size = content_size[i] - viewport_size[i] o = orientation[i] ms = min(max_scroll[i], invisible_size) # Let's calculate the grid we want to snap to. if ms != 0: steps_to_take = int(math.ceil(float(invisible_size) / ms)) if ms == 0 or steps_to_take >= invisible_size: # special case: We MUST go forward by at least 1 pixel. if o >= 0: result[i] += 1 carry = result[i] > invisible_size if carry: result[i] = 0 continue else: result[i] -= 1 carry = result[i] < 0 if carry: result[i] = invisible_size continue break # If orientation is -1, we need to round half up instead of # half down. positions = self._cached_bs(invisible_size, steps_to_take, o == -1) # Where are we now (according to the grid)? index = tools.bin_search(positions, viewport_position[i]) if index < 0: # We're somewhere between two valid grid points, so # let's go to the next one. index = ~index if o >= 0: # index tends to be greater, so we need to go back # manually, if needed. index -= 1 # Let's go to where we're headed for. index += o carry = index < 0 or index >= len(positions) if carry: # There is no space left in this dimension, so let's go # back in this one and one step forward in the next one. result[i] = 0 if o > 0 else invisible_size else: # We found a valid grid point in this dimension, so let's # stop here. result[i] = positions[index] break if carry: # No space left. return [] # Undo axis remapping, if any if axis_map is not None: result = Scrolling._remap_axes(result, Scrolling._inverse_axis_map(axis_map)) return tools.vector_add(result, offset)