Skip to content

Commit 70fd1db

Browse files
committed
fix for paths with all closed sub paths to use default winding mode of OF_POLY_WINDING_NONZERO with a function to set it differently.
1 parent 82fd0e0 commit 70fd1db

File tree

4 files changed

+54
-77
lines changed

4 files changed

+54
-77
lines changed

addons/ofxSvg/src/ofxSvg.cpp

Lines changed: 36 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,15 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
11061106
return;
11071107
}
11081108

1109+
// OF_POLY_WINDING_ODD
1110+
/// OF_POLY_WINDING_NONZERO
1111+
/// OF_POLY_WINDING_POSITIVE
1112+
/// OF_POLY_WINDING_NEGATIVE
1113+
/// OF_POLY_WINDING_ABS_GEQ_TWO
1114+
// aSvgPath->path.setPolyWindingMode(OF_POLY_WINDING_POSITIVE);
1115+
// aSvgPath->path.setPolyWindingMode(mDefaultPathWindingMode);
1116+
1117+
11091118
aSvgPath->path.setCircleResolution(mCircleResolution);
11101119
aSvgPath->path.setCurveResolution(mCurveResolution);
11111120

@@ -1137,6 +1146,9 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
11371146
glm::vec3 secondControlPoint = currentPos;
11381147
glm::vec3 qControlPoint = currentPos;
11391148

