import numpy as np from math import degrees from matplotlib import cbook import math def atan2(dy, dx): if dx == 0 and dy == 0: cbook._warn_external("dx and dy are 0") return 0 else: return math.atan2(dy, dx) # FIXME : The current algorithm seems to return incorrect angle when the line # ends at the boundary. def clip(xlines, ylines, x0, clip="right", xdir=True, ydir=True): clipped_xlines = [] clipped_ylines = [] _pos_angles = [] xsign = 1 if xdir else -1 ysign = 1 if ydir else -1 for x, y in zip(xlines, ylines): if clip in ["up", "right"]: b = (x < x0).astype("i") db = b[1:] - b[:-1] else: b = (x > x0).astype("i") db = b[1:] - b[:-1] if b[0]: ns = 0 else: ns = -1 segx, segy = [], [] for (i,) in np.argwhere(db!=0): c = db[i] if c == -1: dx = (x0 - x[i]) dy = (y[i+1] - y[i]) * (dx / (x[i+1] - x[i])) y0 = y[i] + dy clipped_xlines.append(np.concatenate([segx, x[ns:i+1], [x0]])) clipped_ylines.append(np.concatenate([segy, y[ns:i+1], [y0]])) ns = -1 segx, segy = [], [] if dx == 0. and dy == 0: dx = x[i+1] - x[i] dy = y[i+1] - y[i] a = degrees(atan2(ysign*dy, xsign*dx)) _pos_angles.append((x0, y0, a)) elif c == 1: dx = (x0 - x[i]) dy = (y[i+1] - y[i]) * (dx / (x[i+1] - x[i])) y0 = y[i] + dy segx, segy = [x0], [y0] ns = i+1 if dx == 0. and dy == 0: dx = x[i+1] - x[i] dy = y[i+1] - y[i] a = degrees(atan2(ysign*dy, xsign*dx)) _pos_angles.append((x0, y0, a)) if ns != -1: clipped_xlines.append(np.concatenate([segx, x[ns:]])) clipped_ylines.append(np.concatenate([segy, y[ns:]])) return clipped_xlines, clipped_ylines, _pos_angles def clip_line_to_rect(xline, yline, bbox): x0, y0, x1, y1 = bbox.extents xdir = x1 > x0 ydir = y1 > y0 if x1 > x0: lx1, ly1, c_right_ = clip([xline], [yline], x1, clip="right", xdir=xdir, ydir=ydir) lx2, ly2, c_left_ = clip(lx1, ly1, x0, clip="left", xdir=xdir, ydir=ydir) else: lx1, ly1, c_right_ = clip([xline], [yline], x0, clip="right", xdir=xdir, ydir=ydir) lx2, ly2, c_left_ = clip(lx1, ly1, x1, clip="left", xdir=xdir, ydir=ydir) if y1 > y0: ly3, lx3, c_top_ = clip(ly2, lx2, y1, clip="right", xdir=ydir, ydir=xdir) ly4, lx4, c_bottom_ = clip(ly3, lx3, y0, clip="left", xdir=ydir, ydir=xdir) else: ly3, lx3, c_top_ = clip(ly2, lx2, y0, clip="right", xdir=ydir, ydir=xdir) ly4, lx4, c_bottom_ = clip(ly3, lx3, y1, clip="left", xdir=ydir, ydir=xdir) c_left = [((x, y), (a + 90) % 180 - 90) for x, y, a in c_left_ if bbox.containsy(y)] c_bottom = [((x, y), (90 - a) % 180) for y, x, a in c_bottom_ if bbox.containsx(x)] c_right = [((x, y), (a + 90) % 180 + 90) for x, y, a in c_right_ if bbox.containsy(y)] c_top = [((x, y), (90 - a) % 180 + 180) for y, x, a in c_top_ if bbox.containsx(x)] return list(zip(lx4, ly4)), [c_left, c_bottom, c_right, c_top]