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 mglnrel::ptToBeeline2( const Point2d& a, const Point2d& b, const Point2d& pt, Point2d& ptPerp) { // 两点重合 if (a == b) { ptPerp = a; return a.distanceTo(pt); } // 竖直线 else if (mgEquals(a.x, b.x)) { ptPerp.set(a.x, pt.y); return fabsf(a.x - pt.x); } // 水平线 else if (mgEquals(a.y, b.y)) { ptPerp.set(pt.x, a.y); return fabsf(a.y - pt.y); } else { float t1 = ( b.y - a.y ) / ( b.x - a.x ); float t2 = -1.f / t1; ptPerp.x = ( pt.y - a.y + a.x * t1 - pt.x * t2 ) / ( t1 - t2 ); ptPerp.y = a.y + (ptPerp.x - a.x) * t1; return pt.distanceTo(ptPerp); } }
Matrix2d Matrix2d::transformWith2P(const Point2d& from1, const Point2d& from2, const Point2d& to1, const Point2d& to2) { if (from1 == from2 || to1 == to2 || from1.isDegenerate() || from2.isDegenerate() || to1.isDegenerate() || to2.isDegenerate()) { return Matrix2d::kIdentity(); } return (translation(to1 - from1) * scaling(to2.distanceTo(to1) / from2.distanceTo(from1), to1) * rotation((to2 - to1).angle2() - (from2 - from1).angle2(), to1)); }
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; }
void MgBaseRect::setRectWithAngle(const Point2d& pt1, const Point2d& pt2, float angle, const Point2d& basept) { Box2d rect(pt1, pt2); if (getFlag(kMgSquare)) { if (basept == pt1 && isCurve()) { rect.set(basept, 2 * basept.distanceTo(pt2), 0); } else { float len = mgMax(fabsf(pt2.x - pt1.x), fabsf(pt2.y - pt1.y)); if (basept == pt1 && !isCurve()) { rect.set(pt1, pt1 + Point2d(pt2.x > pt1.x ? len : -len, pt2.y > pt1.y ? len : -len)); } else { rect.set(basept, basept == pt1 ? 2 * len : len, 0); } } } _points[0] = rect.leftTop(); _points[1] = rect.rightTop(); _points[2] = rect.rightBottom(); _points[3] = rect.leftBottom(); if (!mgIsZero(angle)) { Matrix2d mat(Matrix2d::rotation(angle, basept)); for (int i = 0; i < 4; i++) _points[i] *= mat; } }
float MgArc::_hitTest(const Point2d& pt, float tol, MgHitResult& res) const { Point2d points[16]; int n = mgcurv::arcToBezier(points, getCenter(), getRadius(), 0, getStartAngle(), getSweepAngle()); float dist, distMin = _FLT_MAX; Point2d ptTemp; if (_subtype > 0) { dist = mglnrel::ptToLine(getCenter(), getStartPoint(), pt, ptTemp); if (dist <= tol && dist < distMin) { distMin = dist; res.nearpt = ptTemp; } dist = mglnrel::ptToLine(getCenter(), getEndPoint(), pt, ptTemp); if (dist <= tol && dist < distMin) { distMin = dist; res.nearpt = ptTemp; } } for (int i = 0; i + 3 < n; i += 3) { mgnear::nearestOnBezier(pt, points + i, ptTemp); dist = pt.distanceTo(ptTemp); if (dist <= tol && dist < distMin) { distMin = dist; res.nearpt = ptTemp; } } return distMin; }
virtual bool processLine(int, int&, const Point2d& startpt, const Point2d& endpt) { if (box.contains(Box2d(startpt, endpt)) && mglnrel::cross2Line(startpt, endpt, a, b, tmpcross)) { dist = tmpcross.distanceTo(box.center()); if (mindist > dist) { mindist = dist; crosspt = tmpcross; } } return true; }
float mglnrel::ptToLine( const Point2d& a, const Point2d& b, const Point2d& pt, Point2d& nearpt) { Point2d ptTemp; float dist = mglnrel::ptToBeeline2(a, b, pt, nearpt); if (!mglnrel::isBetweenLine3(a, b, nearpt, &ptTemp)) { nearpt = ptTemp; dist = pt.distanceTo(nearpt); } return dist; }
virtual bool processLine(int startIndex, int &endIndex, const Point2d& startpt, const Point2d& endpt) { if (pt.distanceTo(endpt) <= dist) { p->points[startIndex] = endpt; p->points.erase(p->points.begin() + endIndex); p->types.erase(p->types.begin() + endIndex); --endIndex; } else { p->points[startIndex] = pt.rulerPoint(endpt, dist, 0); return false; } return true; }
bool MgCmdDrawLines::checkClosed(const MgMotion* sender, const Point2d& pnt) { bool closed = false; MgBaseLines* lines = (MgBaseLines*)dynshape()->shape(); if ((m_index == 0 || m_index == m_step) && needCheckClosed()) { float distmin = sender->displayMmToModel(2.f); closed = m_step > 2 && pnt.distanceTo(m_index == 0 ? lines->endPoint() : lines->getPoint(0)) < distmin; lines->setClosed(closed); } return closed; }
int mglnrel::ptInArea( const Point2d& pt, int count, const Point2d* pts, int& order, const Tol& tol, bool closed) { int i; int odd = 1; // 1: 交点数为偶数, 0: 交点数为奇数 float minDist = tol.equalPoint(); Point2d nearpt; order = -1; for (i = 0; i < count && tol.equalPoint() < 1.e5f; i++) { // P与某顶点重合. 返回 kPtAtVertex, order = 顶点号 [0, count-1] float d = pt.distanceTo(pts[i]); if (minDist > d) { minDist = d; order = i; } } if (order >= 0) { return kPtAtVertex; } order = -1; minDist = tol.equalPoint(); for (i = 0; i < (closed ? count : count - 1); i++) { const Point2d& p1 = pts[i]; const Point2d& p2 = (i+1 < count) ? pts[i+1] : pts[0]; // P在某条边上. 返回 kPtOnEdge, order = 边号 [0, count-1] float d = mglnrel::ptToBeeline2(p1, p2, pt, nearpt); if (minDist > d) { minDist = d; order = i; } else if (!PtInArea_Edge(odd, pt, p1, p2, i > 0 ? pts[i-1] : pts[count-1])) { continue; } } if (order >= 0) { return kPtOnEdge; } // 如果射线和多边形的交点数为偶数, 则 p==1, P在区外, 返回 kPtOutArea // 为奇数则p==0, P在区内, 返回 kPtInArea return 0 == odd ? kPtInArea : kPtOutArea; }
static void snapGrid(const MgMotion*, const Point2d& orignPt, const MgShape* shape, int ignoreHandle, const MgShape* sp, SnapItem arr[3], Point2d* matchpt) { if (sp->shapec()->isKindOf(MgGrid::Type())) { Point2d newPt (orignPt); const MgGrid* grid = (const MgGrid*)(sp->shapec()); Point2d dists(arr[1].dist, arr[2].dist); int type = grid->snap(newPt, dists); if (type & 1) { arr[1].base = newPt; arr[1].pt = newPt; arr[1].type = kMgSnapGridX; arr[1].dist = dists.x; } if (type & 2) { arr[2].base = newPt; arr[2].pt = newPt; arr[2].type = kMgSnapGridY; arr[2].dist = dists.y; } int d = matchpt ? shape->shapec()->getHandleCount() - 1 : -1; for (; d >= 0; d--) { if (d == ignoreHandle || shape->shapec()->isHandleFixed(d)) continue; Point2d ptd (shape->shapec()->getHandlePoint(d)); dists.set(mgMin(arr[0].dist, arr[1].dist), mgMin(arr[0].dist, arr[2].dist)); newPt = ptd; type = grid->snap(newPt, dists); float dist = newPt.distanceTo(ptd); if ((type & 3) == 3 && arr[0].dist > dist - _MGZERO) { arr[0].dist = dist; arr[0].base = ptd; arr[0].pt = newPt; arr[0].type = kMgSnapGrid; arr[0].shapeid = sp->getID(); arr[0].handleIndex = -1; arr[0].handleIndexSrc = d; // 因为对当前图形先从startM移到pointM,然后再从pointM移到matchpt *matchpt = orignPt + (newPt - ptd); // 所以最后差量为(pnt-ptd) } } } }
virtual bool processBezier(int, int&, const Point2d* pts) { float t = 0; if (box.contains(Box2d(4, pts)) && mgcurv::bezierIntersectionWithLine(pts, a, b, t)) { mgcurv::fitBezier(pts, t, tmpcross); dist = tmpcross.distanceTo(box.center()); if (mindist > dist) { mindist = dist; crosspt = tmpcross; } } return true; }
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; }
bool MgArc::setCenterStartEnd(const Point2d& center, const Point2d& start, const Point2d& end) { float startAngle = (start - center).angle2(); float endAngle = (end - center).angle2(); float sweepAngle = mgToRange(endAngle - startAngle, -_M_2PI, _M_2PI); if (!mgIsZero(sweepAngle)) { float lastSweepAngle = getSweepAngle(); if (fabsf( fabsf(sweepAngle) - fabsf(lastSweepAngle) ) > _M_PI_6) { sweepAngle = sweepAngle + (sweepAngle > 0 ? -_M_2PI : _M_2PI); } } return setCenterRadius(center, start.distanceTo(center), startAngle, sweepAngle); }
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; } }
bool MgArc::setCSE(const Point2d& center, const Point2d& start, const Point2d& end, float lastSweepAngle) { float startAngle = (start - center).angle2(); float endAngle = (end - center).angle2(); float sweepAngle = mgbase::toRange(endAngle - startAngle, -_M_2PI, _M_2PI); if (fabsf(sweepAngle - lastSweepAngle) > _M_PI) { if (fabsf(sweepAngle) < _M_D2R * 5 && fabsf(lastSweepAngle) > _M_PI + _M_PI_2) { sweepAngle = lastSweepAngle > 0 ? _M_2PI : -_M_2PI; } else { sweepAngle = sweepAngle + (sweepAngle > 0 ? -_M_2PI : _M_2PI); } } return setCenterRadius(center, start.distanceTo(center), startAngle, sweepAngle); }
float MgArc::_hitTest(const Point2d& pt, float tol, Point2d& nearpt, int&) const { Point2d points[16]; int n = mgAngleArcToBezier(points, getCenter(), getRadius(), 0, getStartAngle(), getSweepAngle()); float distMin = _FLT_MAX; Point2d ptTemp; for (int i = 0; i + 3 < n; i += 3) { mgNearestOnBezier(pt, points + i, ptTemp); float dist = pt.distanceTo(ptTemp); if (dist <= tol && dist < distMin) { distMin = dist; nearpt = ptTemp; } } return distMin; }
bool MgCmdDrawLines::canAddPoint(const MgMotion* sender, bool ended) { float minDist = mgDisplayMmToModel(3, sender); Point2d endPt = m_shape->shape()->getPoint(m_step - 1); float distToEnd = endPt.distanceTo(sender->pointM); float turnAngle = 90; if (m_step > 1) { Point2d lastPt = m_shape->shape()->getPoint(m_step - 2); turnAngle = (endPt - lastPt).angleTo(sender->pointM - endPt); turnAngle = mgRad2Deg(fabs(turnAngle)); } if (distToEnd < minDist * (ended ? 0.25 : 1)) return false; if (!ended && sin(turnAngle) * distToEnd < 5) return false; return true; }
void TransformCmd::setPointW(int index, const MgMotion* sender) { Point2d point = sender->point * sender->view->xform()->displayToWorld(); if (index > 0) { float a = (point - _origin).angle2(); float len = _origin.distanceTo(point); a = floorf(0.5f + a * _M_R2D / 5) * 5 * _M_D2R; len = floorf(0.5f + len / 5) * 5; _axis[index == 1 ? 0 : 1].setAngleLength(a, len); Matrix2d mat(_axis[0], _axis[1], _origin * sender->view->xform()->worldToModel()); mat *= _xfFirst.inverse(); mat = sender->view->shapes()->modelTransform() * mat; sender->view->xform()->setModelTransform(mat); sender->view->regen(); } else { _origin = point; sender->view->redraw(true); } }
bool MgArc::_setHandlePoint2(int index, const Point2d& pt, float, int& data) { static float lastSweepAngle; if (index == 1 || index == 2) { // 起点、终点 return setCenterRadius(getCenter(), pt.distanceTo(getCenter()), getStartAngle(), getSweepAngle()); } if (index == 3) { // 弧线中点 return setStartMidEnd(getStartPoint(), pt, getEndPoint()); } if (index == 4) { // 改变起始角度 if (data == 0) { lastSweepAngle = getSweepAngle(); data++; } Point2d startPt(getCenter().polarPoint((pt - getCenter()).angle2(), getRadius())); bool ret = setCSE(getCenter(), startPt, getEndPoint(), lastSweepAngle); lastSweepAngle = getSweepAngle(); return ret; } if (index == 5) { // 改变终止角度 if (data == 0) { lastSweepAngle = getSweepAngle(); data++; } Point2d endPt(getCenter().polarPoint((pt - getCenter()).angle2(), getRadius())); bool ret = setCSE(getCenter(), getStartPoint(), endPt, lastSweepAngle); lastSweepAngle = getSweepAngle(); return ret; } if (index == 6) { return setTanStartEnd(pt - getStartPoint(), getStartPoint(), getEndPoint()); } if (index == 7) { return (setTanStartEnd(getEndPoint() - pt, getEndPoint(), getStartPoint()) && _reverse()); } return offset(pt - getCenter(), -1); }
bool MgBaseRect::_setHandlePoint(int index, const Point2d& pt, float) { if (getFlag(kMgSquare)) { if (isCurve() && !isEmpty(_MGZERO)) { float olddist = _getHandlePoint(index).distanceTo(getCenter()); transform(Matrix2d::scaling(pt.distanceTo(getCenter()) / olddist, getCenter())); } else { Matrix2d mat(Matrix2d::rotation(-getAngle(), getCenter())); Point2d pt2(pt * mat); Box2d rect(getRect()); mgnear::moveRectHandle(rect, index, pt2); if (4 == index || 6 == index) { rect = Box2d(rect.center(), rect.height(), rect.height()); } else { rect = Box2d(rect.center(), rect.width(), rect.width()); } setRectWithAngle(rect.leftTop(), rect.rightBottom(), getAngle(), rect.center() * mat.inverse()); } } else { int index2 = index / 4 * 4 + (index % 4 + 2) % 4; Point2d corner(_getHandlePoint(index2)); Matrix2d mat(Matrix2d::rotation(-getAngle(), corner)); Box2d rect(_getHandlePoint(0) * mat, _getHandlePoint(2) * mat); Point2d pt2(pt * mat); mgnear::moveRectHandle(rect, index, pt2); setRectWithAngle(rect.leftTop(), rect.rightBottom(), getAngle(), corner); } update(); return true; }
bool MgArc::setCenterStartEnd(const Point2d& center, const Point2d& start) { float startAngle = (start - center).angle2(); return setCenterRadius(center, start.distanceTo(center), startAngle, 0); }
bool MgEllipse::setCircle2P(const Point2d& start, const Point2d& end) { return setCircle((start + end) / 2, start.distanceTo(end) / 2); }
virtual bool processLine(int, int&, const Point2d& startpt, const Point2d& endpt) { length += startpt.distanceTo(endpt); return true; }
float MgDot::_hitTest(const Point2d& pt, float, MgHitResult& res) const { res.nearpt = _point; return pt.distanceTo(_point); }