Python 以给定角度在矩形上查找点

Python 以给定角度在矩形上查找点,python,math,graphics,geometry,Python,Math,Graphics,Geometry,我试着在一个矩形物体上画一个梯度,有一个给定的角度(θ),梯度的末端接触到矩形的周长 我原以为使用切线会管用,但我很难解决这些问题。是否有一个简单的算法,我只是错过了 最终结果 这是(角度,RectX1,RectX2,RectY1,RectY2)的函数。我希望它以[x1,x2,y1,y2]的形式返回,这样梯度就会画出正方形。 在我的问题中,如果原点是0,那么x2=-x1和y2=-y1。但它并不总是在原点上。根据您的图片,我将假设矩形的中心是(0,0),右上角是(w,h)。然后连接(0,0)到(

我试着在一个矩形物体上画一个梯度,有一个给定的角度(θ),梯度的末端接触到矩形的周长

我原以为使用切线会管用,但我很难解决这些问题。是否有一个简单的算法,我只是错过了

最终结果

这是(角度,RectX1,RectX2,RectY1,RectY2)的函数。我希望它以[x1,x2,y1,y2]的形式返回,这样梯度就会画出正方形。
在我的问题中,如果原点是0,那么x2=-x1和y2=-y1。但它并不总是在原点上。

根据您的图片,我将假设矩形的中心是(0,0),右上角是(w,h)。然后连接(0,0)到(w,h)的线与X轴形成一个角φ,其中tan(φ)=h/w

假设θ>φ,我们正在寻找点(x,y),您绘制的线与矩形的上边缘相交。那么y/x=tan(θ)。我们知道y=h,所以,解x,我们得到x=h/tan(θ)

如果θ<φ,则直线在(x,y)处与矩形的右边缘相交。这一次,我们知道x=w,所以y=tan(θ)*w.

我们把a和b称为矩形边,把(x0,y0)称为矩形中心的坐标

您需要考虑四个地区:

Region from to Where ==================================================================== 1 -arctan(b/a) +arctan(b/a) Right green triangle 2 +arctan(b/a) π-arctan(b/a) Upper yellow triangle 3 π-arctan(b/a) π+arctan(b/a) Left green triangle 4 π+arctan(b/a) -arctan(b/a) Lower yellow triangle

地区从哪里到哪里 ==================================================================== 1-arctan(b/a)+arctan(b/a)直角绿色三角形 2+arctan(b/a)π-arctan(b/a)上黄色三角形 3π-arctan(b/a)π+arctan(b/a)左绿色三角形 4π+arctan(b/a)-arctan(b/a)下黄色三角形 使用一点三角函数fu,我们可以得到每个区域中所需交点的坐标

因此Z0是区域1和3交点的表达式
Z1是区域2和4交点的表达式

所需的线从(X0,Y0)传递到Z0或Z1,具体取决于区域。记住Tan(φ)=Sin(φ)/Cos(φ)

区域中的线从开始到结束 ====================================================================== 1和3(X0,Y0)(X0+a/2,(a/2*Tan(φ))+Y0 2和4(X0,Y0)(X0+b/(2*Tan(φ)),b/2+Y0) 只需注意每个象限中的Tan(φ)符号,并且始终从正x轴逆时针测量角度

HTH!

这个问题有一个很好的答案(更具程序性的iOS/Objective-C),涉及以下步骤:

  • 假设角度大于或等于0且小于2*π,从0(东)开始逆时针旋转
  • 获取与矩形右边缘相交的y坐标[tan(角度)*宽度/2]
  • 检查此y坐标是否在矩形框中(绝对值小于或等于高度的一半)
  • 如果y交点在矩形中,则如果角度小于π/2或大于3π/2,则选择右边缘(宽度/2,-y坐标)。否则选择左边缘(-宽度/2,y坐标)
  • 如果右边缘交点的y坐标超出边界,则计算与底边交点的x坐标[高度/tan(角度)的一半]
  • 下一步确定是要上边缘还是下边缘。如果角度小于π,我们要下边缘(x,-高度的一半)。否则,我们要上边缘(-x坐标,高度的一半)
  • 然后(如果帧的中心不是0,0),按帧的实际中心偏移该点
  • 好了,我终于得到了这个

    注意:我是根据贝里萨里乌斯的绝妙回答得出的。如果你喜欢这个,也请喜欢他的。我所做的就是把他说的话变成代码

    下面是Objective-C中的内容。它应该足够简单,可以转换为您最喜欢的语言

    + (CGPoint) edgeOfView: (UIView*) view atAngle: (float) theta
    {
        // Move theta to range -M_PI .. M_PI
        const double twoPI = M_PI * 2.;
        while (theta < -M_PI)
        {
            theta += twoPI;
        }
    
        while (theta > M_PI)
        {
            theta -= twoPI;
        }
    
        // find edge ofview
        // Ref: http://stackoverflow.com/questions/4061576/finding-points-on-a-rectangle-at-a-given-angle
        float aa = view.bounds.size.width;                                          // "a" in the diagram
        float bb = view.bounds.size.height;                                         // "b"
    
        // Find our region (diagram)
        float rectAtan = atan2f(bb, aa);
        float tanTheta = tan(theta);
    
        int region;
        if ((theta > -rectAtan)
        &&  (theta <= rectAtan) )
        {
            region = 1;
        }
        else if ((theta >  rectAtan)
        &&       (theta <= (M_PI - rectAtan)) )
        {
            region = 2;
        }
        else if ((theta >   (M_PI - rectAtan))
        ||       (theta <= -(M_PI - rectAtan)) )
        {
            region = 3;
        }
        else
        {
            region = 4;
        }
    
        CGPoint edgePoint = view.center;
        float xFactor = 1;
        float yFactor = 1;
    
        switch (region)
        {
            case 1: yFactor = -1;       break;
            case 2: yFactor = -1;       break;
            case 3: xFactor = -1;       break;
            case 4: xFactor = -1;       break;
        }
    
        if ((region == 1)
        ||  (region == 3) )
        {
            edgePoint.x += xFactor * (aa / 2.);                                     // "Z0"
            edgePoint.y += yFactor * (aa / 2.) * tanTheta;
        }
        else                                                                        // region 2 or 4
        {
            edgePoint.x += xFactor * (bb / (2. * tanTheta));                        // "Z1"
            edgePoint.y += yFactor * (bb /  2.);
        }
    
        return edgePoint;
    }
    
    Javascript版本:

    功能边视图(矩形、度){
    var twoPI=Math.PI*2;
    varθ=摄氏度*数学π/180;
    while(θ<-Math.PI){
    θ+=twoPI;
    }
    while(theta>Math.PI){
    θ-=twoPI;
    }
    var rectAtan=Math.atan2(rect.height,rect.width);
    var tanTheta=数学tan(θ);
    var区域;
    
    如果((θ>-rectAtan)&&(θrectAtan)&&&(θ(Math.PI-rectAtan))| |(θ对于Java,LibGDX。为了提高精度,我将角度设为两倍

    public static Vector2 projectToRectEdge(double angle, float width, float height, Vector2 out)
    {
        return projectToRectEdgeRad(Math.toRadians(angle), width, height, out);
    }
    
    public static Vector2 projectToRectEdgeRad(double angle, float width, float height, Vector2 out)
    {
        float theta = negMod((float)angle + MathUtils.PI, MathUtils.PI2) - MathUtils.PI;
    
        float diag = MathUtils.atan2(height, width);
        float tangent = (float)Math.tan(angle);
    
        if (theta > -diag && theta <= diag)
        {
            out.x = width / 2f;
            out.y = width / 2f * tangent;
        }
        else if(theta > diag && theta <= MathUtils.PI - diag)
        {
            out.x = height / 2f / tangent;
            out.y = height / 2f;
        }
        else if(theta > MathUtils.PI - diag && theta <= MathUtils.PI + diag)
        {
            out.x = -width / 2f;
            out.y = -width / 2f * tangent;
        }
        else
        {
            out.x = -height / 2f / tangent;
            out.y = -height / 2f;
        }
    
        return out;
    }
    
    public static Vector2 projectToRectEdge(双角度、浮动宽度、浮动高度、Vector2 out)
    {
    将投影仪返回到CTEREDGERAD(数学半径(角度)、宽度、高度、out);
    }
    公共静态矢量2 projectToRectEdgeRad(双角度、浮动宽度、浮动高度、矢量2输出)
    {
    浮点θ=negMod((浮点)角度+MathUtils.PI,MathUtils.PI2)-MathUtils.PI;
    float diag=MathUtils.atan2(高度、宽度);
    浮动切线=(浮动)数学tan(角度);
    如果(θ> -diag & &θdiag & & theta MaTuffL.Pi-dia&&theta < P>虚幻引擎4(UE4)C++版本

    注意:这是基于奥利的。基于贝里萨利斯的。如果这对你有帮助的话,给那些家伙投票

    更改:使用UE4语法和函数,角度被否定

    标题

    UFUNCTION(BlueprintCallable,meta=(DisplayName=“投影到矩形边(弧度)”),Category=“数学|几何”)
    静态空心投影到矩形边缘点(FVector2D范围、浮动角度、FVector2D和边缘位置);
    
    代码

    void UFunctionLibrary::ProjectToRectangleEdgeradans(FVector2D范围、浮动角度、FVector2D和EdgeLocation)
    {
    //将θ移到-M_π..M_π的范围内。同时也将角度取反,以便按预期工作。
    浮点θ=FMath::展开半径(-角度);
    //参考:http://s
    
    @interface DebugEdgeView()
    {
        int degrees;
        UIView *dotView;
        NSTimer *timer;
    }
    
    @end
    
    @implementation DebugEdgeView
    
    - (void) dealloc
    {
        [timer invalidate];
    }
    
    
    - (id) initWithFrame: (CGRect) frame
    {
        self = [super initWithFrame: frame];
        if (self)
        {
            self.backgroundColor = [[UIColor magentaColor] colorWithAlphaComponent: 0.25];
            degrees = 0;
            self.clipsToBounds = NO;
    
            // create subview dot
            CGRect dotRect = CGRectMake(frame.size.width / 2., frame.size.height / 2., 20, 20);
            dotView = [[DotView alloc] initWithFrame: dotRect];
            dotView.backgroundColor = [UIColor magentaColor];
            [self addSubview: dotView];
    
            // move it around our edges
            timer = [NSTimer scheduledTimerWithTimeInterval: (5. / 360.)
                                                     target: self
                                                   selector: @selector(timerFired:)
                                                   userInfo: nil
                                                    repeats: YES];
        }
    
        return self;
    }
    
    
    - (void) timerFired: (NSTimer*) timer
    {
        float radians = ++degrees * M_PI / 180.;
        if (degrees > 360)
        {
            degrees -= 360;
        }
    
        dispatch_async(dispatch_get_main_queue(), ^{
            CGPoint edgePoint = [MFUtils edgeOfView: self atAngle: radians];
            edgePoint.x += (self.bounds.size.width  / 2.) - self.center.x;
            edgePoint.y += (self.bounds.size.height / 2.) - self.center.y;
            dotView.center = edgePoint;
        });
    }
    
    @end
    
    public static Vector2 projectToRectEdge(double angle, float width, float height, Vector2 out)
    {
        return projectToRectEdgeRad(Math.toRadians(angle), width, height, out);
    }
    
    public static Vector2 projectToRectEdgeRad(double angle, float width, float height, Vector2 out)
    {
        float theta = negMod((float)angle + MathUtils.PI, MathUtils.PI2) - MathUtils.PI;
    
        float diag = MathUtils.atan2(height, width);
        float tangent = (float)Math.tan(angle);
    
        if (theta > -diag && theta <= diag)
        {
            out.x = width / 2f;
            out.y = width / 2f * tangent;
        }
        else if(theta > diag && theta <= MathUtils.PI - diag)
        {
            out.x = height / 2f / tangent;
            out.y = height / 2f;
        }
        else if(theta > MathUtils.PI - diag && theta <= MathUtils.PI + diag)
        {
            out.x = -width / 2f;
            out.y = -width / 2f * tangent;
        }
        else
        {
            out.x = -height / 2f / tangent;
            out.y = -height / 2f;
        }
    
        return out;
    }
    
    import math
    import matplotlib.pyplot as plt
    
    twoPI = math.pi * 2.0
    PI = math.pi
    
    def get_points(width, height, theta):
        theta %= twoPI
    
        aa = width
        bb = height
    
        rectAtan = math.atan2(bb,aa)
        tanTheta = math.tan(theta)
    
        xFactor = 1
        yFactor = 1
        
        # determine regions
        if theta > twoPI-rectAtan or theta <= rectAtan:
            region = 1
        elif theta > rectAtan and theta <= PI-rectAtan:
            region = 2
    
        elif theta > PI - rectAtan and theta <= PI + rectAtan:
            region = 3
            xFactor = -1
            yFactor = -1
        elif theta > PI + rectAtan and theta < twoPI - rectAtan:
            region = 4
            xFactor = -1
            yFactor = -1
        else:
            print(f"region assign failed : {theta}")
            raise
        
        # print(region, xFactor, yFactor)
        edgePoint = [0,0]
        ## calculate points
        if (region == 1) or (region == 3):
            edgePoint[0] += xFactor * (aa / 2.)
            edgePoint[1] += yFactor * (aa / 2.) * tanTheta
        else:
            edgePoint[0] += xFactor * (bb / (2. * tanTheta))
            edgePoint[1] += yFactor * (bb /  2.)
    
        return region, edgePoint
    
    l_x = []
    l_y = []
    theta = 0
    for _ in range(10000):
        r, (x, y) = get_points(600,300, theta)
        l_x.append(x)
        l_y.append(y)
        theta += (0.01 / PI)
    
        if _ % 100 == 0:
            print(r, x,y)
    
    plt.plot(l_x, l_y)
    plt.show()