float MgEllipse::_hitTest(const Point2d& pt, float tol, MgHitResult& res) const { if (isCircle()) { Point2d pt1(getCenter()), pt2(pt); crossCircle(pt1, pt2, this); float d1 = pt.distanceTo(pt1); float d2 = pt.distanceTo(pt2); res.nearpt = d1 < d2 ? pt1 : pt2; return mgMin(d1, d2); } float distMin = _FLT_MAX; const Box2d rect (pt, 2 * tol, 2 * tol); Point2d ptTemp; for (int i = 0; i < 4; i++) { if (rect.isIntersect(Box2d(4, _bzpts + 3 * i))) { mgnear::nearestOnBezier(pt, _bzpts + 3 * i, ptTemp); float dist = pt.distanceTo(ptTemp); if (dist <= tol && dist < distMin) { distMin = dist; res.nearpt = ptTemp; res.segment = i; } } } return distMin; }
float mgnear::cubicSplinesHit( int n, const Point2d* knots, const Vector2d* knotvs, bool closed, const Point2d& pt, float tol, Point2d& nearpt, int& segment, bool hermite) { Point2d ptTemp; float dist, distMin = _FLT_MAX; Point2d pts[4]; const Box2d rect (pt, 2 * tol, 2 * tol); int n2 = (closed && n > 1) ? n + 1 : n; segment = -1; for (int i = 0; i + 1 < n2; i++) { mgcurv::cubicSplineToBezier(n, knots, knotvs, i, pts, hermite); if (rect.isIntersect(computeCubicBox(pts))) { dist = mgnear::nearestOnBezier(pt, pts, ptTemp); if (dist < distMin) { distMin = dist; nearpt = ptTemp; segment = i; } } } return distMin; }
float MgEllipse::_hitTest(const Point2d& pt, float tol, Point2d& nearpt, int& segment) const { float distMin = _FLT_MAX; const Box2d rect (pt, 2 * tol, 2 * tol); Point2d ptTemp; segment = -1; for (int i = 0; i < 4; i++) { if (rect.isIntersect(Box2d(4, _bzpts + 3 * i))) { mgNearestOnBezier(pt, _bzpts + 3 * i, ptTemp); float dist = pt.distanceTo(ptTemp); if (dist <= tol && dist < distMin) { distMin = dist; nearpt = ptTemp; segment = i; } } } return distMin; }
float mgnear::quadSplinesHit(int n, const Point2d* knots, bool closed, const Point2d& pt, float tol, Point2d& nearpt, int& segment) { Point2d ptTemp; float dist, distMin = _FLT_MAX; Point2d pts[3 + 4]; const Box2d rect (pt, 2 * tol, 2 * tol); segment = -1; for (int i = 0; i < (closed ? n : n - 2); i++, pts[0] = pts[2]) { if (i == 0) { pts[0] = closed ? (knots[0] + knots[1]) / 2 : knots[0]; } pts[1] = knots[(i+1) % n]; if (closed || i + 3 < n) pts[2] = (knots[(i+1) % n] + knots[(i+2) % n]) / 2; else pts[2] = knots[i+2]; mgcurv::quadBezierToCubic(pts, pts + 3); if (rect.isIntersect(computeCubicBox(pts + 3))) { dist = mgnear::nearestOnBezier(pt, pts + 3, ptTemp); if (dist < distMin) { distMin = dist; nearpt = ptTemp; segment = i; } } } return distMin; }
bool mgnear::beziersIntersectBox( const Box2d& box, int count, const Point2d* points, bool closed) { for (int i = 0; i + 3 < count; i += 3) { if (box.isIntersect(computeCubicBox(points + i))) { return true; } } if (closed && count > 3) { if (box.isIntersect(computeCubicBox(points[count - 1], points[count - 1] * 2 - points[count - 2].asVector(), points[0] * 2 - points[1].asVector(), points[0]))) { return true; } } return false; }
float mgnear::roundRectHit( const Box2d& rect, float rx, float ry, const Point2d& pt, float tol, Point2d& nearpt, int& segment) { rx = fabsf(rx); if (ry < _MGZERO) ry = rx; rx = mgMin(rx, rect.width() * 0.5f); ry = mgMin(ry, rect.height() * 0.5f); segment = -1; Point2d ptTemp, ptTemp2; float dist, distMin = _FLT_MAX; Point2d pts[8]; const Box2d rectTol (pt, 2 * tol, 2 * tol); // 顶边上的两个圆弧切点,左,右 pts[0] = RoundRectTan(0, 1, rect, rx); pts[1] = RoundRectTan(1, 0, rect, rx); // 右边上的两个圆弧切点,上,下 pts[2] = RoundRectTan(1, 2, rect, ry); pts[3] = RoundRectTan(2, 1, rect, ry); // 底边上的两个圆弧切点,右,左 pts[4] = RoundRectTan(2, 3, rect, rx); pts[5] = RoundRectTan(3, 2, rect, rx); // 左边上的两个圆弧切点,下,上 pts[6] = RoundRectTan(3, 0, rect, ry); pts[7] = RoundRectTan(0, 3, rect, ry); for (int i = 0; i < 4; i++) { Box2d rcLine (pts[2 * i], pts[2 * i + 1]); if (rcLine.isEmpty() || rectTol.isIntersect(rcLine)) { dist = mglnrel::ptToLine(pts[2 * i], pts[2 * i + 1], pt, ptTemp); if (dist <= tol && dist < distMin) { distMin = dist; nearpt = ptTemp; segment = 4 + i; } } } if (rx > _MGZERO && ry > _MGZERO) { _RoundRectHit(rect, rx, ry, pt, tol, rectTol, pts, distMin, nearpt, segment); } return distMin; }
float mgnear::linesHit( int n, const Point2d* points, bool closed, const Point2d& pt, float tol, Point2d& nearpt, int& segment, bool* inside, int* hitType) { Point2d ptTemp; float dist, distMin = _FLT_MAX; const Box2d rect (pt, 2 * tol, 2 * tol); int n2 = (closed && n > 1) ? n + 1 : n; int type = mglnrel::ptInArea(pt, n, points, segment, Tol(tol), closed); if (inside) { *inside = (closed && type == mglnrel::kPtInArea); } if (hitType) { *hitType = type; } if (type == mglnrel::kPtAtVertex) { nearpt = points[segment]; distMin = nearpt.distanceTo(pt); return distMin; } if (type == mglnrel::kPtOnEdge) { distMin = mglnrel::ptToLine(points[segment], points[(segment+1)%n], pt, nearpt); return distMin; } if (!closed || type != mglnrel::kPtInArea) { return distMin; } for (int i = 0; i + 1 < n2; i++) { const Point2d& pt2 = points[(i + 1) % n]; if (closed || rect.isIntersect(Box2d(points[i], pt2))) { dist = mglnrel::ptToLine(points[i], pt2, pt, ptTemp); if (distMin > 1e10f || (dist <= tol && dist < distMin)) { distMin = dist; nearpt = ptTemp; if (dist <= tol) segment = i; } } } return distMin; }
static void _RoundRectHit( const Box2d& rect, float rx, float ry, const Point2d& pt, float tol, const Box2d &rectTol, Point2d* pts, float& distMin, Point2d& nearpt, int& segment) { Point2d ptsBezier[13], ptTemp; Vector2d vec; float dx = rect.width() * 0.5f - rx; float dy = rect.height() * 0.5f - ry; // 按逆时针方向从第一象限到第四象限连接的四段 mgcurv::ellipseToBezier(ptsBezier, rect.center(), rx, ry); pts[3] = ptsBezier[0]; for (int i = 0; i < 4; i++) { pts[0] = pts[3]; pts[1] = ptsBezier[3 * i]; pts[2] = ptsBezier[3 * i + 1]; pts[3] = ptsBezier[3 * i + 2]; switch (i) { case 0: vec.set(dx, dy); break; case 1: vec.set(-dx, dy); break; case 2: vec.set(-dx, -dy); break; case 3: vec.set(dx, -dy); break; } for (int j = 0; j < 4; j++) pts[j] += vec; if (rectTol.isIntersect(Box2d(4, pts))) { mgnear::nearestOnBezier(pt, pts, ptTemp); float dist = pt.distanceTo(ptTemp); if (dist <= tol && dist < distMin) { distMin = dist; nearpt = ptTemp; segment = (5 - i) % 4; } } pts[3] -= vec; } }