bool nuiSpline::Tessellate(nuiPath& rVertices, float Quality) const { if (mCacheValid && mLastQuality == Quality) { uint32 count = mTessCache.GetCount(); for (uint32 i = 0; i < count; i++) rVertices.AddVertex(mTessCache[i]); } else { std::vector<nuiSplinePoint> Points; Evaluate(Points, (uint)(GetSize() * (20.0f * Quality))); std::vector<nuiSplinePoint>::iterator it; std::vector<nuiSplinePoint>::iterator end = Points.end(); for (it = Points.begin(); it != end; ++it) { rVertices.AddVertex(*it); mTessCache.AddVertex(*it); } mLastQuality = Quality; mCacheValid = true; } return true; }
void nuiOutliner::AddCap(const nuiPoint& rFirstPoint, const nuiPoint& rLastPoint, nuiPath& rPoints) const { switch (mLineCap) { case nuiLineCapBut: // Nothing special to do rPoints.AddVertexOptim(rFirstPoint); rPoints.AddVertexOptim(rLastPoint); break; case nuiLineCapRound: { nuiArc arc(rFirstPoint, rLastPoint, mRealLineWidth, mRealLineWidth, 0, true, true); arc.Tessellate(rPoints, 0.5f); //rPoints.AddVertexOptim(rFirstPoint); } break; case nuiLineCapSquare: { nuiVector vec = rLastPoint - rFirstPoint; vec.Normalize(); float tmp = vec[0]; vec[0] = vec[1]; vec[1] = -tmp; vec *= mLineWidth * 0.5f; rPoints.AddVertexOptim(nuiPoint(rFirstPoint+vec)); rPoints.AddVertexOptim(nuiPoint(rLastPoint+vec)); } break; } }
void nuiOutliner::AddJoin(const nuiPoint& Point0, const nuiPoint& Point1, const nuiPoint& Point2, const nuiPoint& Point3, nuiPath& rPoints) const { const double x1 = Point0[0]; const double y1 = Point0[1]; const double x2 = Point1[0]; const double y2 = Point1[1]; const double x3 = Point2[0]; const double y3 = Point2[1]; const double x4 = Point3[0]; const double y4 = Point3[1]; const double x43 = x4 - x3; const double y43 = y4 - y3; const double x21 = x2 - x1; const double y21 = y2 - y1; const double denom = y43 * x21 - x43 * y21; if (denom == 0.0f) { // The segments are parallel. rPoints.AddVertexOptim(Point3); } else { const double y13 = y1 - y3; const double x13 = x1 - x3; const double denom_inv = 1.0f/denom; const double ua = (x43 * y13 - y43 * x13) * denom_inv; const double ub = (x21 * y13 - y21 * x13) * denom_inv; if (mLineJoin == nuiLineJoinMiter || (ua >= 0.0f && ua <= 1.0f) && (ub >= 0.0f && ub <= 1.0f)) { nuiPoint mitter((float)(x1 + ua*(x2-x1)), (float)(y1 + ua*(y2-y1))); rPoints.AddVertexOptim(mitter); } else if (mLineJoin == nuiLineJoinRound) { nuiArc arc(Point1, Point2, mRealLineWidth, mRealLineWidth, 0, false, true); arc.Tessellate(rPoints, 0.5f); } else // Bevel { rPoints.AddVertexOptim(Point1); rPoints.AddVertexOptim(Point2); } } }
bool nuiOutliner::Tessellate(nuiPath& rPoints, float Quality) const { nuiPath Vertices; bool res = mpPath->Tessellate(Vertices, Quality); rPoints.Clear(); if (!res) return false; uint total = Vertices.GetCount(); uint offset = 0; uint count = total; //Find sub path: for (uint i = offset; i < total; i++) { uint ii = i+1; if (Vertices[i].GetType() == nuiPointTypeStop || ii == total) { count = ii - offset; if (Vertices[i].GetType() == nuiPointTypeStop) count--; Tessellate(rPoints, Vertices, offset, count, Quality); offset = ii; } } return res; }
bool nuiArc::Tessellate(nuiPath& rVertices, float Quality) const { // I shamelessly stole this code from svgl double x1, y1, x2, y2; x1 = mStartVertex.Elt[0]; y1 = mStartVertex.Elt[1]; x2 = mStopVertex.Elt[0]; y2 = mStopVertex.Elt[1]; double angle = mAngle; double xr, yr; xr = mXRadius; yr = mYRadius; rVertices.AddVertexOptim(nuiPoint((float)x1, (float)y1)); if ((x1 == x2) && (y1 == y2) && !mLargeArc) { return true; } // be sure mXRadius or mYRadius are non-zero if( mXRadius == 0 || mYRadius == 0) { rVertices.AddVertexOptim(nuiPoint((float)x2, (float)y2)); return true; } // make sure mXRadius and mYRadius are positive if(xr < 0) xr = -xr; if(yr < 0) yr = -yr; const double cosa = cos(angle * M_PI / 180.0); const double sina = sin(angle * M_PI / 180.0); // compute the center (see svg.pdf) const double xp1 = cosa * (x1 - x2) / 2.0 + sina * (y1 - y2) / 2.f; const double yp1 = -sina * (x1 - x2) / 2.0 + cosa * (y1 - y2) / 2.f; double rx2 = xr * xr, ry2 = yr * yr, xp12 = xp1 * xp1, yp12 = yp1 * yp1; // make sure xr and yr are large enough { double tmp = xp12 / rx2 + yp12 / ry2; if (tmp > 1) { rx2 *= tmp; ry2 *= tmp; tmp = sqrt(tmp); xr *= tmp; yr *= tmp; } } double fact = ( rx2 * ry2 / (rx2 * yp12 + ry2 * xp12)) - 1; if (fact < 0) { fact = 0; } fact = sqrt(fact); if (mLargeArc == mSweep) fact = -fact; double xpc = xr * yp1 / yr; double ypc = - yr * xp1 / xr; xpc *= fact; ypc *= fact; double xc = xpc * cosa - ypc * sina + (x1 + x2) / 2.f; double yc = xpc * sina + ypc * cosa + (y1 + y2) / 2.f; // determine t1 and t2, limits given by (x1, y1) (x2, y2) double t1; double deltat; // double t2; { double xv1 = (xp1 - xpc) / xr; double yv1 = (yp1 - ypc) / yr; double norm1 = xv1 * xv1 + yv1 * yv1; double cosangle1 = xv1 / sqrt(norm1); if (cosangle1 < -1.0f) cosangle1 = -1.0f; if (cosangle1 > 1.0f) cosangle1 = 1.0f; t1 = acos(cosangle1); if (yv1 < 0) t1 = -t1; double xv2 = (-xp1 - xpc) / xr; double yv2 = (-yp1 - ypc) / yr; double norm2 = xv2 * xv2 + yv2 * yv2; deltat = xv1 * xv2 + yv1 * yv2; deltat = deltat / sqrt(norm1 * norm2); if (deltat < -1.0) deltat = -1.0; if (deltat > 1.0) deltat = 1.0; deltat = acos(deltat); if ( (xv1 * yv2 - yv1 * xv2) < 0) deltat = -deltat; if (!mSweep && (deltat > 0)) deltat -= 2.0 * M_PI; if (mSweep && (deltat < 0)) deltat += 2.0 * M_PI; } // compute vertices /* fast incremental cos and sin generation cos(a+dt) = cos(a) cos(dt) - sin(a) sin(dt); sin(a+dt) = cos(a) sin(dt) + cos(dt) sin(a); <=> cos(a+2*dt) = cos(a + dt) cos(dt) - sin(a + dt) sin(dt) sin(a+2*dt) = cos(a + dt) sin(dt) + cos(a + dt) sin(a) */ const int nb = ToBelow(M_PI * (xr + yr) * Quality); const double dt = deltat / nb; const double cosdt = cos(dt); const double sindt = sin(dt); double cost1_plus_ndt = cos(t1 + dt); double sint1_plus_ndt = sin(t1 + dt); for (int i = 0; i < nb; ++i) { /* ellipsoid: x(t) = xr.cos(t) y(t) = yr.cos(t) */ double x = xr * cost1_plus_ndt; double y = yr * sint1_plus_ndt; { // compute cos and sin double tmp = cost1_plus_ndt * cosdt - sint1_plus_ndt * sindt; sint1_plus_ndt = cost1_plus_ndt * sindt + sint1_plus_ndt * cosdt; cost1_plus_ndt = tmp; } // rotate // angle = -angle, I don't know why... { double tmp = x * cosa - y * sina; y = x * sina + y * cosa; x = tmp; } // translate x += xc; y += yc; rVertices.AddVertexOptim(nuiPoint((float)x, (float)y)); } return true; }
void nuiOutliner::Tessellate(nuiPath& rPoints, const nuiPath& rVertices, uint offset, int count, float Quality) const { nuiPath Left; nuiPath Right; NGL_ASSERT(count); const nuiPoint& rstart = rVertices[offset]; const nuiPoint& rstop = rVertices[offset + count - 1]; bool closed = rstart == rstop; int segments = count; if (!closed) segments--; int i; // Create four outlined vertex per segment: for (i = 0; i < segments; i++) { int p1 = i; int p2 = (p1+1); p1 += offset; p2 += offset; const nuiPoint& rPoint = rVertices[p1]; const nuiPoint& rNextPoint = rVertices[p2]; if (!(rPoint == rNextPoint)) { nuiVector vec(rNextPoint - rPoint); vec.Normalize(); vec *= mRealLineWidth; float tmp = vec[0]; vec[0] = vec[1]; vec[1] = -tmp; Left.AddVertexOptim(nuiPoint(rPoint + vec)); Left.AddVertexOptim(nuiPoint(rNextPoint + vec)); Right.AddVertexOptim(nuiPoint(rPoint - vec)); Right.AddVertexOptim(nuiPoint(rNextPoint - vec)); } } Right.Reverse(); if (!Left.GetCount() || !Right.GetCount()) // Case of a path where all points have the same coordinates { switch (mLineCap) { case nuiLineCapBut: // The result is empty. break; case nuiLineCapRound: { const double CIRCLE_FACTOR = (1.0/3.5); const double X = rstart[0]; const double Y = rstart[1]; uint count = ToAbove((double)(2.0 * M_PI * (double)mRealLineWidth * (double)CIRCLE_FACTOR)); float step = 2.0f * (float)M_PI / (float)count; for (uint i = 0; i <= count; i++) { float angle = step * (float) i; float x = (float)(X + sin(angle) * mRealLineWidth); float y = (float)(Y + cos(angle) * mRealLineWidth); rPoints.AddVertexOptim(nuiPoint(x, y, 0)); } } break; case nuiLineCapSquare: { const float x = rstart[0]; const float y = rstart[1]; rPoints.AddVertexOptim(nuiPoint(x - mRealLineWidth, y - mRealLineWidth)); rPoints.AddVertexOptim(nuiPoint(x + mRealLineWidth, y - mRealLineWidth)); rPoints.AddVertexOptim(nuiPoint(x + mRealLineWidth, y + mRealLineWidth)); rPoints.AddVertexOptim(nuiPoint(x - mRealLineWidth, y + mRealLineWidth)); rPoints.AddVertexOptim(nuiPoint(x - mRealLineWidth, y - mRealLineWidth)); } break; } } else if (closed && Left.GetCount() >= 2 && Right.GetCount() >= 2) // Case of a closed path (make sure that is at least a segment). { // Left: int ndx1 = 0; int ndx2 = 0; for (i = 0; i < segments-1; i++) { ndx1 = i * 2; ndx2 = ndx1 + 2; AddJoin(Left[ndx1], Left[ndx1+1], Left[ndx2], Left[ndx2+1], rPoints); } if (mLineJoin == nuiLineJoinBevel) rPoints.AddVertexOptim(rPoints.Front()); else rPoints.Back() = rPoints.Front(); rPoints.StopPath(); // Right: for (i = 0; i < segments; i++) { ndx1 = i * 2; ndx2 = ndx1 + 2; AddJoin(Right[ndx1], Right[ndx1+1], Right[ndx2], Right[ndx2+1], rPoints); } rPoints.StopPath(); } else { NGL_ASSERT(!Left.IsEmpty()); NGL_ASSERT(!Right.IsEmpty()); int cnt = segments - 1; // Left: rPoints.AddVertexOptim(Left.Front()); int ndx1 = 0; int ndx2 = 0; for (i = 0; i < cnt; i++) { ndx1 = i * 2; ndx2 = ndx1 + 2; AddJoin(Left[ndx1], Left[ndx1+1], Left[ndx2], Left[ndx2+1], rPoints); } AddCap(Left.Back(), Right.Front(), rPoints); // Right: for (i = 0; i < cnt; i++) { ndx1 = i * 2; ndx2 = ndx1 + 2; AddJoin(Right[ndx1], Right[ndx1+1], Right[ndx2], Right[ndx2+1], rPoints); } AddCap(Right.Back(), Left.Front(), rPoints); rPoints.Front() = rPoints.Back(); rPoints.StopPath(); } }
void nuiPath::AddPathOptim(const nuiPath& rPath) { uint toadd = rPath.GetCount(); for (uint i = 0; i < toadd; i++) AddVertexOptim(rPath[i]); }