ofPolyline ofGetResampledSpacing(const ofPolyline& polyline, float spacing) { ofPolyline result; // if more properties are added to ofPolyline, we need to copy them here result.setClosed(polyline.getClosed()); float totalLength = 0; int curStep = 0; int lastPosition = polyline.size() - 1; if(polyline.getClosed()) { lastPosition++; } for(int i = 0; i < lastPosition; i++) { bool repeatNext = i == (int) (polyline.size() - 1); const ofPoint& cur = polyline[i]; const ofPoint& next = repeatNext ? polyline[0] : polyline[i + 1]; ofPoint diff = next - cur; float curSegmentLength = diff.length(); totalLength += curSegmentLength; while(curStep * spacing <= totalLength) { float curSample = curStep * spacing; float curLength = curSample - (totalLength - curSegmentLength); float relativeSample = curLength / curSegmentLength; result.addVertex(cur.getInterpolated(next, relativeSample)); curStep++; } } return result; }
ofPolyline ofGetSmoothed(const ofPolyline& polyline, int smoothingSize, float smoothingShape) { ofPolyline result = polyline; if(!polyline.getClosed()) { ofLog( OF_LOG_ERROR, "ofSmooth() currently only supports closed ofPolylines." ); return polyline; } // precompute weights and normalization vector<float> weights; float weightSum = 0; weights.push_back(1); // center weight // side weights for(int i = 1; i <= smoothingSize; i++) { float curWeight = ofMap(i, 0, smoothingSize, 1, smoothingShape); weights.push_back(curWeight); weightSum += curWeight; } float weightNormalization = 1 / (1 + 2 * weightSum); // use weights to make weighted averages of neighbors int n = polyline.size(); for(int i = 0; i < n; i++) { for(int j = 1; j <= smoothingSize; j++) { int leftPosition = (n + i - j) % n; int rightPosition = (i + j) % n; const ofPoint& left = polyline[leftPosition]; const ofPoint& right = polyline[rightPosition]; result[i] += (left + right) * weights[j]; } result[i] *= weightNormalization; } return result; }
// a much faster but less accurate version would check distances to vertices first, // which assumes vertices are evenly spaced ofPoint ofGetClosestPoint(const ofPolyline& polyline, const ofPoint& target, unsigned int* nearestIndex) { if(polyline.size() < 2) { if(nearestIndex != NULL) { nearestIndex = 0; } return target; } float distance = 0; ofPoint nearestPoint; unsigned int nearest = 0; float normalizedPosition = 0; unsigned int lastPosition = polyline.size() - 1; if(polyline.getClosed()) { lastPosition++; } for(int i = 0; i < (int) lastPosition; i++) { bool repeatNext = i == (int) (polyline.size() - 1); const ofPoint& cur = polyline[i]; const ofPoint& next = repeatNext ? polyline[0] : polyline[i + 1]; float curNormalizedPosition = 0; ofPoint curNearestPoint = ofGetClosestPoint(cur, next, target, &curNormalizedPosition); float curDistance = curNearestPoint.distance(target); if(i == 0 || curDistance < distance) { distance = curDistance; nearest = i; nearestPoint = curNearestPoint; normalizedPosition = curNormalizedPosition; } } if(nearestIndex != NULL) { if(normalizedPosition > .5) { nearest++; if(nearest == polyline.size()) { nearest = 0; } } *nearestIndex = nearest; } return nearestPoint; }