bool mglnrel::cross2Line( const Point2d& a, const Point2d& b, const Point2d& c, const Point2d& d, Point2d& ptCross, const Tol& tolVec) { float u, v, denom, cosnum; if (mgMin(a.x,b.x) - mgMax(c.x,d.x) > _MGZERO || mgMin(c.x,d.x) - mgMax(a.x,b.x) > _MGZERO || mgMin(a.y,b.y) - mgMax(c.y,d.y) > _MGZERO || mgMin(c.y,d.y) - mgMax(a.y,b.y) > _MGZERO) return false; denom = (c.x-d.x)*(b.y-a.y)-(c.y-d.y)*(b.x-a.x); if (mgIsZero(denom)) return false; cosnum = (b.x-a.x)*(d.x - c.x) + (b.y-a.y)*(d.y-c.y); if (!mgIsZero(cosnum) && fabsf(denom / cosnum) < tolVec.equalVector()) return false; u = ((c.x-a.x)*(d.y-c.y)-(c.y-a.y)*(d.x-c.x)) / denom; if (u < _MGZERO || u > 1.f - _MGZERO) return false; v = ((c.x-a.x)*(b.y-a.y)-(c.y-a.y)*(b.x-a.x)) / denom; if (v < _MGZERO || v > 1.f - _MGZERO) return false; ptCross.x = (1 - u) * a.x + u * b.x; ptCross.y = (1 - u) * a.y + u * b.y; return true; }
void GiTransform::setViewScaleRange(float minScale, float maxScale) { if (minScale > maxScale) mgSwap(minScale, maxScale); minScale = mgMax(minScale, 1e-5f); minScale = mgMin(minScale, 0.5f); maxScale = mgMax(maxScale, 1.f); maxScale = mgMin(maxScale, 50.f); m_impl->minViewScale = minScale; m_impl->maxViewScale = maxScale; }
bool MgGrid::_setHandlePoint(int index, const Point2d& pt, float tol) { if (index < 8) { return __super::_setHandlePoint(index, pt, tol); } float cx = (float)fabs(pt.x - getPoint(3).x); float cy = (float)fabs(pt.y - getPoint(3).y); cx = mgMax(mgMax(cx, tol / 3.f), cy); cx = (float)( (int)(cx * 100) / 10 ) * 0.1f; m_cell.set(cx, cx); return true; }
float GiGraphics::calcPenWidth(float lineWidth, bool useViewScale) const { float w = 1; float px; if (m_impl->maxPenWidth <= 1) lineWidth = 0; if (lineWidth > 0) // 单位:0.01mm { px = lineWidth / 2540.f * xf().getDpiY(); if (useViewScale) px *= xf().getViewScale(); w = mgMin(px, m_impl->maxPenWidth); } else if (lineWidth < 0) // 单位:像素 { w = mgMin(-lineWidth, m_impl->maxPenWidth); } w = mgMax(w, m_impl->minPenWidth); if (lineWidth <= 0 && xf().getDpiY() > getScreenDpi()) { w = w * xf().getDpiY() / getScreenDpi(); } return w; }
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 GiGraphics::calcPenWidth(float lineWidth, bool useViewScale) const { float w = mgMin(m_impl->minPenWidth, 1.f); if (m_impl->maxPenWidth <= 1) lineWidth = 0; if (lineWidth > 0) { // 单位:0.01mm w = lineWidth / 2540.f * xf().getDpiY(); if (useViewScale) w *= xf().getViewScale(); } else if (lineWidth < 0) { // 单位:像素 if (lineWidth < -1e3f) // 不使用UI放缩系数 w = 1e3f - lineWidth; else w = -lineWidth * _penWidthFactor; if (useViewScale) w *= xf().getViewScale(); } w = mgMin(w, m_impl->maxPenWidth); w = mgMax(w, m_impl->minPenWidth); //if (lineWidth <= 0 && xf().getDpiY() > getScreenDpi()) // w = w * xf().getDpiY() / getScreenDpi(); return w; }
// ConvertToBezierForm : // Given a point and a Bezier curve, generate a 5th-degree // Bezier-format equation whose solution finds the point on the // curve nearest the user-defined point. // Parameters : // pt: The point to find t for // pts: The control points // w: Ctl pts of 5th-degree curve, [W_DEGREE+1] // static void ConvertToBezierForm(const Point2d& pt, const Point2d* pts, Point2d* w) { int i, j, k, m, n, ub, lb; int row, column; // Table indices Vector2d c[DEGREE+1]; // pts(i)'s - pt Vector2d d[DEGREE]; // pts(i+1) - pts(i) float cdTable[3][4]; // Dot product of c, d const float z[3][4] = { // Precomputed "z" for cubics {1.0f, 0.6f, 0.3f, 0.1f}, {0.4f, 0.6f, 0.6f, 0.4f}, {0.1f, 0.3f, 0.6f, 1.0f}, }; // Determine the c's -- these are vectors created by subtracting // point pt from each of the control points for (i = 0; i <= DEGREE; i++) { c[i] = pts[i] - pt; } // Determine the d's -- these are vectors created by subtracting // each control point from the next for (i = 0; i <= DEGREE - 1; i++) { d[i] = 3.f * (pts[i+1] - pts[i]); } // Create the c,d table -- this is a table of dot products of the // c's and d's for (row = 0; row <= DEGREE - 1; row++) { for (column = 0; column <= DEGREE; column++) { cdTable[row][column] = d[row].dotProduct(c[column]); } } // Now, apply the z's to the dot products, on the skew diagonal // Also, set up the x-values, making these "points" for (i = 0; i <= W_DEGREE; i++) { w[i].y = 0.f; w[i].x = static_cast<float>(i) / W_DEGREE; } n = DEGREE; m = DEGREE-1; for (k = 0; k <= n + m; k++) { lb = mgMax(0, k - m); ub = mgMin(k, n); for (i = lb; i <= ub; i++) { j = k - i; w[i+j].y += cdTable[j][i] * z[j][i]; } } }
bool GiTransform::zoomByFactor(float factor, const Point2d* pxAt, bool adjust) { float scale = m_impl->viewScale; if (factor > 0) scale *= (1.f + fabsf(factor)); else scale /= (1.f + fabsf(factor)); if (adjust) { scale = mgMax(scale, m_impl->minViewScale); scale = mgMin(scale, m_impl->maxViewScale); } if (mgEquals(scale, m_impl->viewScale)) return false; return zoomScale(scale, pxAt, adjust); }
bool GiGraphics::drawPathWithArrayHead(const GiContext& ctx, MgPath& path, int startArray, int endArray) { float px = calcPenWidth(ctx.getLineWidth(), ctx.isAutoScale()); float scale = 0.5f * xf().getWorldToDisplayX() * (1 + mgMax(0.f, (px - 4.f) / 5)); if (startArray > 0 && startArray <= GiContext::kArrowOpenedCircle) { drawArrayHead(ctx, path, startArray, px, scale); } if (endArray > 0 && endArray <= GiContext::kArrowOpenedCircle) { path.reverse(); drawArrayHead(ctx, path, endArray, px, scale); path.reverse(); } return drawPath_(&ctx, path, false, Matrix2d::kIdentity()); }
bool MgCmdDrawRect::touchMoved(const MgMotion* sender) { Point2d pt1(m_startPt); Point2d pt2(snapPoint(sender)); MgBaseRect* shape = (MgBaseRect*)dynshape()->shape(); if (shape->getFlag(kMgSquare)) { float len = (float)mgMax(fabs(pt2.x - pt1.x), fabs(pt2.y - pt1.y)); Box2d rect(m_startPt, 2.f * len, 0); pt1 = rect.leftTop(); pt2 = rect.rightBottom(); } shape->setRect(pt1, pt2); dynshape()->shape()->update(); return _touchMoved(sender); }
bool GiTransform::zoomWnd(const Point2d& pt1, const Point2d& pt2, bool adjust) { // 计算开窗矩形的中心和宽高 Point2d ptCen ((pt2.x + pt1.x) * 0.5f, (pt2.y + pt1.y) * 0.5f); float w = fabs(static_cast<float>(pt2.x - pt1.x)); float h = fabs(static_cast<float>(pt2.y - pt1.y)); if (w < 4 || h < 4) return false; // 中心不变,扩大开窗矩形使得宽高比例和显示窗口相同 if (h * m_impl->cxWnd > w * m_impl->cyWnd) w = h * m_impl->cxWnd / m_impl->cyWnd; else h = w * m_impl->cyWnd / m_impl->cxWnd; // 计算放缩前矩形中心的世界坐标 Point2d ptW (ptCen * m_impl->matD2W); // 计算新显示比例 float scale = m_impl->viewScale * m_impl->cyWnd / h; if (!adjust && ScaleOutRange(scale, m_impl)) return false; scale = mgMax(scale, m_impl->minViewScale); scale = mgMin(scale, m_impl->maxViewScale); // 计算新显示比例下的显示窗口的世界坐标范围 float halfw = m_impl->cxWnd / (m_impl->w2dx / m_impl->viewScale * scale) * 0.5f; float halfh = m_impl->cyWnd / (m_impl->w2dy / m_impl->viewScale * scale) * 0.5f; Box2d box (ptW, 2 * halfw, 2 * halfh); // 检查显示窗口的新坐标范围是否在极限范围内 if (!m_impl->rectLimitsW.isEmpty() && !m_impl->rectLimitsW.contains(box)) { if (adjust) AdjustCenterW(ptW, halfw, halfh, m_impl->rectLimitsW); else return false; } // 改变显示比例和位置 return m_impl->zoomNoAdjust(ptW, scale); }
bool GiTransform::zoomScale(float viewScale, const Point2d* pxAt, bool adjust) { // 检查显示比例 if (!adjust && ScaleOutRange(viewScale, m_impl)) return false; viewScale = mgMax(viewScale, m_impl->minViewScale); viewScale = mgMin(viewScale, m_impl->maxViewScale); // 得到放缩中心点的客户区坐标 Point2d ptAt (m_impl->cxWnd * 0.5f, m_impl->cyWnd * 0.5f); if (pxAt != NULL) ptAt.set(pxAt->x, pxAt->y); // 得到放缩中心点在放缩前的世界坐标 Point2d ptAtW (ptAt * m_impl->matD2W); // 计算新显示比例下显示窗口中心的世界坐标 Point2d ptW; float w2dx = m_impl->w2dx / m_impl->viewScale * viewScale; float w2dy = m_impl->w2dy / m_impl->viewScale * viewScale; ptW.x = ptAtW.x + (m_impl->cxWnd * 0.5f - ptAt.x) / w2dx; ptW.y = ptAtW.y - (m_impl->cyWnd * 0.5f - ptAt.y) / w2dy; // 检查新显示比例下显示窗口的世界坐标范围是否在极限范围内 float halfw = m_impl->cxWnd / w2dx * 0.5f; float halfh = m_impl->cyWnd / w2dy * 0.5f; Box2d box (ptW, 2 * halfw, 2 * halfh); if (!AdjustCenterIn(adjust, box, m_impl->rectLimitsW, ptW, halfw, halfh)) { return false; } if (halfw - 3 > m_impl->rectLimitsW.width() / 2 && halfh - 3 > m_impl->rectLimitsW.height() / 2) // 显示比例太小 { viewScale *= mgMin(2 * (halfw - 3) / m_impl->rectLimitsW.width(), 2 * (halfh - 3) / m_impl->rectLimitsW.height()); viewScale = mgMin(viewScale, m_impl->maxViewScale); } return m_impl->zoomNoAdjust(ptW, viewScale); }
bool GiTransform::zoom(Point2d centerW, float viewScale, bool* changed) { viewScale = mgMax(viewScale, m_impl->minViewScale); viewScale = mgMin(viewScale, m_impl->maxViewScale); if (!m_impl->rectLimitsW.isEmpty()) { float halfw = m_impl->cxWnd / m_impl->w2dx * 0.5f; float halfh = m_impl->cyWnd / m_impl->w2dy * 0.5f; if (centerW.x - halfw < m_impl->rectLimitsW.xmin) centerW.x += m_impl->rectLimitsW.xmin - (centerW.x - halfw); if (centerW.x + halfw > m_impl->rectLimitsW.xmax) centerW.x += m_impl->rectLimitsW.xmax - (centerW.x + halfw); if (2 * halfw >= m_impl->rectLimitsW.width()) centerW.x = m_impl->rectLimitsW.center().x; if (centerW.y - halfh < m_impl->rectLimitsW.ymin) centerW.y += m_impl->rectLimitsW.ymin - (centerW.y - halfh); if (centerW.y + halfh > m_impl->rectLimitsW.ymax) centerW.y += m_impl->rectLimitsW.ymax - (centerW.y + halfh); if (2 * halfh >= m_impl->rectLimitsW.height()) centerW.y = m_impl->rectLimitsW.center().y; // 如果显示比例很小使得窗口超界,就放大显示 if (2 * halfw > m_impl->rectLimitsW.width() && 2 * halfh > m_impl->rectLimitsW.height()) { viewScale *= mgMin(2 * halfw / m_impl->rectLimitsW.width(), 2 * halfh / m_impl->rectLimitsW.height()); if (viewScale > m_impl->maxViewScale) viewScale = m_impl->maxViewScale; } } m_impl->zoomNoAdjust(centerW, viewScale, changed); return true; }
bool GiTransform::zoom(Point2d centerW, float viewScale) { bool changed = false; viewScale = mgMax(viewScale, m_impl->minViewScale); viewScale = mgMin(viewScale, m_impl->maxViewScale); Box2d rectW(m_impl->rectLimitsW); rectW.inflate(2); float halfw = m_impl->cxWnd / m_impl->w2dx * 0.5f; float halfh = m_impl->cyWnd / m_impl->w2dy * 0.5f; if (centerW.x - halfw < rectW.xmin) centerW.x += rectW.xmin - (centerW.x - halfw); if (centerW.x + halfw > rectW.xmax) centerW.x += rectW.xmax - (centerW.x + halfw); if (2 * halfw >= rectW.width()) centerW.x = rectW.center().x; if (centerW.y - halfh < rectW.ymin) centerW.y += rectW.ymin - (centerW.y - halfh); if (centerW.y + halfh > rectW.ymax) centerW.y += rectW.ymax - (centerW.y + halfh); if (2 * halfh >= rectW.height()) centerW.y = rectW.center().y; // 如果显示比例很小使得窗口超界,就放大显示 if (2 * halfw > rectW.width() && 2 * halfh > rectW.height()) { viewScale *= mgMin(2 * halfw / rectW.width(), 2 * halfh / rectW.height()); if (viewScale > m_impl->maxViewScale) viewScale = m_impl->maxViewScale; } m_impl->zoomNoAdjust(centerW, viewScale, &changed); return changed; }
bool GiGraphics::setPen(const GiContext* ctx) { bool changed = !(m_impl->ctxused & 1); if (m_impl->canvas) { if (ctx && (!mgEquals(ctx->getLineWidth(), m_impl->ctx.getLineWidth()) || ctx->isAutoScale() != m_impl->ctx.isAutoScale())) { m_impl->ctx.setLineWidth(ctx->getLineWidth(), ctx->isAutoScale()); changed = true; } if (ctx && !mgEquals(ctx->getExtraWidth(), m_impl->ctx.getExtraWidth())) { m_impl->ctx.setExtraWidth(ctx->getExtraWidth()); changed = true; } if (ctx && ctx->getLineColor() != m_impl->ctx.getLineColor()) { m_impl->ctx.setLineColor(ctx->getLineColor()); changed = true; } if (ctx && ctx->getLineStyleEx() != m_impl->ctx.getLineStyleEx()) { m_impl->ctx.setLineStyle(ctx->getLineStyleEx(), true); changed = true; } } ctx = &(m_impl->ctx); if (m_impl->canvas && changed) { m_impl->ctxused &= 1; float w = calcPenWidth(ctx->getLineWidth(), ctx->isAutoScale()); float orgw = ctx->getLineWidth(); orgw = (orgw < -0.1f && ctx->isAutoScale()) ? orgw - 1e4f : orgw; m_impl->canvas->setPen(calcPenColor(ctx->getLineColor()).getARGB(), w + ctx->getExtraWidth(), ctx->getLineStyleEx(), mgMax(m_impl->phase, 0.f), orgw); } return !ctx->isNullLine(); }
bool MgCmdDrawRect::touchEnded(const MgMotion* sender) { Point2d pt1(m_startPt); Point2d pt2(snapPoint(sender)); MgBaseRect* shape = (MgBaseRect*)dynshape()->shape(); if (shape->getFlag(kMgSquare)) { float len = (float)mgMax(fabs(pt2.x - pt1.x), fabs(pt2.y - pt1.y)); Box2d rect(m_startPt, 2.f * len, 0); pt1 = rect.leftTop(); pt2 = rect.rightBottom(); } shape->setRect(pt1, pt2); dynshape()->shape()->update(); float minDist = mgDisplayMmToModel(5, sender); if (shape->getWidth() > minDist && shape->getHeight() > minDist) { addRectShape(sender); } return _touchEnded(sender); }
bool GiTransform::zoomScale(float viewScale, const Point2d* pxAt, bool adjust) { // 检查显示比例 if (!adjust && ScaleOutRange(viewScale, m_impl)) return false; viewScale = mgMax(viewScale, m_impl->minViewScale); viewScale = mgMin(viewScale, m_impl->maxViewScale); // 得到放缩中心点的客户区坐标 Point2d ptAt (m_impl->cxWnd * 0.5f, m_impl->cyWnd * 0.5f); if (pxAt != NULL) ptAt.set(pxAt->x, pxAt->y); // 得到放缩中心点在放缩前的世界坐标 Point2d ptAtW (ptAt * m_impl->matD2W); // 计算新显示比例下显示窗口中心的世界坐标 Point2d ptW; float w2dx = m_impl->w2dx / m_impl->viewScale * viewScale; float w2dy = m_impl->w2dy / m_impl->viewScale * viewScale; ptW.x = ptAtW.x + (m_impl->cxWnd * 0.5f - ptAt.x) / w2dx; ptW.y = ptAtW.y - (m_impl->cyWnd * 0.5f - ptAt.y) / w2dy; // 检查新显示比例下显示窗口的世界坐标范围是否在极限范围内 float halfw = m_impl->cxWnd / w2dx * 0.5f; float halfh = m_impl->cyWnd / w2dy * 0.5f; Box2d box (ptW, 2 * halfw, 2 * halfh); if (!m_impl->rectLimitsW.isEmpty() && !m_impl->rectLimitsW.contains(box)) { if (adjust) AdjustCenterW(ptW, halfw, halfh, m_impl->rectLimitsW); else return false; } return m_impl->zoomNoAdjust(ptW, viewScale); }
bool MgBaseRect::isEmpty(float minDist) const { return _points[2].distanceTo(_points[0]) < mgMax(minDist, _MGZERO); }
// ControlPolygonFlatEnough : // Check if the control polygon of a Bezier curve is flat enough // for recursive subdivision to bottom out. // Parameters : // pts: Control pts // degree: Degree of bezier curve // static int ControlPolygonFlatEnough(const Point2d* pts, int degree) { int i; // Index variable float distance[W_DEGREE+1]; // Distances from pts to line float max_distance_above; // maximum of these float max_distance_below; float error; // Precision of root float intercept_1, intercept_2, left_intercept, right_intercept; float a, b, c; // Coefficients of implicit eqn for line from pts[0]-pts[deg] // Find the perpendicular distance // from each interior control point to // line connecting pts[0] and pts[degree] { float abSquared; // Derive the implicit equation for line connecting first *' // and last control points a = pts[0].y - pts[degree].y; b = pts[degree].x - pts[0].x; c = pts[0].x * pts[degree].y - pts[degree].x * pts[0].y; abSquared = (a * a) + (b * b); for (i = 1; i < degree; i++) { // Compute distance from each of the points to that line distance[i] = a * pts[i].x + b * pts[i].y + c; if (distance[i] > 0.0) { distance[i] = (distance[i] * distance[i]) / abSquared; } if (distance[i] < 0.0) { distance[i] = -((distance[i] * distance[i]) / abSquared); } } } // Find the largest distance max_distance_above = 0.0; max_distance_below = 0.0; for (i = 1; i < degree; i++) { if (distance[i] < 0.0) { max_distance_below = mgMin(max_distance_below, distance[i]); }; if (distance[i] > 0.0) { max_distance_above = mgMax(max_distance_above, distance[i]); } } { float det, dInv; float a1, b1, c1, a2, b2, c2; // Implicit equation for zero line a1 = 0.0; b1 = 1.0; c1 = 0.0; // Implicit equation for "above" line a2 = a; b2 = b; c2 = c + max_distance_above; det = a1 * b2 - a2 * b1; dInv = 1 / det; intercept_1 = (b1 * c2 - b2 * c1) * dInv; // Implicit equation for "below" line a2 = a; b2 = b; c2 = c + max_distance_below; det = a1 * b2 - a2 * b1; dInv = 1 / det; intercept_2 = (b1 * c2 - b2 * c1) * dInv; } // Compute intercepts of bounding box left_intercept = mgMin(intercept_1, intercept_2); right_intercept = mgMax(intercept_1, intercept_2); error = 0.5f * (right_intercept-left_intercept); if (error < _MGZERO) { return 1; } else { return 0; } }
void makeLinePattern(CGFloat* dest, const CGFloat* src, int n, float w) { for (int i = 0; i < n; i++) { dest[i] = src[i] * mgMax(w, 1.f); } }
bool GiTransform::zoomTo(const Box2d& rectWorld, const RECT_2D* rcTo, bool adjust) { // 如果图形范围的宽或高接近于零,就返回 if (rectWorld.isEmpty()) return false; // 计算像素到毫米的比例 const float d2mmX = m_impl->viewScale / m_impl->w2dx; const float d2mmY = m_impl->viewScale / m_impl->w2dy; // 计算目标窗口区域(毫米) float w = 0, h = 0; Point2d ptCen; if (rcTo != NULL) { w = fabsf(static_cast<float>(rcTo->right - rcTo->left)); h = fabsf(static_cast<float>(rcTo->bottom - rcTo->top)); ptCen.x = (rcTo->left + rcTo->right) * 0.5f; ptCen.y = (rcTo->top + rcTo->bottom) * 0.5f; } if (w < 4 || h < 4) { w = (float)m_impl->cxWnd; h = (float)m_impl->cyWnd; ptCen.set(w * 0.5f, h * 0.5f); w -= 8; h -= 8; } if (w < 4 || h < 4) return false; w *= d2mmX; h *= d2mmY; ptCen.scaleBy(d2mmX, d2mmY); // 计算新显示比例 (中心不变,缩小窗口区域使得宽高比例和图形范围相同) float scale; if (h * rectWorld.width() > w * rectWorld.height()) { //h = w * rectWorld.height() / rectWorld.width(); scale = w / rectWorld.width(); } else { //w = h * rectWorld.width() / rectWorld.height(); scale = h / rectWorld.height(); } // 检查显示比例 if (!adjust && ScaleOutRange(scale, m_impl)) return false; scale = mgMax(scale, m_impl->minViewScale); scale = mgMin(scale, m_impl->maxViewScale); // 计算在新显示比例下显示窗口中心的世界坐标 Point2d ptW; ptW.x = rectWorld.center().x + (m_impl->cxWnd * d2mmX * 0.5f - ptCen.x) / scale; ptW.y = rectWorld.center().y - (m_impl->cyWnd * d2mmY * 0.5f - ptCen.y) / scale; // 检查新显示比例下显示窗口的世界坐标范围是否在极限范围内 float halfw = m_impl->cxWnd * d2mmX / scale * 0.5f; float halfh = m_impl->cyWnd * d2mmY / scale * 0.5f; Box2d box (ptW, 2 * halfw, 2 * halfh); if (!AdjustCenterIn(adjust, box, m_impl->rectLimitsW, ptW, halfw, halfh)) { return false; } return m_impl->zoomNoAdjust(ptW, scale); }