1149+
int numSubPathsClosed = 0;
1150+
int numSubPaths = 0;
1151+
11401152
auto convertToAbsolute = [](bool aBRelative, glm::vec3& aCurrentPos, std::vector<glm::vec3>& aposes) -> glm::vec3 {
11411153
for(auto& apos : aposes ) {
11421154
if( aBRelative ) {
@@ -1283,36 +1295,27 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
12831295
for( int ni = 0; ni < npositions.size(); ni++ ) {
12841296
ofLogVerbose("ofxSvg::_parsePath") << ni << "->" << npositions[ni];
12851297
}
1286-
// if( npositions.size() > 0 && bRelative ) {
1287-
// mCurrentPathPos = npositions[0];
1288-
// }
12891298
ctype = ofPath::Command::moveTo;
1299+
numSubPaths++;
12901300
} else if( cchar == 'v' || cchar == 'V' ) {
12911301

12921302
float xvalue = 0.f;
12931303
if( cchar == 'v' ) {
12941304
bRelative = true;
1295-
// npositions[0].x = 0.f;
12961305
} else {
1297-
// npositions[0].x = currentPos.x;
12981306
xvalue = currentPos.x;
12991307
}
13001308
npositions = parsePointsDefaultX(currentString,xvalue);
1301-
// npositions[0].y = ofToFloat(currentString);
13021309
//ofLogVerbose("ofxSvg") << cchar << " line to: " << npositions[0] << " current pos: " << currentPos;
13031310
ctype = ofPath::Command::lineTo;
13041311
} else if( cchar == 'H' || cchar == 'h' ) {
13051312
float yvalue = 0.f;
13061313
if( cchar == 'h' ) {
13071314
bRelative = true;
1308-
// npositions[0].y = 0.f;
13091315
} else {
1310-
// npositions[0].y = currentPos.y;
13111316
yvalue = currentPos.y;
13121317
}
13131318
npositions = parsePointsDefaultY(currentString,yvalue);
1314-
// npositions[0].x = ofToFloat(currentString);
1315-
13161319
ctype = ofPath::Command::lineTo;
13171320
} else if( cchar == 'L' || cchar == 'l' ) {
13181321
if( cchar == 'l' ) {
@@ -1401,7 +1404,6 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
14011404
// going to handle this below;
14021405
// inside the if( commandT == ofPath::Command::moveTo ) { check.
14031406
} else {
1404-
// currentPos = convertToAbsolute(bRelative, currentPos, npositions );
14051407
currentPos = convertToAbsolute(bRelative, currentPos, npositions );
14061408
}
14071409
}
@@ -1434,7 +1436,6 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
14341436
if( npositions.size() > 0 ) {
14351437
ofLogVerbose("ofxSvg::moveTo") << npositions[0] << " currentPos: " << currentPos;// << " path pos: " << aSvgPath->pos;
14361438
aSvgPath->path.moveTo(currentPos);
1437-
// mCenterPoints.push_back(npositions[0] + pathOffset);
14381439
}
14391440

14401441
if(npositions.size() > 1 ) {
@@ -1446,19 +1447,12 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
14461447

14471448
if( bLineToRelative ) {
14481449
for( int ki = 1; ki < npositions.size(); ki++ ) {
1449-
// auto newPos = npositions[ki] + cp;
14501450
currentPos += npositions[ki];
14511451
aSvgPath->path.lineTo(currentPos);
1452-
// cp = newPos;
14531452
}
1454-
// currentPos = cp;
1455-
14561453
} else {
14571454
for( int ki = 1; ki < npositions.size(); ki++ ) {
1458-
// mCPoints.push_back(npositions[ki]);
1459-
// ofLogVerbose("ofxSvg::lineTo") << ki << "--->" << npositions[ki];
14601455
aSvgPath->path.lineTo(npositions[ki]);
1461-
// mCenterPoints.push_back(npositions[ki] + pathOffset);
14621456
}
14631457
if(npositions.size() > 0 ) {
14641458
currentPos = npositions.back();
@@ -1484,12 +1478,11 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
14841478
}
14851479
}
14861480
}
1487-
// aSvgPath->path.moveTo(currentPos);
1488-
// aSvgPath->path.lineTo(currentPos);
14891481
} else if( commandT == ofPath::Command::close ) {
14901482
// ofLogNotice("ofxSvg") << "Closing the path";
14911483
// TODO: Not sure if we need to draw a line to the start point here
14921484
aSvgPath->path.close();
1485+
numSubPathsClosed++;
14931486
} else if( commandT == ofPath::Command::bezierTo ) {
14941487

14951488
if( cchar == 'S' || cchar == 's' ) {
@@ -1507,12 +1500,6 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
15071500
}
15081501

15091502
npositions = ppositions;
1510-
1511-
// if( npositions.size() == 2 ) {
1512-
// auto cp2 = (secondControlPoint - prevPos) * -1.f;
1513-
// cp2 += prevPos;
1514-
// npositions.insert(npositions.begin(), cp2 );
1515-
// }
15161503
}
15171504

15181505
auto tcpos = prevPos;
@@ -1525,19 +1512,7 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
15251512
mCPoints.push_back(npositions[k+0]);
15261513
mCPoints.push_back(npositions[k+1]);
15271514
tcpos = npositions[k+2];
1528-
1529-
// mCPoints.push_back(npositions[k+0]);
1530-
// mCPoints.push_back(npositions[k+1]);
1531-
// mCenterPoints.push_back(npositions[k+2]);
15321515
}
1533-
1534-
// mCPoints.insert( mCPoints.end(), npositions.begin(), npositions.end() );
1535-
1536-
// if( npositions.size() == 3 ) {
1537-
// aSvgPath->path.bezierTo(npositions[0], npositions[1], npositions[2]);
1538-
// }
1539-
//
1540-
// secondControlPoint = npositions[1];
15411516
} else if( commandT == ofPath::Command::quadBezierTo ) {
15421517
if( cchar == 'T' || cchar == 't' ) {
15431518
if( npositions.size() == 1 ) {
@@ -1564,9 +1539,7 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
15641539

15651540
glm::vec3 spt = prevPos;
15661541
glm::vec3 ept = npositions[3];
1567-
1568-
1569-
// glm::vec3 cpt(spt.x, ept.y, 0.0f);
1542+
15701543
auto cpt = glm::vec3(findArcCenter(spt, ept, radii.x, radii.y, xAxisRotation, largeArcFlag, sweepFlag ), 0.f);
15711544
auto windingOrder = _getWindingOrderOnArc( spt, cpt, ept );
15721545

@@ -1629,19 +1602,12 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
16291602
ofPolyline tline;
16301603

16311604
if( windingOrder < 0 ) {
1632-
// aSvgPath->path.arcNegative(cpt, radii.x, radii.y, glm::degrees(startAngle), glm::degrees(endAngle) );
16331605
tline.arcNegative(cpt, radii.x, radii.y, glm::degrees(startAngle-xrotRad), glm::degrees(endAngle-xrotRad), mCircleResolution );
1634-
// tline.arcNegative(cpt, radii.x, radii.y, glm::degrees(startAngle), glm::degrees(endAngle) );
1635-
// aSvgPath->path.arcNegative(cpt, radii.x, radii.y, glm::degrees(startAngle-xrotRad), glm::degrees(endAngle-xrotRad) );
16361606
} else {
16371607
tline.arc(cpt, radii.x, radii.y, glm::degrees(startAngle-xrotRad), glm::degrees(endAngle-xrotRad), mCircleResolution );
1638-
// tline.arc(cpt, radii.x, radii.y, glm::degrees(startAngle), glm::degrees(endAngle) );
1639-
// aSvgPath->path.arc(cpt, radii.x, radii.y, glm::degrees(startAngle-xrotRad), glm::degrees(endAngle-xrotRad) );
1640-
// aSvgPath->path.arc(cpt, radii.x, radii.y, glm::degrees(startAngle), glm::degrees(endAngle) );
16411608
}
16421609

16431610
// rotate based on x-axis rotation //
1644-
16451611
// aSvgPath->path.rotateRad(xrotRad, glm::vec3(0.0f, 0.0f, 1.f));
16461612

16471613
for( auto& pv : tline.getVertices() ) {
@@ -1666,11 +1632,6 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
16661632
}
16671633
}
16681634

1669-
// auto centers = findEllipseCenter( spt, ept, radii.x, radii.y );
1670-
1671-
// ofLogNotice("centers: ") << std::get<0>(centers) << " and " << std::get<1>(centers) << " spt: " << spt << " ept: " << ept << " radii: " << radii;
1672-
1673-
16741635
mCenterPoints.push_back(cpt);
16751636
// mCenterPoints.push_back(cpt);
16761637
npositions.clear();
@@ -1682,6 +1643,14 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
16821643
}
16831644

16841645
// ofLogNotice("ofxSvg") << "["<<cchar<<"]: " << currentString;
1646+
// ofLogNotice("ofxSvg::_parsepath") << " num closed: " << numSubPathsClosed << " / " << numSubPaths;
1647+
1648+
// If all of the paths are closed, then set the winding mode.
1649+
// We only set it on closed paths because other winding modes can force close the paths
1650+
// which is incorrect with open paths / polylines.
1651+
if( numSubPaths == numSubPathsClosed ) {
1652+
aSvgPath->path.setPolyWindingMode(mDefaultClosedPathWindingMode);
1653+
}
16851654

16861655
justInCase++;
16871656
}
@@ -1711,6 +1680,7 @@ ofxSvgCssClass ofxSvg::_parseStyle( ofXml& anode ) {
17111680
// now lets try to apply it to the path
17121681
auto& tCss = mSvgCss.getClass(className);
17131682
for( auto& tprop : tCss.properties ) {
1683+
// ofLogNotice("ofxSvg") << " adding property " << tprop.first << " value: " << tprop.second.srcString;
17141684
css.addProperty(tprop.first, tprop.second);
17151685
}
17161686
}
@@ -1723,7 +1693,7 @@ ofxSvgCssClass ofxSvg::_parseStyle( ofXml& anode ) {
17231693
// avoid the following
17241694
std::vector<std::string> reservedAtts = {
17251695
"d", "id", "xlink:href", "width", "height", "rx", "ry", "cx", "cy", "r", "style", "font-family",
1726-
"x","y","x1","y1","x2","y2"
1696+
"x","y","x1","y1","x2","y2","transform"
17271697
};
17281698

17291699
// lets try to do this a better way
@@ -1825,7 +1795,6 @@ glm::vec3 ofxSvg::_parseMatrixString(const std::string& input, const std::string
18251795
//--------------------------------------------------------------
18261796
glm::mat4 ofxSvg::setTransformFromSvgMatrixString( string aStr, std::shared_ptr<ofxSvgElement> aele ) {
18271797
ofLogVerbose("-----------ofxSvg::setTransformFromSvgMatrixString") << aele->getTypeAsString() << " name: " << aele->getName() +"----------------";
1828-
// aele->scale = glm::vec2(1.0f, 1.0f);
18291798
// aele->rotation = 0.0;
18301799
aele->setScale(1.f);
18311800
aele->mModelRotationPoint = glm::vec2(0.0f, 0.0f);
@@ -1834,7 +1803,6 @@ glm::mat4 ofxSvg::setTransformFromSvgMatrixString( string aStr, std::shared_ptr<
18341803

18351804
float trotation = 0.f;
18361805
glm::mat4 mat = glm::mat4(1.f);
1837-
// glm::mat4 gmat = mModelMatrix;
18381806

18391807
if( ofIsStringInString(aStr, "translate")) {
18401808
auto transStr = aStr;
@@ -1900,7 +1868,7 @@ glm::mat4 ofxSvg::setTransformFromSvgMatrixString( string aStr, std::shared_ptr<
19001868
// gmat = glm::scale(gmat, glm::vec3(aele->getScale().x, aele->getScale().y, 1.f));
19011869
}
19021870

1903-
glm::vec3 pos3 = mat * glm::vec4( aele->getPosition().x, aele->getPosition().y, 0.0f, 1.f );
1871+
// glm::vec3 pos3 = mat * glm::vec4( aele->getPosition().x, aele->getPosition().y, 0.0f, 1.f );
19041872
// pos3 = gmat * glm::vec4( aele->pos.x, aele->pos.y, 0.0f, 1.f );
19051873
// aele->pos.x = pos3.x;
19061874
// aele->pos.y = pos3.y;
@@ -1928,10 +1896,8 @@ glm::mat4 ofxSvg::setTransformFromSvgMatrixString( string aStr, std::shared_ptr<
19281896

19291897
aele->setPosition(matrixF[4], matrixF[5], 0.f);
19301898

1931-
19321899
float trotation = glm::degrees( atan2f(matrixF[1],matrixF[0]) );
19331900

1934-
19351901
// aele->scale.x = glm::sqrt(matrixF[0] * matrixF[0] + matrixF[1] * matrixF[1]);
19361902
// aele->scale.y = glm::sqrt(matrixF[2] * matrixF[2] + matrixF[3] * matrixF[3]);
19371903
float sx = glm::sqrt(matrixF[0] * matrixF[0] + matrixF[1] * matrixF[1]);
@@ -1956,11 +1922,6 @@ glm::mat4 ofxSvg::setTransformFromSvgMatrixString( string aStr, std::shared_ptr<
19561922

19571923
mat = glm::scale(mat, glm::vec3(aele->getScale().x, aele->getScale().y, 1.f));
19581924

1959-
// pos3 = mat * glm::vec4( aele->getPosition().x, aele->getPosition().y, 0.0f, 1.f );
1960-
// aele->setPosition( pos3.x, pos3.y, 0.0f);
1961-
// aele->pos.x = pos3.x;
1962-
// aele->pos.y = pos3.y;
1963-
19641925
ofLogVerbose("ofxSvg::setTransformFromSvgMatrixString") << "pos: " << aele->getPosition() << " rotation: " << trotation << " scale: " << aele->getScale();
19651926
}
19661927
}
@@ -2187,6 +2148,16 @@ void ofxSvg::setHasStroke(bool abStroke) {
21872148
}
21882149
}
21892150

2151+
//--------------------------------------------------------------
2152+
void ofxSvg::setDefaultClosedPathWindingMode( ofPolyWindingMode aWindingMode ) {
2153+
mDefaultClosedPathWindingMode = aWindingMode;
2154+
}
2155+
2156+
//--------------------------------------------------------------
2157+
ofPolyWindingMode ofxSvg::getDefaultClosedPathWindingMode() {
2158+
return mDefaultClosedPathWindingMode;
2159+
}
2160+
21902161
//--------------------------------------------------------------
21912162
std::shared_ptr<ofxSvgGroup> ofxSvg::addGroup(std::string aname) {
21922163
auto tgroup = std::make_shared<ofxSvgGroup>();

addons/ofxSvg/src/ofxSvg.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,14 @@ class ofxSvg : public ofxSvgGroup {
172172
/// \brief Get the current css used for items.
173173
/// \return ofxSvgCssClass.
174174
ofxSvgCssClass& getCurrentCss() { return mCurrentCss;}
175+
/// \brief Set the default winding mode of imported paths with all sub paths that are closed. The default is OF_POLY_WINDING_NONZERO.
176+
/// If not all of the sub paths are closed, the winding mode is not set.
177+
/// Must be called before load() to apply to newly imported paths.
178+
/// \param ofPolyWindingMode to be used as default.
179+
void setDefaultClosedPathWindingMode( ofPolyWindingMode aWindingMode );
180+
/// \brief Get the default winding mode of imported paths.
181+
/// \return ofPolyWindingMode.
182+
ofPolyWindingMode getDefaultClosedPathWindingMode();
175183

176184
ofxSvgCssStyleSheet& getCssStyleSheet() {return mSvgCss; }
177185
/// \brief Add a ofxSvgGroup to the document. This will also push back the group as current.
@@ -292,10 +300,6 @@ class ofxSvg : public ofxSvgGroup {
292300
/// \param shared_ptr<ofxSvgElement> aelement to be removed.
293301
/// \return bool true if element was found and removed.
294302
bool remove( std::shared_ptr<ofxSvgElement> aelement ) override;
295-
/// \brief Remove elements in a vector from this document or child groups.
296-
/// \param vector<shared_ptr<ofxSvgElement> > aelements Elements to be removed.
297-
/// \return bool true if all of the elements were found and removed.
298-
// bool remove( std::vector<std::shared_ptr<ofxSvgElement> > aelements ) override;
299303

300304
/// \brief Used for development to provide insight into anchor point / control point placements.
301305
virtual void drawDebug();
@@ -365,6 +369,8 @@ class ofxSvg : public ofxSvgGroup {
365369
static ofPath sDummyPath;
366370
mutable std::vector<ofPath> mPaths;
367371

372+
ofPolyWindingMode mDefaultClosedPathWindingMode = OF_POLY_WINDING_NONZERO;
373+
368374
};
369375

370376

addons/ofxSvg/src/ofxSvgElements.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -786,19 +786,19 @@ void ofxSvgText::TextSpan::applyStyle(ofxSvgCssClass& aclass) {
786786

787787
if( !aclass.hasProperty("color") ) {
788788
if( aclass.hasProperty("fill")) {
789-
aclass.addProperty("color", aclass.getColor("fill") );
789+
mSvgCssClass.addProperty("color", aclass.getColor("fill") );
790790
} else {
791-
aclass.addProperty("color", ofColor(0));
791+
mSvgCssClass.addProperty("color", ofColor(0));
792792
}
793793
}
794794

795795
alpha = 1.f;
796-
if( aclass.hasProperty("opacity")) {
797-
alpha = aclass.getFloatValue("opacity", 1.f);
796+
if( mSvgCssClass.hasProperty("opacity")) {
797+
alpha = mSvgCssClass.getFloatValue("opacity", 1.f);
798798
}
799799

800-
ofLogVerbose("ofxSvgText::TextSpan::applyStyle") << "text: " << text;
801-
ofLogVerbose("ofxSvgText::TextSpan::applyStyle") << " css class: " << aclass.toString() << std::endl;// << " color: " << color;
800+
ofLogVerbose("ofxSvgText::TextSpan::applyStyle") << "text: " << text << " has fill: " << mSvgCssClass.hasProperty("fill") << " color: " << getColor();
801+
ofLogVerbose("ofxSvgText::TextSpan::applyStyle") << " css class: " << mSvgCssClass.toString() << std::endl;// << " color: " << color;
802802

803803
}
804804

addons/ofxSvg/src/ofxSvgFontBook.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ bool ofxSvgFontBook::loadFont(const of::filesystem::path& aDirectory, ofxSvgCssC
105105
bf = false;
106106
}
107107

108-
ofLogNotice("ofxSvgFontBook") << __FUNCTION__ << " : " << fs.str() << " : starting off searching directory : " << fontsDirectory;
108+
ofLogVerbose("ofxSvgFontBook") << __FUNCTION__ << " : " << fs.str() << " : starting off searching directory : " << fontsDirectory;
109109
string tNewFontPath = "";
110110

111111
std::vector<std::string> subStrs;
@@ -193,7 +193,7 @@ bool ofxSvgFontBook::loadFont(const of::filesystem::path& aDirectory, ofxSvgCssC
193193

194194

195195

196-
ofLogNotice("ofxSvgFontBook") << __FUNCTION__ << " : Trying to load font from: " << tfontPath;
196+
ofLogVerbose("ofxSvgFontBook") << __FUNCTION__ << " : Trying to load font from: " << tfontPath;
197197

198198
if (tfontPath == "") {
199199
bFontLoadOk = false;

0 commit comments

Comments
 (0)