0

我无法理解计算atan2并将 a 放置geom_arc在预期的一侧。

这是一些带有四对点 ABC 的虚拟数据。我想画四个弧线,每个弧线都在角度较小的一侧并打印角度。计算角度 dregrees 是可行的,但我需要 1)一些开关来纠正atan2和/或 2)另一个开关将弧放在预期的一侧。只是交换startend没有任何效果。

tmp <- tibble(xA = c(11, 14, 11, 14), yA = c(8, 8, 7, 7),
              xB = c(10, 15, 10, 15), yB = c(10, 10, 5, 5),
              xC = c(8, 17, 8, 17), yC = c(11, 11, 4, 4))

tmp <- tmp %>%
  # dAB = distance between A and B (same as distance BC)
  mutate(dAB = sqrt((xA - xB)^2 + (yA - yB)^2)) %>%
  mutate(dBC = sqrt((xB - xC)^2 + (yB - yC)^2)) %>%
  # calculate atan AB for arc start
  mutate(arcAB = atan2(yA - yB, xA - xB)) %>%
  # calculate atan BC for arc end
  mutate(arcBC = atan2(yC - yB, xC - xB)) %>%
  # calculate angle degree
  mutate(arc_deg = round((arcBC * (180 / pi)) - (arcAB *  (180 / pi)), 1)) %>%
  mutate(arc_deg = case_when(
    arc_deg >  180 ~ arc_deg - 360,
    arc_deg < -180 ~ arc_deg + 360,
    TRUE           ~ arc_deg)) %>%
  # calculate position for angle dreeg text
  mutate(xAC = (xA + xC) / 2) %>%
  mutate(yAC = (yA + yC) / 2)
tmp

ggplot(tmp) + 
  geom_segment(aes(x = xB, xend = xA, y = yB, yend = yA), size = 1, col = "blue") +
  geom_segment(aes(x = xB, xend = xC, y = yB, yend = yC), size = 1, col = "green") +
  geom_text(aes(x = xB + 0.2, y = yB), label = "B") +
  geom_text(aes(x = xA + 0.2, y = yA), label = "A") +
  geom_text(aes(x = xC - 0.2, y = yC), label = "C") +
  geom_arc(aes(x0 = xB, y0 = yB, r = dAB, start = arcAB, end = arcBC)) + # plus or minus 2*pi
  geom_text(aes(x = xAC, y = yAC, label = paste0(arc_deg, "°"))) +
  coord_fixed() + theme_bw()

上面的代码只适用于右下角的弧线: 弧形

我已经看到了这一点,左上角的弧线+ 2*pi会产生预期的效果,但我认为这需要四种不同的方式来涵盖所有情况。

我知道这可能更像是一个数学问题,并考虑在其他地方提出,但最终这是一个应用程序,ggforce::geom_arc其他人可能有类似的问题。(对我来说,这将是完美的,如果我只需要给出 A、B 和 C 的坐标以及顺时针的起点和终点。)

4

1 回答 1

1

看来您正在寻找这样的东西:

tmp %>% 
  mutate(r = sqrt((yA - yB)^2 + (xA - xB)^2),
         thetaAB = atan2(xA - xB, yA - yB),
         thetaAB = ifelse(thetaAB < 0, thetaAB + 2 * pi, thetaAB),
         thetaBC = atan2(xC - xB, yC - yB),
         thetaBC = ifelse(thetaBC < 0, thetaBC + 2 * pi, thetaBC),
         direction = ifelse(abs(thetaAB - thetaBC) > pi, "switch", "keep"),
         Start = ifelse(abs(thetaAB - thetaBC) > pi & thetaAB < thetaBC, 
                        thetaAB + 2 * pi, thetaAB),
         End = ifelse(abs(thetaAB - thetaBC) >  pi & thetaBC < thetaAB, 
                      thetaBC + 2 * pi, thetaBC),
         arc_deg = format((End - Start) * 180/pi, digits = 4),
         xAC = (xA + xC) / 2,
         yAC = (yA + yC) / 2) %>%
  ggplot() + 
  geom_segment(aes(x = xB, xend = xA, y = yB, yend = yA), size = 1, col = "blue") +
  geom_segment(aes(x = xB, xend = xC, y = yB, yend = yC), size = 1, col = "green") +
  geom_text(aes(x = xB + 0.2, y = yB), label = "B") +
  geom_text(aes(x = xA + 0.2, y = yA), label = "A") +
  geom_text(aes(x = xC - 0.2, y = yC), label = "C") +
  geom_arc(aes(x0 = xB, y0 = yB, r = r, start = Start, end = End)) + # plus or minus 2*pi
  geom_text(aes(x = xAC, y = yAC, label = paste0(arc_deg, "°"))) +
  coord_fixed() + 
  theme_bw()

在此处输入图像描述

这里要实现的关键是:

  • geom_arc目前而言,0 度出现在 y 轴上(在 12 点钟位置),正角从那里顺时针测量。通常在使用三角函数时,我们会将 0 度视为沿 x 轴并逆时针测量。这是背后的假设之一atan2。这意味着您需要计算atan2atan2([delta x], [delta y])不是相反(文档说atan2([delta y], [delta x]),这就是您的代码的方式)。
  • 由于您希望测量两条线之间的最小角度(即小于 pi 弧度的角度),您需要找出角度之间的哪些差异大于pi弧度,在这些情况下,添加2 * pi两个角度中的较小者. 这确保了角度始终低于 pi 弧度。
于 2021-11-11T17:58:56.403 回答