def rotate_clip(data_np, theta_deg, rotctr_x=None, rotctr_y=None,
                out=None):
    """
    Rotate numpy array `data_np` by `theta_deg` around rotation center
    (rotctr_x, rotctr_y).  If the rotation center is omitted it defaults
    to the center of the array.

    No adjustment is done to the data array beforehand, so the result will
    be clipped according to the size of the array (the output array will be
    the same size as the input array).
    """

    # If there is no rotation, then we are done
    if math.fmod(theta_deg, 360.0) == 0.0:
        return data_np

    ht, wd = data_np.shape[:2]

    if rotctr_x is None:
        rotctr_x = wd // 2
    if rotctr_y is None:
        rotctr_y = ht // 2

    if have_opencv:
        # opencv is fastest
        M = cv2.getRotationMatrix2D((rotctr_y, rotctr_x), theta_deg, 1)
        if out is not None:
            out[:, :, ...] = cv2.warpAffine(data_np, M, (wd, ht))
            newdata = out
        else:
            newdata = cv2.warpAffine(data_np, M, (wd, ht))
            new_ht, new_wd = newdata.shape[:2]

            assert (wd == new_wd) and (ht == new_ht), \
                   Exception("rotated cutout is %dx%d original=%dx%d" % (
                new_wd, new_ht, wd, ht))

    else:
        yi, xi = numpy.mgrid[0:ht, 0:wd]
        xi -= rotctr_x
        yi -= rotctr_y
        cos_t = numpy.cos(numpy.radians(theta_deg))
        sin_t = numpy.sin(numpy.radians(theta_deg))

        #t1 = time.time()
        if have_numexpr:
            ap = ne.evaluate("(xi * cos_t) - (yi * sin_t) + rotctr_x")
            bp = ne.evaluate("(xi * sin_t) + (yi * cos_t) + rotctr_y")
        else:
            ap = (xi * cos_t) - (yi * sin_t) + rotctr_x
            bp = (xi * sin_t) + (yi * cos_t) + rotctr_y
        #print "rotation in %.5f sec" % (time.time() - t1)

        #ap = numpy.rint(ap).astype('int').clip(0, wd-1)
        #bp = numpy.rint(bp).astype('int').clip(0, ht-1)
        # Optomizations to reuse existing intermediate arrays
        numpy.rint(ap, out=ap)
        ap = ap.astype('int')
        ap.clip(0, wd-1, out=ap)
        numpy.rint(bp, out=bp)
        bp = bp.astype('int')
        bp.clip(0, ht-1, out=bp)

        if out is not None:
            out[:, :, ...] = data_np[bp, ap]
            newdata = out
        else:
            newdata = data_np[bp, ap]
            new_ht, new_wd = newdata.shape[:2]

            assert (wd == new_wd) and (ht == new_ht), \
                   Exception("rotated cutout is %dx%d original=%dx%d" % (
                new_wd, new_ht, wd, ht))

    return newdata
dataset/ETHPy150Open ejeschke/ginga/ginga/trcalc.py/rotate_clip/original