framework2
908 строк · 27.5 Кб
1#include "ofPath.h"
2#include "ofColor.h"
3
4using std::vector;
5
6#if defined(TARGET_EMSCRIPTEN)
7ofTessellator ofPath::tessellator;
8#elif HAS_TLS
9thread_local ofTessellator ofPath::tessellator;
10#endif
11
12ofPath::Command::Command(Type type)
13:type(type){
14
15}
16
17//----------------------------------------------------------
18ofPath::Command::Command(Type type , const glm::vec3 & p)
19:type(type)
20,to(p)
21,cp1(glm::vec3(0))
22,cp2(glm::vec3(0))
23,radiusX(0)
24,radiusY(0)
25,angleBegin(0)
26,angleEnd(0)
27{}
28
29//----------------------------------------------------------
30ofPath::Command::Command(Type type , const glm::vec3 & p, const glm::vec3 & cp1, const glm::vec3 & cp2)
31:type(type)
32,to(p)
33,cp1(cp1)
34,cp2(cp2)
35,radiusX(0)
36,radiusY(0)
37,angleBegin(0)
38,angleEnd(0)
39{
40}
41
42//----------------------------------------------------------
43ofPath::Command::Command(Type type , const glm::vec3 & centre, float radiusX, float radiusY, float angleBegin, float angleEnd)
44:type(type)
45,to(centre)
46,cp1(glm::vec3(0))
47,cp2(glm::vec3(0))
48,radiusX(radiusX)
49,radiusY(radiusY)
50,angleBegin(angleBegin)
51,angleEnd(angleEnd)
52{
53}
54
55//----------------------------------------------------------
56ofPath::ofPath(){
57strokeWidth = 0;
58bFill = true;
59windingMode = OF_POLY_WINDING_ODD;
60prevCurveRes = 20;
61curveResolution = 20;
62circleResolution = 20;
63mode = COMMANDS;
64bNeedsTessellation = false;
65bHasChanged = false;
66bUseShapeColor = true;
67bNeedsPolylinesGeneration = false;
68clear();
69}
70
71//----------------------------------------------------------
72void ofPath::clear(){
73commands.clear();
74// for performance, instead of clearing the whole vector
75// let one polyline and clear it: avoids instantiation
76polylines.resize(1);
77polylines[0].clear();
78cachedTessellation.clear();
79flagShapeChanged();
80}
81
82//----------------------------------------------------------
83void ofPath::newSubPath(){
84if(mode==COMMANDS){
85}else{
86polylines.push_back(ofPolyline());
87}
88}
89
90//----------------------------------------------------------
91void ofPath::lineTo(const glm::vec3 & p){
92if(mode==COMMANDS){
93addCommand(Command(Command::lineTo,p));
94}else{
95lastPolyline().lineTo(p);
96}
97flagShapeChanged();
98}
99
100//----------------------------------------------------------
101void ofPath::lineTo(const glm::vec2 & p){
102lineTo(glm::vec3(p,0.0));
103}
104
105//----------------------------------------------------------
106void ofPath::lineTo(float x, float y, float z){
107lineTo(glm::vec3(x,y,z));
108}
109
110//----------------------------------------------------------
111void ofPath::lineTo(float x, float y){
112lineTo(glm::vec3(x,y,0));
113}
114
115//----------------------------------------------------------
116void ofPath::moveTo(const glm::vec3 & p){
117if(mode==COMMANDS){
118addCommand(Command(Command::moveTo,p));
119}else{
120if(lastPolyline().size()>0) newSubPath();
121lastPolyline().addVertex(p);
122}
123flagShapeChanged();
124}
125
126//----------------------------------------------------------
127void ofPath::moveTo(const glm::vec2 & p){
128moveTo(glm::vec3(p, 0.0));
129}
130
131//----------------------------------------------------------
132void ofPath::moveTo(float x, float y, float z){
133moveTo(glm::vec3(x,y,z));
134}
135
136//----------------------------------------------------------
137void ofPath::curveTo(const glm::vec3 & p){
138if(mode==COMMANDS){
139addCommand(Command(Command::curveTo,p));
140}else{
141lastPolyline().curveTo(p,curveResolution);
142}
143flagShapeChanged();
144}
145
146//----------------------------------------------------------
147void ofPath::curveTo(const glm::vec2 & p){
148curveTo(glm::vec3(p, 0.0));
149}
150
151//----------------------------------------------------------
152void ofPath::curveTo(float x, float y, float z){
153curveTo(glm::vec3(x,y,z));
154}
155
156//----------------------------------------------------------
157void ofPath::curveTo(float x, float y){
158curveTo(glm::vec3(x,y,0));
159}
160
161//----------------------------------------------------------
162void ofPath::bezierTo(const glm::vec3 & cp1, const glm::vec3 & cp2, const glm::vec3 & p){
163if(mode==COMMANDS){
164addCommand(Command(Command::bezierTo,p,cp1,cp2));
165}else{
166lastPolyline().bezierTo(cp1,cp2,p,curveResolution);
167}
168flagShapeChanged();
169}
170
171//----------------------------------------------------------
172void ofPath::bezierTo(const glm::vec2 & cp1, const glm::vec2 & cp2, const glm::vec2 & p){
173bezierTo(glm::vec3(cp1,0.0), glm::vec3(cp2,0.0), glm::vec3(p,0.0));
174}
175
176//----------------------------------------------------------
177void ofPath::bezierTo(float cx1, float cy1, float cx2, float cy2, float x, float y){
178bezierTo(glm::vec3(cx1,cy1,0),glm::vec3(cx2,cy2,0),glm::vec3(x,y,0));
179}
180
181//----------------------------------------------------------
182void ofPath::bezierTo(float cx1, float cy1, float cz1, float cx2, float cy2, float cz2, float x, float y, float z){
183bezierTo(glm::vec3(cx1,cy1,cz1),glm::vec3(cx2,cy2,cz2),glm::vec3(x,y,z));
184}
185
186//----------------------------------------------------------
187void ofPath::quadBezierTo(const glm::vec3 & cp1, const glm::vec3 & cp2, const glm::vec3 & p){
188if(mode==COMMANDS){
189addCommand(Command(Command::quadBezierTo,p,cp1,cp2));
190}else{
191lastPolyline().quadBezierTo(cp1,cp2,p,curveResolution);
192}
193flagShapeChanged();
194}
195
196//----------------------------------------------------------
197void ofPath::quadBezierTo(const glm::vec2 & cp1, const glm::vec2 & cp2, const glm::vec2 & p){
198quadBezierTo(glm::vec3(cp1, 0.0), glm::vec3(cp2, 0.0), glm::vec3(p, 0.0));
199}
200
201//----------------------------------------------------------
202void ofPath::quadBezierTo(float cx1, float cy1, float cx2, float cy2, float x, float y){
203quadBezierTo(glm::vec3(cx1,cy1,0),glm::vec3(cx2,cy2,0),glm::vec3(x,y,0));
204}
205
206//----------------------------------------------------------
207void ofPath::quadBezierTo(float cx1, float cy1, float cz1, float cx2, float cy2, float cz2, float x, float y, float z){
208quadBezierTo(glm::vec3(cx1,cy1,cz1),glm::vec3(cx2,cy2,cz2),glm::vec3(x,y,z));
209}
210
211//----------------------------------------------------------
212void ofPath::arc(const glm::vec3 & centre, float radiusX, float radiusY, float angleBegin, float angleEnd, bool clockwise){
213if(clockwise) {
214arc(centre,radiusX,radiusY,angleBegin,angleEnd);
215} else {
216arcNegative(centre,radiusX,radiusY,angleBegin,angleEnd);
217}
218}
219
220//----------------------------------------------------------
221void ofPath::arc(const glm::vec2 & centre, float radiusX, float radiusY, float angleBegin, float angleEnd, bool clockwise){
222arc(glm::vec3(centre, 0.0), radiusX, radiusY, angleBegin, angleEnd, clockwise);
223}
224
225//----------------------------------------------------------
226void ofPath::arc(const glm::vec3 & centre, float radiusX, float radiusY, float angleBegin, float angleEnd){
227if(mode==COMMANDS){
228//addCommand adds a moveTo if one hasn't been set, but in this case it is adding a moveTo to the center of the arc and not the beginning of the arc
229if(commands.empty() || commands.back().type==Command::close){
230glm::vec3 start = centre + glm::vec3( glm::cos( glm::radians(angleBegin) ) * radiusX, glm::sin( glm::radians(angleBegin) ) * radiusY, 0.0f );
231commands.push_back(Command(Command::moveTo,start));
232}
233addCommand(Command(Command::arc,centre,radiusX,radiusY,angleBegin,angleEnd));
234}else{
235lastPolyline().arc(centre,radiusX,radiusY,angleBegin,angleEnd,circleResolution);
236}
237flagShapeChanged();
238}
239
240//----------------------------------------------------------
241void ofPath::arc(const glm::vec2 & centre, float radiusX, float radiusY, float angleBegin, float angleEnd){
242arc(glm::vec3(centre, 0.0), radiusX, radiusY, angleBegin, angleEnd);
243}
244
245//----------------------------------------------------------
246void ofPath::arc(float x, float y, float radiusX, float radiusY, float angleBegin, float angleEnd){
247arc(glm::vec3(x,y,0),radiusX,radiusY,angleBegin,angleEnd);
248}
249
250//----------------------------------------------------------
251void ofPath::arc(float x, float y, float z, float radiusX, float radiusY, float angleBegin, float angleEnd){
252arc(glm::vec3(x,y,z),radiusX,radiusY,angleBegin,angleEnd);
253}
254
255//----------------------------------------------------------
256void ofPath::arcNegative(const glm::vec3 & centre, float radiusX, float radiusY, float angleBegin, float angleEnd){
257if(mode==COMMANDS){
258if(commands.empty() || commands.back().type==Command::close){
259glm::vec3 start = centre + glm::vec3( glm::cos( glm::radians(angleBegin) ) * radiusX, glm::sin( glm::radians(angleBegin) ) * radiusY, 0.0f );
260commands.push_back(Command(Command::moveTo,start));
261}
262addCommand(Command(Command::arcNegative,centre,radiusX,radiusY,angleBegin,angleEnd));
263}else{
264lastPolyline().arcNegative(centre,radiusX,radiusY,angleBegin,angleEnd,circleResolution);
265}
266flagShapeChanged();
267}
268
269//----------------------------------------------------------
270void ofPath::arcNegative(const glm::vec2 & centre, float radiusX, float radiusY, float angleBegin, float angleEnd){
271arcNegative(glm::vec3(centre,0),radiusX,radiusY,angleBegin,angleEnd);
272}
273
274//----------------------------------------------------------
275void ofPath::arcNegative(float x, float y, float radiusX, float radiusY, float angleBegin, float angleEnd){
276arcNegative(glm::vec3(x,y,0),radiusX,radiusY,angleBegin,angleEnd);
277}
278
279//----------------------------------------------------------
280void ofPath::arcNegative(float x, float y, float z, float radiusX, float radiusY, float angleBegin, float angleEnd){
281arcNegative(glm::vec3(x,y,z),radiusX,radiusY,angleBegin,angleEnd);
282}
283
284//----------------------------------------------------------
285void ofPath::triangle(float x1,float y1,float x2,float y2,float x3, float y3){
286triangle(x1,y1,0.0f,x2,y2,0.0f,x3,y3,0.0f);
287}
288
289//----------------------------------------------------------
290void ofPath::triangle(float x1,float y1,float z1,float x2,float y2,float z2,float x3, float y3,float z3){
291moveTo(x1,y1,z1);
292lineTo(x2,y2,z2);
293lineTo(x3,y3,z3);
294close();
295}
296
297//----------------------------------------------------------
298void ofPath::triangle(const glm::vec3 & p1, const glm::vec3 & p2, const glm::vec3 & p3){
299triangle(p1.x,p1.y,p1.z,p2.x,p2.y,p2.z,p3.x,p3.y,p3.z);
300}
301
302//----------------------------------------------------------
303void ofPath::triangle(const glm::vec2 & p1, const glm::vec2 & p2, const glm::vec2 & p3){
304triangle(p1.x,p1.y,0.0,p2.x,p2.y,0.0,p3.x,p3.y,0.0);
305}
306
307
308//----------------------------------------------------------
309void ofPath::circle(float x, float y, float radius){
310circle(x,y,0.0f,radius);
311}
312
313//----------------------------------------------------------
314void ofPath::circle(float x, float y, float z, float radius){
315moveTo(x + radius, y, z);
316arc(x,y,z,radius,radius,0,360);
317}
318
319//----------------------------------------------------------
320void ofPath::circle(const glm::vec3 & p, float radius){
321circle(p.x,p.y,p.z,radius);
322}
323
324//----------------------------------------------------------
325void ofPath::circle(const glm::vec2 & p, float radius){
326circle(p.x,p.y,0.0,radius);
327}
328
329
330//----------------------------------------------------------
331void ofPath::ellipse(float x, float y, float width, float height){
332ellipse(x,y,0.0f,width,height);
333}
334
335//----------------------------------------------------------
336void ofPath::ellipse(float x, float y, float z, float width, float height){
337arc(x,y,z,width*.5f,height*.5f,0,360);
338}
339
340//----------------------------------------------------------
341void ofPath::ellipse(const glm::vec3 & p, float width, float height){
342ellipse(p.x,p.y,p.z,width,height);
343}
344
345//----------------------------------------------------------
346void ofPath::ellipse(const glm::vec2 & p, float width, float height){
347ellipse(p.x,p.y,0.0,width,height);
348}
349
350//----------------------------------------------------------
351void ofPath::rectangle(const ofRectangle & r){
352moveTo(r.getTopLeft());
353lineTo(r.getTopRight());
354lineTo(r.getBottomRight());
355lineTo(r.getBottomLeft());
356close();
357}
358
359//----------------------------------------------------------
360void ofPath::rectangle(const glm::vec3 & p,float w,float h){
361moveTo(p);
362lineTo(p.x+w,p.y,p.z);
363lineTo(p.x+w,p.y+h,p.z);
364lineTo(p.x,p.y+h,p.z);
365close();
366}
367
368//----------------------------------------------------------
369void ofPath::rectangle(const glm::vec2 & p,float w,float h){
370rectangle(glm::vec3(p,0.0), w, h);
371}
372
373//----------------------------------------------------------
374void ofPath::rectangle(float x,float y,float w,float h){
375moveTo(x,y);
376lineTo(x+w,y);
377lineTo(x+w,y+h);
378lineTo(x,y+h);
379close();
380}
381
382//----------------------------------------------------------
383void ofPath::rectangle(float x,float y,float z,float w,float h){
384moveTo(x,y,z);
385lineTo(x+w,y,z);
386lineTo(x+w,y+h,z);
387lineTo(x,y+h,z);
388close();
389}
390
391//----------------------------------------------------------
392void ofPath::rectRounded(const ofRectangle & b, float r){
393rectRounded(b.x,b.y,0,b.width,b.height,r,r,r,r);
394}
395
396//----------------------------------------------------------
397void ofPath::rectRounded(const glm::vec3 & p, float w, float h, float r){
398rectRounded(p.x,p.y,p.z,w,h,r,r,r,r);
399}
400
401//----------------------------------------------------------
402void ofPath::rectRounded(const glm::vec2 & p, float w, float h, float r){
403rectRounded(p.x,p.y,0.0,w,h,r,r,r,r);
404}
405
406//----------------------------------------------------------
407void ofPath::rectRounded(float x, float y, float w, float h, float r){
408rectRounded(x,y,0.0f,w,h,r,r,r,r);
409}
410
411//----------------------------------------------------------
412void ofPath::rectRounded(const glm::vec3 & p, float w, float h, float topLeftRadius,
413float topRightRadius,
414float bottomRightRadius,
415float bottomLeftRadius){
416
417rectRounded(p.x,p.y,p.z,w,h,topLeftRadius,topRightRadius,bottomRightRadius,bottomLeftRadius);
418}
419
420//----------------------------------------------------------
421void ofPath::rectRounded(const glm::vec2 & p, float w, float h, float topLeftRadius,
422float topRightRadius,
423float bottomRightRadius,
424float bottomLeftRadius){
425
426rectRounded(p.x,p.y,0.0,w,h,topLeftRadius,topRightRadius,bottomRightRadius,bottomLeftRadius);
427}
428
429//----------------------------------------------------------
430void ofPath::rectRounded(const ofRectangle & b, float topLeftRadius,
431float topRightRadius,
432float bottomRightRadius,
433float bottomLeftRadius){
434rectRounded(b.x,b.y,0,b.width,b.height,topLeftRadius,topRightRadius,bottomRightRadius,bottomLeftRadius);
435}
436
437//----------------------------------------------------------
438void ofPath::rectRounded(float x, float y, float z, float w, float h, float topLeftRadius,
439float topRightRadius,
440float bottomRightRadius,
441float bottomLeftRadius){
442// since we support w / h < 0, canonicalize the rectangle for easier drawing
443if(w < 0.0f) {
444x += w;
445w *= -1.0f;
446}
447
448if(h < 0.0f) {
449y += h;
450h *= -1.0f;
451}
452
453// keep radii in check
454float maxRadius = std::min(w / 2.0f, h / 2.0f);
455topLeftRadius = std::min(topLeftRadius, maxRadius);
456topRightRadius = std::min(topRightRadius, maxRadius);
457bottomRightRadius = std::min(bottomRightRadius, maxRadius);
458bottomLeftRadius = std::min(bottomLeftRadius, maxRadius);
459
460// if all radii are ~= 0.0f, then render as a normal rectangle
461if((fabs(topLeftRadius) < std::numeric_limits<float>::epsilon()) &&
462(fabs(topRightRadius) < std::numeric_limits<float>::epsilon()) &&
463(fabs(bottomRightRadius) < std::numeric_limits<float>::epsilon()) &&
464(fabs(bottomLeftRadius) < std::numeric_limits<float>::epsilon())) {
465
466// rect mode respect happens in ofRect
467rectangle(x, y, z, w, h);
468} else {
469float left = x;
470float right = x + w;
471float top = y;
472float bottom = y + h;
473
474
475moveTo(left + topLeftRadius, top, z);
476
477// top right
478if(fabs(topRightRadius) >= std::numeric_limits<float>::epsilon()) {
479arc(right - topRightRadius, top + topRightRadius, z, topRightRadius, topRightRadius, 270, 360);
480} else {
481lineTo(right, top, z);
482}
483
484lineTo(right, bottom - bottomRightRadius);
485// bottom right
486if(fabs(bottomRightRadius) >= std::numeric_limits<float>::epsilon()) {
487arc(right - bottomRightRadius, bottom - bottomRightRadius, z, bottomRightRadius, bottomRightRadius, 0, 90);
488}
489
490lineTo(left + bottomLeftRadius, bottom, z);
491
492// bottom left
493if(fabs(bottomLeftRadius) >= std::numeric_limits<float>::epsilon()) {
494arc(left + bottomLeftRadius, bottom - bottomLeftRadius, z, bottomLeftRadius, bottomLeftRadius, 90, 180);
495}
496
497lineTo(left, top + topLeftRadius, z);
498
499// top left
500if(fabs(topLeftRadius) >= std::numeric_limits<float>::epsilon()) {
501arc(left + topLeftRadius, top + topLeftRadius, z, topLeftRadius, topLeftRadius, 180, 270);
502}
503close();
504
505}
506}
507
508//----------------------------------------------------------
509void ofPath::close(){
510if(mode==COMMANDS){
511addCommand(Command(Command::close));
512}else{
513lastPolyline().setClosed(true);
514}
515flagShapeChanged();
516}
517
518//----------------------------------------------------------
519void ofPath::setPolyWindingMode(ofPolyWindingMode newMode){
520if(windingMode != newMode){
521windingMode = newMode;
522bNeedsTessellation = true;
523}
524}
525
526//----------------------------------------------------------
527void ofPath::setFilled(bool hasFill){
528if(bFill != hasFill){
529bFill = hasFill;
530bNeedsTessellation = true;
531}
532}
533
534//----------------------------------------------------------
535void ofPath::setStrokeWidth(float width){
536strokeWidth = width;
537}
538
539//----------------------------------------------------------
540ofPolyline & ofPath::lastPolyline(){
541if(polylines.empty() || polylines.back().isClosed()){
542polylines.push_back(ofPolyline());
543}
544return polylines.back();
545}
546
547//----------------------------------------------------------
548vector<ofPath::Command> & ofPath::getCommands(){
549if(mode==POLYLINES){
550ofLogWarning("ofPath") << "getCommands(): trying to get path commands from shape with polylines only";
551}else{
552flagShapeChanged();
553}
554return commands;
555}
556
557//----------------------------------------------------------
558const vector<ofPath::Command> & ofPath::getCommands() const{
559if(mode==POLYLINES){
560ofLogWarning("ofPath") << "getCommands(): trying to get path commands from shape with polylines only";
561}
562return commands;
563}
564
565//----------------------------------------------------------
566ofPolyWindingMode ofPath::getWindingMode() const{
567return windingMode;
568}
569
570//----------------------------------------------------------
571bool ofPath::isFilled() const{
572return bFill;
573}
574
575//----------------------------------------------------------
576ofColor ofPath::getFillColor() const{
577return fillColor;
578}
579
580//----------------------------------------------------------
581ofColor ofPath::getStrokeColor() const{
582return strokeColor;
583}
584
585//----------------------------------------------------------
586float ofPath::getStrokeWidth() const{
587return strokeWidth;
588}
589
590//----------------------------------------------------------
591void ofPath::generatePolylinesFromCommands(){
592if(mode==POLYLINES || commands.empty()) return;
593if(bNeedsPolylinesGeneration || curveResolution!=prevCurveRes){
594prevCurveRes = curveResolution;
595
596polylines.clear();
597int j=-1;
598
599for(int i=0; i<(int)commands.size();i++){
600switch(commands[i].type){
601case Command::moveTo:
602polylines.push_back(ofPolyline());
603j++;
604polylines[j].addVertex(commands[i].to);
605break;
606case Command::lineTo:
607polylines[j].addVertex(commands[i].to);
608break;
609case Command::curveTo:
610polylines[j].curveTo(commands[i].to, curveResolution);
611break;
612case Command::bezierTo:
613polylines[j].bezierTo(commands[i].cp1,commands[i].cp2,commands[i].to, curveResolution);
614break;
615case Command::quadBezierTo:
616polylines[j].quadBezierTo(commands[i].cp1,commands[i].cp2,commands[i].to, curveResolution);
617break;
618case Command::arc:
619polylines[j].arc(commands[i].to,commands[i].radiusX,commands[i].radiusY,commands[i].angleBegin,commands[i].angleEnd, circleResolution);
620break;
621case Command::arcNegative:
622polylines[j].arcNegative(commands[i].to,commands[i].radiusX,commands[i].radiusY,commands[i].angleBegin,commands[i].angleEnd, circleResolution);
623break;
624case Command::close:
625polylines[j].setClosed(true);
626break;
627}
628}
629
630bNeedsPolylinesGeneration = false;
631bNeedsTessellation = true;
632}
633}
634
635//----------------------------------------------------------
636void ofPath::tessellate(){
637generatePolylinesFromCommands();
638if(!bNeedsTessellation || polylines.empty() || std::all_of(polylines.begin(), polylines.end(), [](const ofPolyline & p) {return p.getVertices().empty();})) return;
639if(bFill){
640tessellator.tessellateToMesh( polylines, windingMode, cachedTessellation);
641}
642if(hasOutline() && windingMode!=OF_POLY_WINDING_ODD){
643tessellator.tessellateToPolylines( polylines, windingMode, tessellatedContour);
644}
645bNeedsTessellation = false;
646}
647
648//----------------------------------------------------------
649const vector<ofPolyline> & ofPath::getOutline() const{
650if(windingMode!=OF_POLY_WINDING_ODD){
651const_cast<ofPath*>(this)->tessellate();
652return tessellatedContour;
653}else{
654const_cast<ofPath*>(this)->generatePolylinesFromCommands();
655return polylines;
656}
657}
658
659//----------------------------------------------------------
660const ofMesh & ofPath::getTessellation() const{
661const_cast<ofPath*>(this)->tessellate();
662return cachedTessellation;
663}
664
665//----------------------------------------------------------
666void ofPath::draw(float x, float y) const{
667ofGetCurrentRenderer()->draw(*this,x,y);
668}
669
670//----------------------------------------------------------
671void ofPath::draw() const{
672ofGetCurrentRenderer()->draw(*this);
673}
674
675//----------------------------------------------------------
676void ofPath::flagShapeChanged(){
677if(mode==COMMANDS){
678bHasChanged = true;
679bNeedsPolylinesGeneration = true;
680}else{
681bNeedsTessellation = true;
682}
683}
684
685bool ofPath::hasChanged(){
686if(mode==COMMANDS){
687bool changed = bHasChanged;
688bHasChanged = false;
689return changed;
690}else{
691return bNeedsTessellation;
692}
693}
694
695//----------------------------------------------------------
696void ofPath::setMode(Mode _mode){
697mode = _mode;
698}
699
700//----------------------------------------------------------
701ofPath::Mode ofPath::getMode() const {
702return mode;
703}
704
705//----------------------------------------------------------
706void ofPath::setCurveResolution(int _curveResolution){
707curveResolution = _curveResolution;
708}
709
710//----------------------------------------------------------
711int ofPath::getCurveResolution() const {
712return curveResolution;
713}
714
715//----------------------------------------------------------
716void ofPath::setCircleResolution(int res){
717circleResolution = res;
718}
719
720//----------------------------------------------------------
721int ofPath::getCircleResolution() const {
722return circleResolution;
723}
724
725//----------------------------------------------------------
726void ofPath::setArcResolution(int res){
727circleResolution = res;
728}
729
730//----------------------------------------------------------
731int ofPath::getArcResolution() const {
732return circleResolution;
733}
734
735//----------------------------------------------------------
736void ofPath::setUseShapeColor(bool useColor){
737bUseShapeColor = useColor;
738}
739
740//----------------------------------------------------------
741bool ofPath::getUseShapeColor() const {
742return bUseShapeColor;
743}
744
745//----------------------------------------------------------
746void ofPath::setColor( const ofColor& color ) {
747setFillColor( color );
748setStrokeColor( color );
749}
750
751//----------------------------------------------------------
752void ofPath::setHexColor( int hex ) {
753setColor( ofColor().fromHex( hex ) );
754}
755
756//----------------------------------------------------------
757void ofPath::setFillColor(const ofColor & color){
758setUseShapeColor(true);
759fillColor = color;
760}
761
762//----------------------------------------------------------
763void ofPath::setFillHexColor( int hex ) {
764setFillColor( ofColor().fromHex( hex ) );
765}
766
767//----------------------------------------------------------
768void ofPath::setStrokeColor(const ofColor & color){
769setUseShapeColor(true);
770strokeColor = color;
771}
772
773//----------------------------------------------------------
774void ofPath::setStrokeHexColor( int hex ) {
775setStrokeColor( ofColor().fromHex( hex ) );
776};
777
778//----------------------------------------------------------
779void ofPath::simplify(float tolerance){
780if(mode==COMMANDS) generatePolylinesFromCommands();
781for(int i=0;i<(int)polylines.size();i++){
782polylines[i].simplify(tolerance);
783}
784}
785
786//----------------------------------------------------------
787void ofPath::translate(const glm::vec3 & p){
788if(mode==COMMANDS){
789for(int j=0;j<(int)commands.size();j++){
790commands[j].to += p;
791if(commands[j].type==Command::bezierTo || commands[j].type==Command::quadBezierTo){
792commands[j].cp1 += p;
793commands[j].cp2 += p;
794}
795}
796}else{
797for(int i=0;i<(int)polylines.size();i++){
798for(int j=0;j<(int)polylines[i].size();j++){
799polylines[i][j] += p;
800}
801}
802}
803flagShapeChanged();
804}
805
806//----------------------------------------------------------
807void ofPath::translate(const glm::vec2 & p){
808translate(glm::vec3(p, 0.0));
809}
810
811//----------------------------------------------------------
812
813void ofPath::rotateDeg(float degrees, const glm::vec3& axis ){
814auto radians = ofDegToRad(degrees);
815if(mode==COMMANDS){
816for(int j=0;j<(int)commands.size();j++){
817commands[j].to = glm::rotate(commands[j].to, radians, axis);
818if(commands[j].type==Command::bezierTo || commands[j].type==Command::quadBezierTo){
819commands[j].cp1 = glm::rotate(commands[j].cp1, radians, axis);
820commands[j].cp2 = glm::rotate(commands[j].cp2, radians, axis);
821}
822if(commands[j].type==Command::arc || commands[j].type==Command::arcNegative){
823commands[j].angleBegin += degrees;
824commands[j].angleEnd += degrees;
825}
826}
827}else{
828for(int i=0;i<(int)polylines.size();i++){
829for(int j=0;j<(int)polylines[i].size();j++){
830polylines[i][j] = glm::rotate(toGlm(polylines[i][j]), radians, axis);
831}
832}
833}
834flagShapeChanged();
835}
836
837//----------------------------------------------------------
838void ofPath::rotateRad(float radians, const glm::vec3& axis ){
839rotateDeg(ofRadToDeg(radians), axis);
840}
841
842//----------------------------------------------------------
843void ofPath::rotate(float degrees, const glm::vec3& axis ){
844rotateDeg(degrees, axis);
845}
846
847//----------------------------------------------------------
848void ofPath::rotate(float degrees, const glm::vec2& axis ){
849rotateDeg(degrees, glm::vec3(axis, 0.0));
850}
851
852//----------------------------------------------------------
853void ofPath::rotateDeg(float degrees, const glm::vec2& axis){
854rotateDeg(degrees, glm::vec3(axis, 0.0));
855}
856
857//----------------------------------------------------------
858void ofPath::rotateRad(float radians, const glm::vec2& axis){
859rotateRad(radians, glm::vec3(axis, 0.0));
860}
861
862//----------------------------------------------------------
863void ofPath::scale(float x, float y){
864if(mode==COMMANDS){
865for(std::size_t j=0;j<commands.size();j++){
866commands[j].to.x*=x;
867commands[j].to.y*=y;
868if(commands[j].type==Command::bezierTo || commands[j].type==Command::quadBezierTo){
869commands[j].cp1.x*=x;
870commands[j].cp1.y*=y;
871commands[j].cp2.x*=x;
872commands[j].cp2.y*=y;
873}
874if(commands[j].type==Command::arc || commands[j].type==Command::arcNegative){
875commands[j].radiusX *= x;
876commands[j].radiusY *= y;
877}
878}
879}else{
880for(std::size_t i=0;i<polylines.size();i++){
881for(std::size_t j=0;j<polylines[i].size();j++){
882polylines[i][j].x*=x;
883polylines[i][j].y*=y;
884}
885}
886}
887flagShapeChanged();
888}
889
890void ofPath::append(const ofPath & path){
891if(mode==COMMANDS){
892for(auto & command: path.getCommands()){
893addCommand(command);
894}
895}else{
896for(auto & poly: path.getOutline()){
897polylines.push_back(poly);
898}
899}
900flagShapeChanged();
901}
902
903void ofPath::addCommand(const ofPath::Command & command){
904if((commands.empty() || commands.back().type==Command::close) && command.type!=Command::moveTo){
905commands.push_back(Command(Command::moveTo,command.to));
906}
907commands.push_back(command);
908}
909