Extending a line segment to fit into a bounding box

前端 未结 5 1668
误落风尘
误落风尘 2021-01-12 03:25

I have a line segment defined by two pointFs, along with a 2D bounding rectangle. I want to extend the line segment as much as possible in both directions so t

5条回答
  •  难免孤独
    2021-01-12 03:58

    An extended version of the @andredor algorithm to cover all cases (also when the segments are not parallel to the axes - e.g. when the segments are diagonal). With elaborate explanation of the method as documentation.

    def extend_line(xmin, ymin, xmax, ymax, x1, y1, x2, y2):
        """
        Extend a line so that it reaches the walls of the bbox.
    
        Args:
            xmin(int): The very left coordinate of the bbox.
            ymin(int): The very top coordinate of the bbox.
            xmax(int): The very right coordinate of the bbox.
            ymax(int): The very bottom coordinate of the bbox.
            x1(int): The start x coordinate of the line.
            y1(int): The start y coordinate of the line.
            x2(int): The end x coordinate of the line.
            y2(int): The end y coordinate of the line.
    
        Returns:
            - (list): The start and end (x, y) coordinates of the extended line.
        """
    
        # If we imagine extending the line until it crosses the top wall of the 
        # bbox at point `(xmin, y_for_xmin)` and then imagine drawing 
        # perpendicular lines from each point `(x1, y1)`, `(x2, y2)` to the wall 
        # of the bbox, we end up with 2 perpendicular trianlges with the same 
        # angles - similar triangles. The rule of the similar triangles is that   
        # the side lengths of two similar triangles are proportional. 
        # That's how we get the equal ratios:
        # `| y_for_xmin - y1 | / | xmin - x1 | == | y2 - y1 | / | x2 - x1 |`
        # After we move some numbers from one to the other side of this equation, 
        # we get the value for `y_for_xmin`. That's where the line should cross 
        # the top wall of the bbox. We do the same for all other coordinates.
        # NOTE: These calculations are valid if one starts to draw a line from top 
        # to botton and from left to right. In case the direction is reverted, we 
        # need to switch the min and max for each point (x, y). We do that below.
        y_for_xmin = y1 + (y2 - y1) * (xmin - x1) / (x2 - x1)
        y_for_xmax = y1 + (y2 - y1) * (xmax - x1) / (x2 - x1)
        x_for_ymin = x1 + (x2 - x1) * (ymin - y1) / (y2 - y1)
        x_for_ymax = x1 + (x2 - x1) * (ymax - y1) / (y2 - y1)
    
        # The line is vertical
        if (x2 - x1) < (y2 - y1):
            # The line is drawn from right to left
            if x1 > x2:
                # Switch the min and max x coordinates for y, 
                # because the direction is from right (min) to left (max)
                y_for_xmin, y_for_xmax = y_for_xmax, y_for_xmin
        # The line is horizontal
        else:
            # The line is drawn from bottom to top
            if y1 > y2:
                # Switch the min and max y coordinates for x,
                # because the direction is from bottom (min) to top (max)
                x_for_ymin, x_for_ymax = x_for_ymax, x_for_ymin
    
        # The line is drawn from right to left
        if x1 > x2:
            # Get the maximal value for x1.
            # When `x_for_ymin < xmin`(line goes out of the 
            # bbox from the top), we clamp to xmin.
            x1 = max(max(int(x_for_ymin), xmin), x1)
        # The line is drawn from left to right
        else:
            # Get the minimal value for x1.
            # When `x_for_ymin < xmin`(line goes out of the 
            # bbox from the top), we clamp to xmin. 
            x1 = min(max(int(x_for_ymin), xmin), x1)
    
        # Get the maximal value for x2.
        # When `x_for_ymax > xmax` (line goes out of the 
        # bbox from the bottom), we clamp to xmax.
        x2 = max(min(int(x_for_ymax), xmax), x2)
    
        # Get the minimal value for y1
        # When `y_for_xmin < ymin`(line goes out of the 
        # bbox from the left), we clamp to ymin.
        y1 = min(max(int(y_for_xmin), ymin), ymax)
    
        # Get the minimal value for y2
        y2 = min(int(y_for_xmax), ymax)
    
        # Done
        return [x1, y1, x2, y2]
    

提交回复
热议问题