@@ -1406,7 +1406,102 @@ namespace olc::utils::geom2d
14061406 }
14071407
14081408
1409+ // project(t,c)
1410+ // project a line, onto a circle, via a ray (i.e. how far along the ray can the line travel until it contacts the circle?)
1411+ template <typename T1, typename T2, typename T3>
1412+ inline std::optional<olc::v_2d<T1>> project (const line<T1>& l, const circle<T2>& c, const ray<T3>& q)
1413+ {
1414+ // The ray intersects the line in the midpoint at all times
1415+ // The function returns a projected midpoint
1416+
1417+ // initialize variables for readable mathematics
1418+ const auto & [a, b] = c.pos ;
1419+ const auto & r = c.radius ;
1420+ auto [rx, ry] = q.direction ;
1421+ const auto & [ox, oy] = q.origin ;
1422+ const auto & [slope, intercept] = l.coefficients ();
1423+ const auto & length = l.vector ().mag ();
1424+
1425+ // First, we find two points on the cirlce that correspond to the tangent of the same slope
1426+ // as the given line
1427+ const double y_t1 = r / std::sqrt (slope * slope + 1 ) + b;
1428+ const double y_t2 = - r / std::sqrt (slope * slope + 1 ) + b;
1429+ std::vector<olc::v_2d<double >> tangent_points{ { {a - (y_t1 - b) * slope, y_t1}, {a - (y_t2 - b) * slope, y_t2} } };
1430+
1431+ // Check if the ray intersects the tangent line along its way and not from behind
1432+ const auto & v = (tangent_points[0 ] - q.origin ).norm ();
1433+ if (v.dot (q.direction ) < 0 ) return {};
1434+
1435+ // The line is first being projected onto both tangent lines, the tangent point and the intersections between the ray and
1436+ // the tangent line are stored as well
1437+ std::vector<std::pair<olc::v_2d<double >, std::pair<olc::v_2d<double >, olc::v_2d<double >>>> side_points;
1438+ std::vector<olc::v_2d<T1>> possible_points;
1439+
1440+ for (const auto & tangent_point : tangent_points)
1441+ {
1442+ const auto & [xt, yt] = tangent_point;
1443+ const double xl = (ry / rx * ox - xt * slope + yt - oy) / (ry / rx - slope);
1444+ const double yl = (xl - xt) * slope + yt;
1445+ // Creating two points on a tangent line that correspond to the start and the end of a projected line
1446+ const double x_side_1 = xl - 0.5 * length * l.vector ().norm ().x ;
1447+ const double x_side_2 = xl + 0.5 * length * l.vector ().norm ().x ;
1448+ const double y_side_1 = (x_side_1 - xl) * slope + yl;
1449+ const double y_side_2 = (x_side_2 - xl) * slope + yl;
1450+
1451+ side_points.push_back ({ {x_side_1, y_side_1}, {{xl, yl}, {xt, yt}} });
1452+ side_points.push_back ({ {x_side_2, y_side_2}, {{xl, yl}, {xt, yt}} });
1453+ }
1454+
1455+
1456+ for (const auto & [side_point, pair] : side_points)
1457+ {
1458+ const auto & [x1, y1] = side_point;
1459+ const auto & tangent_intersection_point = pair.first ;
1460+ const auto & tangent_point = pair.second ;
1461+
1462+ // We search for a scalar s such that:
1463+ // x_new = x_side + s * rx
1464+ // y_new = y_side + s * ry
1465+ // (x_new - a)^2 + (y_new - b)^2 = r^2
1466+ //
1467+ // Where (x_new, y_new) is a point on the circle that is obtained by transporting a line projected onto the
1468+ // tangent via a ray q
1469+ //
1470+ // For each tangent line and for each side of the line being transported, there can be up to 4 transported lines
1471+
1472+ const double D = (2 * rx * (x1 - a) + 2 * ry * (y1 - b)) * (2 * rx * (x1 - a) + 2 * ry * (y1 - b)) -
1473+ 4 * (rx * rx + ry * ry) * ((x1 - a) * (x1 - a) + (y1 - b) * (y1 - b) - r * r);
1474+ if (D > 0 )
1475+ {
1476+ const double s1 = (-2 * rx * (x1 - a) - 2 * ry * (y1 - b) + std::sqrt (D)) / (2 * (rx * rx + ry * ry));
1477+ const double s2 = (-2 * rx * (x1 - a) - 2 * ry * (y1 - b) - std::sqrt (D)) / (2 * (rx * rx + ry * ry));
1478+ const olc::v_2d<T1> p1{ tangent_intersection_point + s1 * q.direction };
1479+ const olc::v_2d<T1> p2{ tangent_intersection_point + s2 * q.direction };
1480+ // Only add a point is it is not inside the circle
1481+ !contains (c, p1) ? possible_points.push_back (p1) : (void )0 ;
1482+ !contains (c, p2) ? possible_points.push_back (p2) : (void )0 ;
1483+ // Only add a tangent-ray intersection point if the distance between it and the tangent point is less than of equal
1484+ // half length of the line. It means the line can lie on the tangent
1485+ (tangent_intersection_point - tangent_point).mag () <= 0.5 * length ? possible_points.push_back (tangent_intersection_point) : (void )0 ;
1486+ }
1487+ else if (D == 0 )
1488+ {
1489+ const double s = (-2 * rx * (x1 - a) - 2 * ry * (y1 - b)) / (2 * (rx * rx + ry * ry));
1490+ const olc::v_2d<T1> p{ tangent_intersection_point + s * q.direction };
1491+ !contains (c, p) ? possible_points.push_back (p): (void )0 ;
1492+ }
1493+ }
14091494
1495+ if (possible_points.empty ()) return {};
1496+
1497+ // Compare by the distance to the origin
1498+ return *std::min_element (possible_points.begin (), possible_points.end (),
1499+ [&q](const auto & lhs, const auto & rhs)
1500+ {
1501+ return (lhs - q.origin ).mag2 () < (rhs - q.origin ).mag2 ();
1502+ });
1503+
1504+ }
14101505
14111506
14121507
@@ -1923,6 +2018,14 @@ namespace olc::utils::geom2d
19232018 return internal::filter_duplicate_points (intersections);
19242019 }
19252020
2021+ // project(t,c)
2022+ // project a triangle, onto a circle, via a ray (i.e. how far along the ray can the triangle travel until it contacts the circle?)
2023+ template <typename T1, typename T2, typename T3>
2024+ inline std::optional<olc::v_2d<T1>> project (const triangle<T1>& t, const circle<T2>& c, const ray<T3> q)
2025+ {
2026+
2027+ }
2028+
19262029
19272030 // envelope_c(c)
19282031 // Return circle that fully encapsulates a point
0 commit comments