////////////////// // GradientLayerMesh GradientLayerMesh::GradientLayerMesh(const Rect &rect,Vec2 axisStart,Vec2 axisEnd,const Gradient *gradient) { ConvexPolygon rectPol = { rect.GetCorner(0), rect.GetCorner(2), rect.GetCorner(3), rect.GetCorner(1), }; std::vector<float> slicePositions; for(int i = 0;i < gradient->GetNumVertices();i++) { slicePositions.push_back(gradient->GetVertexPos(i)); } Vec2 axis = axisEnd - axisStart; axis /= axis.GetSqrLen(); float axisOffset = -Dot(axis,axisStart); std::vector<ParallelSlice> slices = rectPol.GetParallelSlices( axisStart,axisEnd,slicePositions.data(),(int)slicePositions.size()); for(const ParallelSlice &slice : slices) { int seg = slice.GetSegment(); if(seg == 0) { std::vector<Triangle2D> tris = slice.GetPolygon().Triangulate(); for(const Triangle2D &tri : tris) { mVertices.push_back(tri.mVertices[0]); mVertices.push_back(tri.mVertices[1]); mVertices.push_back(tri.mVertices[2]); mColors.push_back(MakeVec4(1,0,0,1)); mColors.push_back(MakeVec4(0,1,0,1)); mColors.push_back(MakeVec4(0,0,1,1)); } } else if(seg == gradient->GetNumVertices()) { std::vector<Triangle2D> tris = slice.GetPolygon().Triangulate(); for(const Triangle2D &tri : tris) { mVertices.push_back(tri.mVertices[0]); mVertices.push_back(tri.mVertices[1]); mVertices.push_back(tri.mVertices[2]); mColors.push_back(MakeVec4(1,0,0,1)); mColors.push_back(MakeVec4(0,1,0,1)); mColors.push_back(MakeVec4(0,0,1,1)); } } else { std::vector<Triangle2D> tris = slice.GetPolygon().Triangulate(); for(const Triangle2D &tri : tris) { for(int i = 0;i < 3;i++) { mVertices.push_back(tri.mVertices[i]); float t = Dot(axis,tri.mVertices[i]) + axisOffset; float segT = (t - slicePositions[seg - 1]) / (slicePositions[seg] - slicePositions[seg - 1]); Vec4 color = gradient->GetVertexColor(seg - 1) * (1 - segT) + gradient->GetVertexColor(seg) * segT; mColors.push_back(color); } } } } }
std::vector<ParallelSlice> ConvexPolygon::GetParallelSlices(const Vec2 &min,const Vec2 &max, const float *slicePositions,int numSlicePositions) const { int numVerts = GetNumVertices(); const Vec2 *vertices = GetVertices(); assert((max - min).GetSqrLen() > .001f); assert(std::is_sorted(slicePositions,slicePositions + numSlicePositions)); Vec2 axis = max - min; axis /= axis.GetSqrLen(); float axisOrigin = Dot(axis,min); std::vector<float> vertOnAxis(numVerts); for(int i = 0;i < numVerts;i++) vertOnAxis[i] = Dot(axis,vertices[i]) - axisOrigin; int minVert = (int)(std::min_element(vertOnAxis.begin(),vertOnAxis.end()) - vertOnAxis.begin()); int maxVert = (int)(std::max_element(vertOnAxis.begin(),vertOnAxis.end()) - vertOnAxis.begin()); #ifndef NDEBUG for(int i = 0;i < numVerts;i++) { assert(vertOnAxis[i] >= vertOnAxis[minVert]); assert(vertOnAxis[i] <= vertOnAxis[maxVert]); } #endif // Segments are indexed so that their index matches the index of the high slice. // minSeg and maxSeg describes the range [minSeg,maxSeg] of intervals which contain a part // of the polygon in their interior. minSeg is thus the index of the first slicePosition // that is strictly greater than minPos, and maxSeg the index of the first slicePosition // that is greater than or equal to maxPos int minSeg = numSlicePositions; int maxSeg = numSlicePositions; for(int i = 0;i < numSlicePositions;i++) { if(vertOnAxis[minVert] < slicePositions[i]) { minSeg = i; break; } } for(int i = minSeg;i < numSlicePositions;i++) { if(vertOnAxis[maxVert] <= slicePositions[i]) { maxSeg = i; break; } } assert(minSeg == 0 || vertOnAxis[minVert] >= slicePositions[minSeg - 1]); assert(minSeg == numSlicePositions || vertOnAxis[minVert] < slicePositions[minSeg]); assert(maxSeg == 0 || vertOnAxis[maxVert] > slicePositions[maxSeg - 1]); assert(maxSeg == numSlicePositions || vertOnAxis[maxVert] <= slicePositions[maxSeg]); // index of the vertex at the beginning of the current counter clockwise/clockwise edge respectively. int ccwPrevVert = minVert; int cwPrevVert = minVert; // index of the vertex at the end of the current counter clockwise/clockwise edge respectively. int ccwVert = (minVert + 1) % numVerts; int cwVert = (minVert - 1 + numVerts) % numVerts; std::vector<ParallelSlice> ret; std::vector<Vec2> nextPol; nextPol.push_back(vertices[minVert]); for(int seg = minSeg;seg < maxSeg;seg++) { std::vector<Vec2> curPol; curPol.swap(nextPol); while(true) { float curPos = vertOnAxis[ccwVert]; if(curPos < slicePositions[seg]) { curPol.push_back(vertices[ccwVert]); ccwPrevVert = ccwVert; ccwVert = (ccwVert + 1) % numVerts; } else if(curPos > slicePositions[seg]) { float prevPos = vertOnAxis[ccwPrevVert]; float t = (slicePositions[seg] - prevPos) / (curPos - prevPos); Vec2 newVert = vertices[ccwPrevVert] + t * (vertices[ccwVert] - vertices[ccwPrevVert]); curPol.push_back(newVert); nextPol.push_back(newVert); break; } else //if(pos == slicePositions[seg]) { curPol.push_back(vertices[ccwVert]); nextPol.push_back(vertices[ccwVert]); ccwPrevVert = ccwVert; ccwVert = (ccwVert + 1) % numVerts; break; } } int firstClockWise = (int)curPol.size(); while(true) { float curPos = vertOnAxis[cwVert]; if(curPos < slicePositions[seg]) { curPol.push_back(vertices[cwVert]); cwPrevVert = cwVert; cwVert = (cwVert + numVerts - 1) % numVerts; } else if(curPos > slicePositions[seg]) { float prevPos = vertOnAxis[cwPrevVert]; float t = (slicePositions[seg] - prevPos) / (curPos - prevPos); Vec2 newVert = vertices[cwPrevVert] + t * (vertices[cwVert] - vertices[cwPrevVert]); curPol.push_back(newVert); nextPol.push_back(newVert); break; } else //if(pos == slicePositions[seg]) { curPol.push_back(vertices[cwVert]); nextPol.push_back(vertices[cwVert]); cwPrevVert = cwVert; cwVert = (cwVert + numVerts - 1) % numVerts; break; } } std::reverse(curPol.begin() + firstClockWise,curPol.end()); std::reverse(nextPol.begin(), nextPol.end()); ret.push_back(ParallelSlice(ConvexPolygon(curPol),seg)); } // special case for the last segment, where, among other things, we may not // have a value for slicePositions[seg] (may be outside its bounds). { std::vector<Vec2> lastPol = nextPol; while(ccwVert != cwVert) { lastPol.push_back(vertices[ccwVert]); ccwVert = (ccwVert + 1) % numVerts; } lastPol.push_back(vertices[cwVert]); ret.push_back(ParallelSlice(ConvexPolygon(lastPol),maxSeg)); } return ret; }