// TODO: this serves a similar purpose to SplitLine above, but is more general. Also, SplitLine seems to be implemented more // efficiently, might be nice to take some cues from it void SimRender::SubdividePoints(std::vector<CVector2D>& points, float maxSegmentLength, bool closed) { size_t numControlPoints = points.size(); if (numControlPoints < 2) return; ENSURE(maxSegmentLength > 0); size_t endIndex = numControlPoints; if (!closed && numControlPoints > 2) endIndex--; std::vector<CVector2D> newPoints; for (size_t i = 0; i < endIndex; i++) { const CVector2D& curPoint = points[i]; const CVector2D& nextPoint = points[(i+1) % numControlPoints]; const CVector2D line(nextPoint - curPoint); CVector2D lineDirection = line.Normalized(); // include control point i + a list of intermediate points between i and i + 1 (excluding i+1 itself) newPoints.push_back(curPoint); // calculate how many intermediate points are needed so that each segment is of length <= maxSegmentLength float lineLength = line.Length(); size_t numSegments = (size_t) ceilf(lineLength / maxSegmentLength); float segmentLength = lineLength / numSegments; for (size_t s = 1; s < numSegments; ++s) // start at one, we already included curPoint { newPoints.push_back(curPoint + lineDirection * (s * segmentLength)); } } points.swap(newPoints); }
void SimRender::ConstructDashedLine(const std::vector<CVector2D>& keyPoints, SDashedLine& dashedLineOut, const float dashLength, const float blankLength) { // sanity checks if (dashLength <= 0) return; if (blankLength <= 0) return; if (keyPoints.size() < 2) return; dashedLineOut.m_Points.clear(); dashedLineOut.m_StartIndices.clear(); // walk the line, counting the total length so far at each node point. When the length exceeds dashLength, cut the last segment at the // required length and continue for blankLength along the line to start a new dash segment. // TODO: we should probably extend this function to also allow for closed lines. I was thinking of slightly scaling the dash/blank length // so that it fits the length of the curve, but that requires knowing the length of the curve upfront which is sort of expensive to compute // (O(n) and lots of square roots). bool buildingDash = true; // true if we're building a dash, false if a blank float curDashLength = 0; // builds up the current dash/blank's length as we walk through the line nodes CVector2D dashLastPoint = keyPoints[0]; // last point of the current dash/blank being built. // register the first starting node of the first dash dashedLineOut.m_Points.push_back(keyPoints[0]); dashedLineOut.m_StartIndices.push_back(0); // index of the next key point on the path. Must always point to a node that is further along the path than dashLastPoint, so we can // properly take a direction vector along the path. size_t i = 0; while(i < keyPoints.size() - 1) { // get length of this segment CVector2D segmentVector = keyPoints[i + 1] - dashLastPoint; // vector from our current point along the path to nextNode float segmentLength = segmentVector.Length(); float targetLength = (buildingDash ? dashLength : blankLength); if (curDashLength + segmentLength > targetLength) { // segment is longer than the dash length we still have to go, so we'll need to cut it; create a cut point along the segment // line that is of just the required length to complete the dash, then make it the base point for the next dash/blank. float cutLength = targetLength - curDashLength; CVector2D cutPoint = dashLastPoint + (segmentVector.Normalized() * cutLength); // start a new dash or blank in the next iteration curDashLength = 0; buildingDash = !buildingDash; // flip from dash to blank and vice-versa dashLastPoint = cutPoint; // don't increment i, we haven't fully traversed this segment yet so we still need to use the same point to take the // direction vector with in the next iteration // this cut point is either the end of the current dash or the beginning of a new dash; either way, we're gonna need it // in the points array. dashedLineOut.m_Points.push_back(cutPoint); if (buildingDash) { // if we're gonna be building a new dash, then cutPoint is now the base point of that new dash, so let's register its // index as a start index of a dash. dashedLineOut.m_StartIndices.push_back(dashedLineOut.m_Points.size() - 1); } } else { // the segment from lastDashPoint to keyPoints[i+1] doesn't suffice to complete the dash, so we need to add keyPoints[i+1] // to this dash's points and continue from there if (buildingDash) // still building the dash, add it to the output (we don't need to store the blanks) dashedLineOut.m_Points.push_back(keyPoints[i+1]); curDashLength += segmentLength; dashLastPoint = keyPoints[i+1]; i++; } } }