Нахождение точек на прямоугольнике под заданным углом - PullRequest
17 голосов
/ 31 октября 2010

Я пытаюсь нарисовать градиент в объекте прямоугольника с заданным углом (тета), где концы градиента касаются периметра прямоугольника.

Graph

Я думал, что использование тангенса сработает, но у меня проблемы с выводом изломов.Есть ли простой алгоритм, который я просто упускаю?

Конечный результат

Итак, это будет функция (angle, RectX1, RectX2, RectY1,RectY2).Я хочу, чтобы он возвращался в виде [x1, x2, y1, y2], чтобы градиент рисовал через квадрат.В моей задаче, если источник равен 0, то x2 = -x1 и y2 = -y1.Но это не всегда будет исходно.

Ответы [ 6 ]

35 голосов
/ 31 октября 2010

Давайте назовем a и b вашими сторонами прямоугольника, а (x0, y0) координатами вашего центра прямоугольника.

У вас есть четыре региона для рассмотрения:

alt text

    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

Немного тригонометрии-фу, мы можем получить координаты для вашего желаемого пересечения в каждомобласть, край.

alt text

Итак Z0 - это выражение для точки пересечения для областей 1 и 3
, а Z1 - это выражение дляточка пересечения для областей 2 и 4

Желаемые линии проходят от (X0, Y0) до Z0 или Z1 в зависимости от региона.Итак, помня, что Tan (φ) = Sin (φ) / Cos (φ)


    Lines in regions      Start                   End
    ======================================================================
       1 and 3           (X0,Y0)      (X0 + a/2 , (a/2 * Tan(φ))+ Y0
       2 and 4           (X0,Y0)      (X0 + b/(2* Tan(φ)) , b/2 + Y0)

Просто помните о знаках Tan (φ) в каждом квадранте и что угол всегда измеряется от THEПОЗИТИВНО против часовой стрелки против часовой стрелки.

HTH!

10 голосов
/ 01 ноября 2014

Хорошо, Вот так! , я наконец получил это.

ПРИМЕЧАНИЕ: Я основал это на удивительном ответе ВелисарияЕсли вам это нравится, пожалуйста, как и его тоже.Все, что я сделал, это превратил то, что он сказал, в код.

Вот как это выглядит в 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: /3408800/nahozhdenie-tochek-na-pryamougolnike-pod-zadannym-uglom
    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;
}

Кроме того, вот небольшой тестовый просмотр, который я создал, чтобы убедиться, что он работает.Создайте этот вид и поместите его куда-нибудь, он сделает еще один маленький вид вокруг края.

@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
6 голосов
/ 08 августа 2015

Javascript версия:

function edgeOfView(rect, deg) {
  var twoPI = Math.PI*2;
  var theta = deg * Math.PI / 180;
  
  while (theta < -Math.PI) {
    theta += twoPI;
  }
  
  while (theta > Math.PI) {
    theta -= twoPI;
  }
  
  var rectAtan = Math.atan2(rect.height, rect.width);
  var tanTheta = Math.tan(theta);
  var region;
  
  if ((theta > -rectAtan) && (theta <= rectAtan)) {
      region = 1;
  } else if ((theta > rectAtan) && (theta <= (Math.PI - rectAtan))) {
      region = 2;
  } else if ((theta > (Math.PI - rectAtan)) || (theta <= -(Math.PI - rectAtan))) {
      region = 3;
  } else {
      region = 4;
  }
  
  var edgePoint = {x: rect.width/2, y: rect.height/2};
  var xFactor = 1;
  var 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 * (rect.width / 2.);                                     // "Z0"
    edgePoint.y += yFactor * (rect.width / 2.) * tanTheta;
  } else {
    edgePoint.x += xFactor * (rect.height / (2. * tanTheta));                        // "Z1"
    edgePoint.y += yFactor * (rect.height /  2.);
  }
  
  return edgePoint;
};
3 голосов
/ 31 октября 2010

Следуя вашей картинке, я собираюсь предположить, что прямоугольник центрирован в (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. </p>

1 голос
/ 02 марта 2012

Есть хороший (более программный iOS / Objective-C) ответ на этот вопрос на Найдите CGPoint в прямоугольнике UIView, пересекаемом прямой линией под заданным углом от центральной точки , включающем следующие шаги :

  1. Предположим, что угол больше или равен 0 и меньше, чем 2 * π, против часовой стрелки от 0 (восток).
  2. Получите координату y пересечения с правым краем прямоугольника [tan (angle) * width / 2].
  3. Проверьте, находится ли эта координата y в прямоугольной рамке (абсолютное значение меньше или равно половине высоты).
  4. Если пересечение y находится в прямоугольнике, то если угол меньше π / 2 или больше 3π / 2, выберите правый край (ширина / 2, -y координата). В противном случае выберите левый край (-width / 2, координата y).
  5. Если координата y правого края пересечения была за пределами, вычислите координату x пересечения с нижним краем [половина высоты / tan (угол)].
  6. Далее определите, хотите ли вы верхний или нижний край. Если угол меньше π, нам нужен нижний край (x, половина от высоты). В противном случае нам нужен верхний край (-x координата, половина высоты).
  7. Затем (если центр кадра не равен 0,0), сместить точку на фактический центр кадра.
0 голосов
/ 06 января 2019

Для 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...