* @brief Adds a Canvas2D-style arcTo: tangent arc of radius r between two segments. */
| 1039 | * @brief Adds a Canvas2D-style arcTo: tangent arc of radius r between two segments. |
| 1040 | */ |
| 1041 | void Widgets::PainterContext::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal r) |
| 1042 | { |
| 1043 | if (r < 0.0) |
| 1044 | return; |
| 1045 | |
| 1046 | if (m_path.isEmpty()) { |
| 1047 | m_path.moveTo(x1, y1); |
| 1048 | return; |
| 1049 | } |
| 1050 | |
| 1051 | const QPointF p0 = m_path.currentPosition(); |
| 1052 | const qreal v1x = p0.x() - x1; |
| 1053 | const qreal v1y = p0.y() - y1; |
| 1054 | const qreal v2x = x2 - x1; |
| 1055 | const qreal v2y = y2 - y1; |
| 1056 | const qreal len1 = std::hypot(v1x, v1y); |
| 1057 | const qreal len2 = std::hypot(v2x, v2y); |
| 1058 | if (qFuzzyIsNull(len1) || qFuzzyIsNull(len2) || qFuzzyIsNull(r)) { |
| 1059 | m_path.lineTo(x1, y1); |
| 1060 | return; |
| 1061 | } |
| 1062 | |
| 1063 | const qreal cos_a = (v1x * v2x + v1y * v2y) / (len1 * len2); |
| 1064 | const qreal a = std::acos(qBound<qreal>(-1.0, cos_a, 1.0)); |
| 1065 | if (qFuzzyCompare(a, M_PI) || qFuzzyIsNull(a)) { |
| 1066 | m_path.lineTo(x1, y1); |
| 1067 | return; |
| 1068 | } |
| 1069 | |
| 1070 | const qreal d = r / std::tan(a * 0.5); |
| 1071 | const qreal invLen1 = 1.0 / len1; |
| 1072 | const qreal invLen2 = 1.0 / len2; |
| 1073 | const qreal n1x = v1x * invLen1; |
| 1074 | const qreal n1y = v1y * invLen1; |
| 1075 | const qreal n2x = v2x * invLen2; |
| 1076 | const qreal n2y = v2y * invLen2; |
| 1077 | const qreal tx1 = x1 + n1x * d; |
| 1078 | const qreal ty1 = y1 + n1y * d; |
| 1079 | const qreal tx2 = x1 + n2x * d; |
| 1080 | const qreal ty2 = y1 + n2y * d; |
| 1081 | |
| 1082 | const qreal sx = n1x + n2x; |
| 1083 | const qreal sy = n1y + n2y; |
| 1084 | const qreal sl = std::hypot(sx, sy); |
| 1085 | if (qFuzzyIsNull(sl)) { |
| 1086 | m_path.lineTo(x1, y1); |
| 1087 | return; |
| 1088 | } |
| 1089 | |
| 1090 | const qreal cR = r / std::sin(a * 0.5); |
| 1091 | const qreal invSl = 1.0 / sl; |
| 1092 | const qreal cX = x1 + (sx * invSl) * cR; |
| 1093 | const qreal cY = y1 + (sy * invSl) * cR; |
| 1094 | const qreal cross = v1x * v2y - v1y * v2x; |
| 1095 | |
| 1096 | m_path.lineTo(tx1, ty1); |
| 1097 | |
| 1098 | const qreal startAng = std::atan2(ty1 - cY, tx1 - cX); |