framework2
1252 строки · 37.1 Кб
1#ifndef OF_POLYLINE_H2#include "ofPolyline.h"3#endif4
5#include "ofRectangle.h"6#include "ofGraphicsBaseTypes.h"7#include "ofVectorMath.h"8#include "ofAppRunner.h"9#include "ofMath.h"10#include "ofLog.h"11#include "ofConstants.h"12
13//----------------------------------------------------------
14template<class T>15ofPolyline_<T>::ofPolyline_(){16setRightVector();17clear();18}
19
20//----------------------------------------------------------
21template<class T>22ofPolyline_<T>::ofPolyline_(const std::vector<T>& verts){23setRightVector();24clear();25addVertices(verts);26}
27
28//----------------------------------------------------------
29template<class T>30ofPolyline_<T> ofPolyline_<T>::fromRectangle(const ofRectangle& rect) {31ofPolyline_ polyline;32polyline.addVertex(rect.getMin());33polyline.addVertex(rect.getMaxX(),rect.getMinY());34polyline.addVertex(rect.getMax());35polyline.addVertex(rect.getMinX(),rect.getMaxY());36polyline.close();37return polyline;38}
39
40//----------------------------------------------------------
41template<class T>42void ofPolyline_<T>::clear() {43setClosed(false);44points.clear();45curveVertices.clear();46flagHasChanged();47}
48
49//----------------------------------------------------------
50template<class T>51void ofPolyline_<T>::addVertex(const T& p) {52curveVertices.clear();53points.push_back(p);54flagHasChanged();55}
56
57//----------------------------------------------------------
58template<class T>59void ofPolyline_<T>::addVertex(float x, float y, float z) {60curveVertices.clear();61addVertex(T(x,y,z));62flagHasChanged();63}
64
65//----------------------------------------------------------
66template<class T>67void ofPolyline_<T>::addVertices(const std::vector<T>& verts) {68curveVertices.clear();69points.insert( points.end(), verts.begin(), verts.end() );70flagHasChanged();71}
72
73//----------------------------------------------------------
74template<class T>75void ofPolyline_<T>::addVertices(const T* verts, int numverts) {76curveVertices.clear();77points.insert( points.end(), verts, verts + numverts );78flagHasChanged();79}
80
81//----------------------------------------------------------
82template<class T>83void ofPolyline_<T>::insertVertex(const T &p, int index) {84curveVertices.clear();85points.insert(points.begin()+index, p);86flagHasChanged();87}
88
89//----------------------------------------------------------
90template<class T>91void ofPolyline_<T>::insertVertex(float x, float y, float z, int index) {92insertVertex(T(x, y, z), index);93}
94
95
96//----------------------------------------------------------
97template<class T>98void ofPolyline_<T>::removeVertex(int index) {99if(index >= points.size()){100ofLogError("ofPolyline") << "removeVertex(): ignoring out of range index " << index << ", number of vertices is" << points.size();101}else{102curveVertices.clear();103points.erase(points.begin()+index);104flagHasChanged();105}106}
107
108
109//----------------------------------------------------------
110template<class T>111size_t ofPolyline_<T>::size() const {112return points.size();113}
114
115//----------------------------------------------------------
116template<class T>117const T& ofPolyline_<T>::operator[] (int index) const {118return points[index];119}
120
121//----------------------------------------------------------
122template<class T>123T& ofPolyline_<T>::operator[] (int index) {124flagHasChanged();125return points[index];126}
127
128//----------------------------------------------------------
129template<class T>130void ofPolyline_<T>::resize(size_t size){131points.resize(size);132flagHasChanged();133}
134
135//----------------------------------------------------------
136template<class T>137void ofPolyline_<T>::setClosed( bool tf ) {138bClosed = tf;139flagHasChanged();140}
141
142//----------------------------------------------------------
143template<class T>144bool ofPolyline_<T>::isClosed() const {145return bClosed;146}
147
148//----------------------------------------------------------
149template<class T>150void ofPolyline_<T>::close(){151setClosed(true);152}
153
154//----------------------------------------------------------
155template<class T>156bool ofPolyline_<T>::hasChanged(){157if(bHasChanged){158bHasChanged=false;159return true;160}else{161return false;162}163}
164
165//----------------------------------------------------------
166template<class T>167void ofPolyline_<T>::flagHasChanged() {168bHasChanged = true;169bCacheIsDirty = true;170}
171
172//----------------------------------------------------------
173template<class T>174std::vector<T> & ofPolyline_<T>::getVertices(){175flagHasChanged();176return points;177}
178
179//----------------------------------------------------------
180template<class T>181const std::vector<T> & ofPolyline_<T>::getVertices() const {182return points;183}
184
185
186//----------------------------------------------------------
187template<class T>188void ofPolyline_<T>::setCircleResolution(int res){189if (res > 1 && res != (int)circlePoints.size()){190circlePoints.resize(res);191
192float angle = 0.0f;193const float angleAdder = glm::two_pi<float>() / (float)res;194for (int i = 0; i < res; i++){195circlePoints[i].x = cos(angle);196circlePoints[i].y = sin(angle);197circlePoints[i].z = 0.0f;198angle += angleAdder;199}200}201}
202
203//----------------------------------------------------------
204template<class T>205// wraps any radian angle -FLT_MAX to +FLT_MAX into 0->2PI range.
206// TODO, make angle treatment consistent across all functions
207// should always be radians? or should this take degrees?
208// used internally, so perhaps not as important
209float ofPolyline_<T>::wrapAngle(float angleRadians) {210return ofWrap(angleRadians, 0.0f, glm::two_pi<float>());211}
212
213//----------------------------------------------------------
214template<class T>215void ofPolyline_<T>::bezierTo( const T & cp1, const T & cp2, const T & to, int curveResolution ){216// if, and only if poly vertices has points, we can make a bezier217// from the last point218curveVertices.clear();219
220// the resolultion with which we computer this bezier221// is arbitrary, can we possibly make it dynamic?222
223if (size() > 0){224float x0 = points[size()-1].x;225float y0 = points[size()-1].y;226float z0 = points[size()-1].z;227
228float ax, bx, cx;229float ay, by, cy;230float az, bz, cz;231float t, t2, t3;232float x, y, z;233
234// polynomial coefficients235cx = 3.0f * (cp1.x - x0);236bx = 3.0f * (cp2.x - cp1.x) - cx;237ax = to.x - x0 - cx - bx;238
239cy = 3.0f * (cp1.y - y0);240by = 3.0f * (cp2.y - cp1.y) - cy;241ay = to.y - y0 - cy - by;242
243cz = 3.0f * (cp1.z - z0);244bz = 3.0f * (cp2.z - cp1.z) - cz;245az = to.z - z0 - cz - bz;246
247for (int i = 1; i <= curveResolution; i++){248t = (float)i / (float)(curveResolution);249t2 = t * t;250t3 = t2 * t;251x = (ax * t3) + (bx * t2) + (cx * t) + x0;252y = (ay * t3) + (by * t2) + (cy * t) + y0;253z = (az * t3) + (bz * t2) + (cz * t) + z0;254points.emplace_back(x,y,z);255}256}257flagHasChanged();258}
259
260//----------------------------------------------------------
261template<class T>262void ofPolyline_<T>::quadBezierTo(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, int curveResolution){263curveVertices.clear();264for(int i=0; i <= curveResolution; i++){265double t = (double)i / (double)(curveResolution);266double a = (1.0 - t)*(1.0 - t);267double b = 2.0 * t * (1.0 - t);268double c = t*t;269double x = a * x1 + b * x2 + c * x3;270double y = a * y1 + b * y2 + c * y3;271double z = a * z1 + b * z2 + c * z3;272points.emplace_back(x, y, z);273}274flagHasChanged();275}
276
277//----------------------------------------------------------
278template<class T>279void ofPolyline_<T>::curveTo( const T & to, int curveResolution ){280
281curveVertices.push_back(to);282
283if (curveVertices.size() == 4){284
285float x0 = curveVertices[0].x;286float y0 = curveVertices[0].y;287float z0 = curveVertices[0].z;288float x1 = curveVertices[1].x;289float y1 = curveVertices[1].y;290float z1 = curveVertices[1].z;291float x2 = curveVertices[2].x;292float y2 = curveVertices[2].y;293float z2 = curveVertices[2].z;294float x3 = curveVertices[3].x;295float y3 = curveVertices[3].y;296float z3 = curveVertices[3].z;297
298float t,t2,t3;299float x,y,z;300
301for (int i = 1; i <= curveResolution; i++){302
303t = (float)i / (float)(curveResolution);304t2 = t * t;305t3 = t2 * t;306
307x = 0.5f * ( ( 2.0f * x1 ) +308( -x0 + x2 ) * t +309( 2.0f * x0 - 5.0f * x1 + 4 * x2 - x3 ) * t2 +310( -x0 + 3.0f * x1 - 3.0f * x2 + x3 ) * t3 );311
312y = 0.5f * ( ( 2.0f * y1 ) +313( -y0 + y2 ) * t +314( 2.0f * y0 - 5.0f * y1 + 4 * y2 - y3 ) * t2 +315( -y0 + 3.0f * y1 - 3.0f * y2 + y3 ) * t3 );316
317z = 0.5f * ( ( 2.0f * z1 ) +318( -z0 + z2 ) * t +319( 2.0f * z0 - 5.0f * z1 + 4 * z2 - z3 ) * t2 +320( -z0 + 3.0f * z1 - 3.0f * z2 + z3 ) * t3 );321
322points.emplace_back(x,y,z);323}324curveVertices.pop_front();325}326flagHasChanged();327}
328
329//----------------------------------------------------------
330template<class T>331void ofPolyline_<T>::arc(const T & center, float radiusX, float radiusY, float angleBegin, float angleEnd, bool clockwise, int circleResolution){332
333if(circleResolution<=1) circleResolution=2;334setCircleResolution(circleResolution);335points.reserve(points.size()+circleResolution);336
337const float epsilon = 0.0001f;338
339const size_t nCirclePoints = circlePoints.size();340float segmentArcSize = glm::two_pi<float>() / (float)nCirclePoints;341
342// convert angles to radians and wrap them into the range 0-M_TWO_PI and343float angleBeginRad = wrapAngle(ofDegToRad(angleBegin));344float angleEndRad = wrapAngle(ofDegToRad(angleEnd));345
346while(angleBeginRad >= angleEndRad) angleEndRad += glm::two_pi<float>();347
348// determine the directional angle delta349float d = clockwise ? angleEndRad - angleBeginRad : angleBeginRad - angleEndRad;350// find the shortest angle delta, clockwise delta direction yeilds POSITIVE values351float deltaAngle = atan2(sin(d),cos(d));352
353// establish the remaining angle that we have to work through354float remainingAngle = deltaAngle;355
356// if the delta angle is in the CCW direction OR the start and stop angles are357// effectively the same adjust the remaining angle to be a be a full rotation358if(deltaAngle < 0 || std::abs(deltaAngle) < epsilon) remainingAngle += glm::two_pi<float>();359
360T radii(radiusX, radiusY, 0.f);361T point(0);362
363int currentLUTIndex = 0;364bool isFirstPoint = true; // special case for the first point365
366while(remainingAngle > 0) {367if(isFirstPoint) {368// TODO: should this be the exact point on the circle or369// should it be an intersecting point on the line that connects two370// surrounding LUT points?371//372// get the EXACT first point requested (for points that373// don't fall precisely on a LUT entry)374point = T(cos(angleBeginRad), sin(angleBeginRad), 0.f);375// set up the get any in between points from the LUT376float ratio = angleBeginRad / glm::two_pi<float>() * (float)nCirclePoints;377currentLUTIndex = clockwise ? (int)ceil(ratio) : (int)floor(ratio);378float lutAngleAtIndex = currentLUTIndex * segmentArcSize;379// the angle between the beginning angle and the next angle in the LUT table380float d = clockwise ? (lutAngleAtIndex - angleBeginRad) : (angleBeginRad - lutAngleAtIndex);381float firstPointDelta = atan2(sin(d),cos(d)); // negative is in the clockwise direction382
383// if the are "equal", get the next one CCW384if(std::abs(firstPointDelta) < epsilon) {385currentLUTIndex = clockwise ? (currentLUTIndex + 1) : (currentLUTIndex - 1);386firstPointDelta = segmentArcSize; // we start at the next lut point387}388
389// start counting from the offset390remainingAngle -= firstPointDelta;391isFirstPoint = false;392} else {393point = T(circlePoints[currentLUTIndex].x, circlePoints[currentLUTIndex].y, 0.f);394if(clockwise) {395currentLUTIndex++; // go to the next LUT point396remainingAngle -= segmentArcSize; // account for next point397// if the angle overshoots, then the while loop will fail next time398} else {399currentLUTIndex--; // go to the next LUT point400remainingAngle -= segmentArcSize; // account for next point401// if the angle overshoots, then the while loop will fail next time402}403}404
405// keep the current lut index in range406if(clockwise) {407currentLUTIndex = currentLUTIndex % nCirclePoints;408} else {409if(currentLUTIndex < 0) currentLUTIndex = nCirclePoints + currentLUTIndex;410}411
412// add the point to the poly line413point = point * radii + center;414points.push_back(point);415
416// if the next LUT point moves us past the end angle then417// add a a point a the exact end angle and call it finished418if(remainingAngle < epsilon) {419point = T(cos(angleEndRad), sin(angleEndRad), 0.f);420point = point * radii + center;421points.push_back(point);422remainingAngle = 0; // call it finished, the next while loop test will fail423}424}425flagHasChanged();426}
427
428//----------------------------------------------------------
429template<class T>430float ofPolyline_<T>::getPerimeter() const {431if(points.size() < 2) {432return 0;433} else {434updateCache();435return lengths.back();436}437}
438
439//----------------------------------------------------------
440template<class T>441float ofPolyline_<T>::getArea() const{442updateCache();443return area;444}
445
446//----------------------------------------------------------
447template<class T>448T ofPolyline_<T>::getCentroid2D() const{449updateCache();450return centroid2D;451}
452
453//----------------------------------------------------------
454template<class T>455ofRectangle ofPolyline_<T>::getBoundingBox() const {456
457ofRectangle box;458for(size_t i = 0; i < size(); i++) {459if(i == 0) {460box.set(points[i],0,0);461} else {462box.growToInclude(points[i]);463}464}465return box;466}
467
468//----------------------------------------------------------
469template<class T>470ofPolyline_<T> ofPolyline_<T>::getSmoothed(int smoothingSize, float smoothingShape) const {471int n = size();472smoothingSize = ofClamp(smoothingSize, 0, n);473smoothingShape = ofClamp(smoothingShape, 0, 1);474
475// precompute weights and normalization476std::vector<float> weights;477weights.resize(smoothingSize);478// side weights479for(int i = 1; i < smoothingSize; i++) {480float curWeight = ofMap(i, 0, smoothingSize, 1, smoothingShape);481weights[i] = curWeight;482}483
484// make a copy of this polyline485ofPolyline_ result = *this;486
487for(int i = 0; i < n; i++) {488float sum = 1; // center weight489for(int j = 1; j < smoothingSize; j++) {490T cur(0);491int leftPosition = i - j;492int rightPosition = i + j;493if(leftPosition < 0 && bClosed) {494leftPosition += n;495}496if(leftPosition >= 0) {497cur += points[leftPosition];498sum += weights[j];499}500if(rightPosition >= n && bClosed) {501rightPosition -= n;502}503if(rightPosition < n) {504cur += points[rightPosition];505sum += weights[j];506}507result[i] += cur * weights[j];508}509result[i] /= sum;510}511
512return result;513}
514
515//----------------------------------------------------------
516template<class T>517ofPolyline_<T> ofPolyline_<T>::getResampledBySpacing(float spacing) const {518if(spacing==0 || size() == 0) return *this;519ofPolyline_ poly;520float totalLength = getPerimeter();521float f=0;522for(f=0; f<=totalLength; f += spacing) {523poly.lineTo(getPointAtLength(f));524}525
526if(!isClosed()) {527if( f != totalLength ){528poly.lineTo(points.back());529}530poly.setClosed(false);531} else {532poly.setClosed(true);533}534
535return poly;536}
537
538//----------------------------------------------------------
539template<class T>540ofPolyline_<T> ofPolyline_<T>::getResampledByCount(int count) const {541float perimeter = getPerimeter();542if(count < 2) {543ofLogWarning("ofPolyline_") << "getResampledByCount(): requested " << count <<" points, using minimum count of 2 ";544count = 2;545}546return ofPolyline_<T>::getResampledBySpacing(perimeter / (count-1));547}
548
549//----------------------------------------------------------
550// http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/
551template<class T>552inline T getClosestPointUtil(const T& p1, const T& p2, const T& p3, float* normalizedPosition) {553// if p1 is coincident with p2, there is no line554if(p1 == p2) {555if(normalizedPosition != nullptr) {556*normalizedPosition = 0;557}558return p1;559}560
561float u = (p3.x - p1.x) * (p2.x - p1.x);562u += (p3.y - p1.y) * (p2.y - p1.y);563// perfect place for fast inverse sqrt...564float len = glm::length(toGlm(p2 - p1));565u /= (len * len);566
567// clamp u568if(u > 1) {569u = 1;570} else if(u < 0) {571u = 0;572}573if(normalizedPosition != nullptr) {574*normalizedPosition = u;575}576return glm::mix(toGlm(p1), toGlm(p2), u);577}
578
579//----------------------------------------------------------
580template<class T>581// a much faster but less accurate version would check distances to vertices first,
582// which assumes vertices are evenly spaced
583T ofPolyline_<T>::getClosestPoint(const T& target, unsigned int* nearestIndex) const {584const ofPolyline_ & polyline = *this;585
586if(polyline.size() < 2) {587if(nearestIndex != nullptr) {588nearestIndex = 0;589}590return target;591}592
593float distance = 0;594T nearestPoint(0);595unsigned int nearest = 0;596float normalizedPosition = 0;597unsigned int lastPosition = polyline.size() - 1;598if(polyline.isClosed()) {599lastPosition++;600}601for(int i = 0; i < (int) lastPosition; i++) {602bool repeatNext = i == (int) (polyline.size() - 1);603
604const auto& cur = polyline[i];605const auto& next = repeatNext ? polyline[0] : polyline[i + 1];606
607float curNormalizedPosition = 0;608auto curNearestPoint = getClosestPointUtil(cur, next, target, &curNormalizedPosition);609float curDistance = glm::distance(toGlm(curNearestPoint), toGlm(target));610if(i == 0 || curDistance < distance) {611distance = curDistance;612nearest = i;613nearestPoint = curNearestPoint;614normalizedPosition = curNormalizedPosition;615}616}617
618if(nearestIndex != nullptr) {619if(normalizedPosition > .5) {620nearest++;621if(nearest == polyline.size()) {622nearest = 0;623}624}625*nearestIndex = nearest;626}627
628return nearestPoint;629}
630
631//--------------------------------------------------
632template<class T>633bool ofPolyline_<T>::inside(const T & p, const ofPolyline_ & polyline){634return ofPolyline_<T>::inside(p.x,p.y,polyline);635}
636
637//--------------------------------------------------
638template<class T>639bool ofPolyline_<T>::inside(float x, float y, const ofPolyline_ & polyline){640int counter = 0;641int i;642double xinters;643T p1,p2;644
645int N = polyline.size();646
647p1 = polyline[0];648for (i=1;i<=N;i++) {649p2 = polyline[i % N];650if (y > std::min(p1.y,p2.y)) {651if (y <= std::max(p1.y,p2.y)) {652if (x <= std::max(p1.x,p2.x)) {653if (p1.y != p2.y) {654xinters = (y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x;655if (p1.x == p2.x || x <= xinters)656counter++;657}658}659}660}661p1 = p2;662}663
664if (counter % 2 == 0) return false;665else return true;666}
667
668//--------------------------------------------------
669template<class T>670bool ofPolyline_<T>::inside(float x, float y) const {671return ofPolyline_<T>::inside(x, y, *this);672
673}
674
675//--------------------------------------------------
676template<class T>677bool ofPolyline_<T>::inside(const T & p) const {678return ofPolyline_<T>::inside(p, *this);679}
680
681
682
683//--------------------------------------------------
684namespace of{685namespace priv{686
687//This is for polygon/contour simplification - we use it to reduce the number of points needed in688//representing the letters as openGL shapes - will soon be moved to ofGraphics.cpp689
690// From: http://softsurfer.com/Archive/algorithm_0205/algorithm_0205.htm691// Copyright 2002, softSurfer (www.softsurfer.com)692// This code may be freely used and modified for any purpose693// providing that this copyright notice is included with it.694// SoftSurfer makes no warranty for this code, and cannot be held695// liable for any real or imagined damage resulting from its use.696// Users of this code must verify correctness for their application.697template<class T>698struct Segment{699T P0;700T P1;701};702
703template<class T>704static void simplifyDP(float tol, T* v, int j, int k, int* mk ){705if (k <= j+1) // there is nothing to simplify706return;707
708// check for adequate approximation by segment S from v[j] to v[k]709int maxi = j; // index of vertex farthest from S710float maxd2 = 0; // distance squared of farthest vertex711float tol2 = tol * tol; // tolerance squared712Segment<T> S = {v[j], v[k]}; // segment from v[j] to v[k]713auto u = S.P1 - S.P0; // segment direction vector714double cu = glm::dot(toGlm(u), toGlm(u)); // segment length squared715
716// test each vertex v[i] for max distance from S717// compute using the Feb 2001 Algorithm's dist_ofPoint_to_Segment()718// Note: this works in any dimension (2D, 3D, ...)719T w;720T Pb; // base of perpendicular from v[i] to S721float b, cw, dv2; // dv2 = distance v[i] to S squared722
723for (int i=j+1; i<k; i++){724// compute distance squared725w = v[i] - S.P0;726cw = glm::dot(toGlm(w), toGlm(u));727if ( cw <= 0 ) dv2 = glm::length2(toGlm(v[i]) - toGlm(S.P0));728else if ( cu <= cw ) dv2 = glm::length2(toGlm(v[i]) - toGlm(S.P1));729else {730b = (float)(cw / cu);731Pb = S.P0 + u*b;732dv2 = glm::length2(toGlm(v[i]) - toGlm(Pb));733}734// test with current max distance squared735if (dv2 <= maxd2) continue;736
737// v[i] is a new max vertex738maxi = i;739maxd2 = dv2;740}741if (maxd2 > tol2) // error is worse than the tolerance742{743// split the polyline at the farthest vertex from S744mk[maxi] = 1; // mark v[maxi] for the simplified polyline745// recursively simplify the two subpolylines at v[maxi]746simplifyDP( tol, v, j, maxi, mk ); // polyline v[j] to v[maxi]747simplifyDP( tol, v, maxi, k, mk ); // polyline v[maxi] to v[k]748}749// else the approximation is OK, so ignore intermediate vertices750}751}752}
753
754//--------------------------------------------------
755template<class T>756void ofPolyline_<T>::simplify(float tol){757if(points.size() < 2) return;758
759int n = size();760
761if(n == 0) {762return;763}764
765std::vector <T> sV;766sV.resize(n);767
768int i, k, m, pv; // misc counters769float tol2 = tol * tol; // tolerance squared770std::vector<T> vt;771std::vector<int> mk;772vt.resize(n);773mk.resize(n,0);774
775
776// STAGE 1. Vertex Reduction within tolerance of prior vertex cluster777vt[0] = points[0]; // start at the beginning778for (i=k=1, pv=0; i<n; i++) {779if (glm::length2((const glm::vec3&)points[i] - (const glm::vec3&)points[pv]) < tol2) continue;780
781vt[k++] = points[i];782pv = i;783}784if (pv < n-1) vt[k++] = points[n-1]; // finish at the end785
786// STAGE 2. Douglas-Peucker polyline simplification787mk[0] = mk[k-1] = 1; // mark the first and last vertices788of::priv::simplifyDP( tol, &vt[0], 0, k-1, &mk[0] );789
790// copy marked vertices to the output simplified polyline791for (i=m=0; i<k; i++) {792if (mk[i]) sV[m++] = vt[i];793}794
795//get rid of the unused points796if( m < (int)sV.size() ){797points.assign( sV.begin(),sV.begin()+m );798}else{799points = sV;800}801
802}
803
804//--------------------------------------------------
805template<class T>806void ofPolyline_<T>::translate(const glm::vec3 & p){807for(auto & point : points){808point += p;809}810flagHasChanged();811}
812
813//--------------------------------------------------
814template<class T>815void ofPolyline_<T>::translate(const glm::vec2 &p){816translate(glm::vec3(p, 0.0));817}
818
819//--------------------------------------------------
820template<class T>821void ofPolyline_<T>::rotateDeg(float degrees, const glm::vec3& axis){822rotateRad(ofDegToRad(degrees), axis);823}
824
825//--------------------------------------------------
826template<class T>827void ofPolyline_<T>::rotateRad(float radians, const glm::vec3& axis){828for(auto & point : points){829point = glm::rotate(toGlm(point), radians, axis);830}831flagHasChanged();832}
833
834//--------------------------------------------------
835template<class T>836void ofPolyline_<T>::rotate(float degrees, const glm::vec3 &axis){837rotateDeg(degrees, axis);838}
839
840//--------------------------------------------------
841template<class T>842void ofPolyline_<T>::rotateDeg(float degrees, const glm::vec2& axis){843rotateRad(ofDegToRad(degrees), glm::vec3(axis, 0.0));844}
845
846//--------------------------------------------------
847template<class T>848void ofPolyline_<T>::rotateRad(float radians, const glm::vec2& axis){849rotateRad(radians, glm::vec3(axis, 0.0));850}
851
852//--------------------------------------------------
853template<class T>854void ofPolyline_<T>::rotate(float degrees, const glm::vec2 &axis){855rotateRad(ofDegToRad(degrees), glm::vec3(axis, 0.0));856}
857
858//--------------------------------------------------
859template<class T>860void ofPolyline_<T>::scale(float x, float y){861for(auto & point : points){862point.x *= x;863point.y *= y;864}865flagHasChanged();866}
867
868//--------------------------------------------------
869template<class T>870void ofPolyline_<T>::draw() const{871ofGetCurrentRenderer()->draw(*this);872}
873
874//--------------------------------------------------
875template<class T>876void ofPolyline_<T>::setRightVector(T v) {877rightVector = v;878flagHasChanged();879}
880
881//--------------------------------------------------
882template<class T>883T ofPolyline_<T>::getRightVector() const {884return rightVector;885}
886
887//--------------------------------------------------
888template<class T>889float ofPolyline_<T>::getIndexAtLength(float length) const {890if(points.size() < 2) return 0;891updateCache();892
893float totalLength = getPerimeter();894length = ofClamp(length, 0, totalLength);895
896int lastPointIndex = isClosed() ? points.size() : points.size()-1;897
898int i1 = ofClamp(floor(length / totalLength * lastPointIndex), 0, lengths.size()-2); // start approximation here899int leftLimit = 0;900int rightLimit = lastPointIndex;901
902float distAt1, distAt2;903for(int iterations = 0; iterations < 32; iterations ++) { // limit iterations904i1 = ofClamp(i1, 0, lengths.size()-1);905distAt1 = lengths[i1];906if(distAt1 <= length) { // if Length at i1 is less than desired Length (this is good)907distAt2 = lengths[i1+1];908if(distAt2 >= length) {909float t = ofMap(length, distAt1, distAt2, 0, 1);910return i1 + t;911} else {912leftLimit = i1;913}914} else {915rightLimit = i1;916}917i1 = (leftLimit + rightLimit)/2;918}919return 0;920}
921
922
923//--------------------------------------------------
924template<class T>925float ofPolyline_<T>::getIndexAtPercent(float f) const {926return getIndexAtLength(f * getPerimeter());927}
928
929//--------------------------------------------------
930template<class T>931float ofPolyline_<T>::getLengthAtIndex(int index) const {932if(points.size() < 2) return 0;933updateCache();934return lengths[getWrappedIndex(index)];935}
936
937//--------------------------------------------------
938template<class T>939float ofPolyline_<T>::getLengthAtIndexInterpolated(float findex) const {940if(points.size() < 2) return 0;941updateCache();942int i1, i2;943float t;944getInterpolationParams(findex, i1, i2, t);945return ofLerp(getLengthAtIndex(i1), getLengthAtIndex(i2), t);946}
947
948
949//--------------------------------------------------
950template<class T>951T ofPolyline_<T>::getPointAtLength(float f) const {952if(points.size() < 2) return T();953updateCache();954return getPointAtIndexInterpolated(getIndexAtLength(f));955}
956
957//--------------------------------------------------
958template<class T>959T ofPolyline_<T>::getPointAtPercent(float f) const {960float length = getPerimeter();961return getPointAtLength(f * length);962}
963
964
965//--------------------------------------------------
966template<class T>967T ofPolyline_<T>::getPointAtIndexInterpolated(float findex) const {968if(points.size() < 2) return T();969int i1, i2;970float t;971getInterpolationParams(findex, i1, i2, t);972T leftPoint(points[i1]);973T rightPoint(points[i2]);974return glm::mix(toGlm(leftPoint), toGlm(rightPoint), t);975}
976
977
978//--------------------------------------------------
979template<class T>980float ofPolyline_<T>::getAngleAtIndex(int index) const {981return getDegreesAtIndex(index);982}
983
984//--------------------------------------------------
985template<class T>986float ofPolyline_<T>::getAngleAtIndexInterpolated(float findex) const {987return getDegreesAtIndexInterpolated(findex);988}
989
990
991//--------------------------------------------------
992template<class T>993float ofPolyline_<T>::getDegreesAtIndex(int index) const {994if(points.size() < 2) return 0;995updateCache();996return ofRadToDeg(angles[getWrappedIndex(index)]);997}
998
999//--------------------------------------------------
1000template<class T>1001float ofPolyline_<T>::getDegreesAtIndexInterpolated(float findex) const {1002if(points.size() < 2) return 0;1003int i1, i2;1004float t;1005getInterpolationParams(findex, i1, i2, t);1006return ofRadToDeg(ofLerp(getDegreesAtIndex(i1), getDegreesAtIndex(i2), t));1007}
1008
1009
1010//--------------------------------------------------
1011template<class T>1012float ofPolyline_<T>::getRadiansAtIndex(int index) const {1013if(points.size() < 2) return 0;1014updateCache();1015return angles[getWrappedIndex(index)];1016}
1017
1018//--------------------------------------------------
1019template<class T>1020float ofPolyline_<T>::getRadiansAtIndexInterpolated(float findex) const {1021if(points.size() < 2) return 0;1022int i1, i2;1023float t;1024getInterpolationParams(findex, i1, i2, t);1025return ofLerp(getRadiansAtIndex(i1), getRadiansAtIndex(i2), t);1026}
1027
1028//--------------------------------------------------
1029template<class T>1030T ofPolyline_<T>::getRotationAtIndex(int index) const {1031if(points.size() < 2) return T();1032updateCache();1033return rotations[getWrappedIndex(index)];1034}
1035
1036//--------------------------------------------------
1037template<class T>1038T ofPolyline_<T>::getRotationAtIndexInterpolated(float findex) const {1039if(points.size() < 2) return T();1040int i1, i2;1041float t;1042getInterpolationParams(findex, i1, i2, t);1043return glm::mix(toGlm(getRotationAtIndex(i1)), toGlm(getRotationAtIndex(i2)), t);1044}
1045
1046//--------------------------------------------------
1047template<class T>1048T ofPolyline_<T>::getTangentAtIndex(int index) const {1049if(points.size() < 2) return T();1050updateCache();1051return tangents[getWrappedIndex(index)];1052}
1053
1054//--------------------------------------------------
1055template<class T>1056T ofPolyline_<T>::getTangentAtIndexInterpolated(float findex) const {1057if(points.size() < 2) return T();1058int i1, i2;1059float t;1060getInterpolationParams(findex, i1, i2, t);1061return glm::mix(toGlm(getTangentAtIndex(i1)), toGlm(getTangentAtIndex(i2)), t);1062}
1063
1064//--------------------------------------------------
1065template<class T>1066T ofPolyline_<T>::getNormalAtIndex(int index) const {1067if(points.size() < 2) return T();1068updateCache();1069return normals[getWrappedIndex(index)];1070}
1071
1072//--------------------------------------------------
1073template<class T>1074T ofPolyline_<T>::getNormalAtIndexInterpolated(float findex) const {1075if(points.size() < 2) return T();1076int i1, i2;1077float t;1078getInterpolationParams(findex, i1, i2, t);1079return glm::mix(toGlm(getNormalAtIndex(i1)), toGlm(getNormalAtIndex(i2)), t);1080}
1081
1082
1083//--------------------------------------------------
1084template<class T>1085void ofPolyline_<T>::calcData(int index, T &tangent, float &angle, T &rotation, T &normal) const {1086int i1 = getWrappedIndex( index - 1 );1087int i2 = getWrappedIndex( index );1088int i3 = getWrappedIndex( index + 1 );1089
1090const auto &p1 = toGlm(points[i1]);1091const auto &p2 = toGlm(points[i2]);1092const auto &p3 = toGlm(points[i3]);1093
1094auto v1(p1 - p2); // vector to previous point1095auto v2(p3 - p2); // vector to next point1096
1097v1 = glm::normalize(v1);1098v2 = glm::normalize(v2);1099
1100// If just one of p1, p2, or p3 was identical, further calculations1101// are (almost literally) pointless, as v1 or v2 will then contain1102// NaN values instead of floats.1103
1104bool noSegmentHasZeroLength = (v1 == v1 && v2 == v2);1105
1106if ( noSegmentHasZeroLength ){1107tangent = toOf( glm::length2(v2 - v1) > 0 ? glm::normalize(v2 - v1) : -v1 );1108normal = toOf( glm::normalize( glm::cross( toGlm( rightVector ), toGlm( tangent ) ) ) );1109rotation = toOf( glm::cross( v1, v2 ) );1110angle = glm::pi<float>() - acosf( ofClamp( glm::dot( v1, v2 ), -1.f, 1.f ) );1111} else{1112rotation = tangent = normal = T( 0.f );1113angle = 0.f;1114}1115}
1116
1117
1118//--------------------------------------------------
1119template<class T>1120int ofPolyline_<T>::getWrappedIndex(int index) const {1121if(points.empty()) return 0;1122
1123if(index < 0) return isClosed() ? (index + points.size()) % points.size() : 0;1124if(index > int(points.size())-1) return isClosed() ? index % points.size() : points.size() - 1;1125return index;1126}
1127
1128//--------------------------------------------------
1129template<class T>1130void ofPolyline_<T>::getInterpolationParams(float findex, int &i1, int &i2, float &t) const {1131i1 = floor(findex);1132t = findex - i1;1133i1 = getWrappedIndex(i1);1134i2 = getWrappedIndex(i1 + 1);1135}
1136
1137//--------------------------------------------------
1138template<class T>1139void ofPolyline_<T>::updateCache(bool bForceUpdate) const {1140if(bCacheIsDirty || bForceUpdate) {1141lengths.clear();1142angles.clear();1143rotations.clear();1144normals.clear();1145tangents.clear();1146area = 0;1147centroid2D = {0.f, 0.f, 0.f};1148bCacheIsDirty = false;1149
1150if(points.size() < 2) return;1151
1152// area1153for(int i=0;i<(int)points.size()-1;i++){1154area += points[i].x * points[i+1].y - points[i+1].x * points[i].y;1155}1156area += points[points.size()-1].x * points[0].y - points[0].x * points[points.size()-1].y;1157area *= 0.5;1158
1159if(fabsf(area) < std::numeric_limits<float>::epsilon()) {1160centroid2D = getBoundingBox().getCenter();1161} else {1162// centroid1163// TODO: doesn't seem to work on all concave shapes1164for(int i=0;i<(int)points.size()-1;i++){1165centroid2D.x += (points[i].x + points[i+1].x) * (points[i].x*points[i+1].y - points[i+1].x*points[i].y);1166centroid2D.y += (points[i].y + points[i+1].y) * (points[i].x*points[i+1].y - points[i+1].x*points[i].y);1167}1168centroid2D.x += (points[points.size()-1].x + points[0].x) * (points[points.size()-1].x*points[0].y - points[0].x*points[points.size()-1].y);1169centroid2D.y += (points[points.size()-1].y + points[0].y) * (points[points.size()-1].x*points[0].y - points[0].x*points[points.size()-1].y);1170
1171centroid2D.x /= (6*area);1172centroid2D.y /= (6*area);1173}1174
1175
1176// per vertex cache1177lengths.resize(points.size());1178tangents.resize(points.size());1179angles.resize(points.size());1180normals.resize(points.size());1181rotations.resize(points.size());1182
1183float angle;1184T rotation;1185T normal;1186T tangent;1187
1188float length = 0;1189for(int i=0; i<(int)points.size(); i++) {1190lengths[i] = length;1191
1192calcData(i, tangent, angle, rotation, normal);1193tangents[i] = tangent;1194angles[i] = angle;1195rotations[i] = rotation;1196normals[i] = normal;1197
1198length += glm::distance(toGlm(points[i]), toGlm(points[getWrappedIndex(i + 1)]));1199}1200
1201if(isClosed()) lengths.push_back(length);1202}1203}
1204
1205
1206//--------------------------------------------------
1207template<class T>1208typename std::vector<T>::iterator ofPolyline_<T>::begin(){1209return points.begin();1210}
1211
1212//--------------------------------------------------
1213template<class T>1214typename std::vector<T>::iterator ofPolyline_<T>::end(){1215return points.end();1216}
1217
1218//--------------------------------------------------
1219template<class T>1220typename std::vector<T>::const_iterator ofPolyline_<T>::begin() const{1221return points.begin();1222}
1223
1224//--------------------------------------------------
1225template<class T>1226typename std::vector<T>::const_iterator ofPolyline_<T>::end() const{1227return points.end();1228}
1229
1230//--------------------------------------------------
1231template<class T>1232typename std::vector<T>::reverse_iterator ofPolyline_<T>::rbegin(){1233return points.rbegin();1234}
1235
1236//--------------------------------------------------
1237template<class T>1238typename std::vector<T>::reverse_iterator ofPolyline_<T>::rend(){1239return points.rend();1240}
1241
1242//--------------------------------------------------
1243template<class T>1244typename std::vector<T>::const_reverse_iterator ofPolyline_<T>::rbegin() const{1245return points.rbegin();1246}
1247
1248//--------------------------------------------------
1249template<class T>1250typename std::vector<T>::const_reverse_iterator ofPolyline_<T>::rend() const{1251return points.rend();1252}
1253
1254