Skip to content

Commit 648aeda

Browse files
committed
more support for legacy transforms and more robust parsing.
1 parent 65c241c commit 648aeda

File tree

3 files changed

+157
-64
lines changed

3 files changed

+157
-64
lines changed

addons/ofxSvg/src/ofxSvg.cpp

Lines changed: 154 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -869,7 +869,7 @@ bool ofxSvg::_addElementFromXmlNode( ofXml& tnode, vector< shared_ptr<ofxSvgElem
869869
return true;
870870
}
871871

872-
std::vector<glm::vec3> parsePoints(const std::string& input) {
872+
std::vector<float> parseToFloats(const std::string& input) {
873873
std::vector<glm::vec3> points;
874874
std::regex regex("[-]?\\d*\\.?\\d+"); // Matches positive/negative floats
875875
std::sregex_iterator begin(input.begin(), input.end(), regex), end;
@@ -885,7 +885,29 @@ std::vector<glm::vec3> parsePoints(const std::string& input) {
885885
}
886886
}
887887

888-
// Create vec2 pairs from the values
888+
return values;
889+
}
890+
891+
std::vector<glm::vec3> parsePoints(const std::string& input) {
892+
// std::vector<glm::vec3> points;
893+
// std::regex regex("[-]?\\d*\\.?\\d+"); // Matches positive/negative floats
894+
// std::sregex_iterator begin(input.begin(), input.end(), regex), end;
895+
//
896+
// std::vector<float> values;
897+
//
898+
// // Extract all floating-point values using regex
899+
// for (std::sregex_iterator i = begin; i != end; ++i) {
900+
// try {
901+
// values.push_back(std::stof((*i).str()));
902+
// } catch (const std::invalid_argument&) {
903+
// std::cerr << "Invalid number found: " << (*i).str() << std::endl;
904+
// }
905+
// }
906+
907+
std::vector<glm::vec3> points;
908+
auto values = parseToFloats( input );
909+
910+
// Create vec3 pairs from the values
889911
for (size_t i = 0; i < values.size(); i += 2) {
890912
if (i + 1 < values.size()) {
891913
glm::vec3 point(values[i], values[i + 1], 0.f);
@@ -902,6 +924,44 @@ std::vector<glm::vec3> parsePoints(const std::string& input) {
902924
}
903925

904926

927+
std::vector<glm::vec3> parsePointsDefaultY(const std::string& input, float aYpos ) {
928+
std::vector<glm::vec3> points;
929+
auto values = parseToFloats( input );
930+
931+
// Create vec3 pairs from the values
932+
for (size_t i = 0; i < values.size(); i++) {
933+
glm::vec3 point(values[i], aYpos, 0.f);
934+
points.push_back(point);
935+
}
936+
937+
if( values.size() == 1 && points.size() < 1) {
938+
glm::vec3 point(values[0], aYpos, 0.f);
939+
points.push_back(point);
940+
}
941+
942+
return points;
943+
}
944+
945+
std::vector<glm::vec3> parsePointsDefaultX(const std::string& input, float aXpos ) {
946+
std::vector<glm::vec3> points;
947+
auto values = parseToFloats( input );
948+
949+
// Create vec3 pairs from the values
950+
for (size_t i = 0; i < values.size(); i++) {
951+
glm::vec3 point(aXpos, values[i], 0.f);
952+
points.push_back(point);
953+
}
954+
955+
if( values.size() == 1 && points.size() < 1) {
956+
glm::vec3 point(aXpos, values[0], 0.f);
957+
points.push_back(point);
958+
}
959+
960+
return points;
961+
}
962+
963+
964+
905965
//----------------------------------------------------
906966
std::vector<double> _parseSvgArc(const std::string& arcStr) {
907967
std::vector<double> result;
@@ -1027,8 +1087,7 @@ void ofxSvg::_parsePolylinePolygon( ofXml& tnode, std::shared_ptr<ofxSvgPath> aS
10271087
// reference: https://www.w3.org/TR/SVG2/paths.html#PathData
10281088
//--------------------------------------------------------------
10291089
void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
1030-
// path4160
1031-
1090+
// path27340-8
10321091

10331092
aSvgPath->path.clear();
10341093

@@ -1107,14 +1166,13 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
11071166
return aCurrentPos;
11081167
};
11091168

1110-
auto lineToRelativeRecursive = [](glm::vec3& aStartPos, glm::vec3& acurrentPos, std::vector<glm::vec3>& aposes, std::shared_ptr<ofxSvgPath> aPath ) {
1111-
// int ncounter = 0;
1169+
auto lineToRelativeFromAbsoluteRecursive = [](glm::vec3& aStartPos, glm::vec3& acurrentPos, std::vector<glm::vec3>& aposes, std::shared_ptr<ofxSvgPath> aPath ) {
1170+
// int ncounter = 0;
11121171
auto cp = aStartPos;
11131172
for( auto& np : aposes ) {
11141173
auto relativePos = np-aStartPos;
1174+
// auto relativePos = np;
11151175
auto newPos = relativePos + cp;
1116-
// ofLogNotice("ofxSvg::_parsePath") << ncounter << " - l: " << prevPos << " cp: " << cp << " np: " << np;
1117-
// ofLogVerbose("ofxSvg::_parsePath") << ncounter << " - l: " << prevPos << " np: " << np << " relative: " << relativePos << " newPos: " << newPos << " currentPos: " << currentPos;
11181176
aPath->path.lineTo(newPos);
11191177
cp = newPos;//relativePos+prevPos;
11201178
// ncounter++;
@@ -1131,6 +1189,8 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
11311189

11321190
aSvgPath->path.clear();
11331191

1192+
// auto prevCmd = ofPath::Command::close;
1193+
11341194
unsigned int justInCase = 0;
11351195
// std::vector<ofPath::Command> commands;
11361196
bool breakMe = false;
@@ -1212,31 +1272,37 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
12121272
}
12131273
npositions = parsePoints(currentString);
12141274
for( int ni = 0; ni < npositions.size(); ni++ ) {
1215-
ofLogVerbose("ofxSvg::_parsePath") << ni << "-" << npositions[ni];
1275+
ofLogVerbose("ofxSvg::_parsePath") << ni << "->" << npositions[ni];
12161276
}
12171277
// if( npositions.size() > 0 && bRelative ) {
12181278
// mCurrentPathPos = npositions[0];
12191279
// }
12201280
ctype = ofPath::Command::moveTo;
12211281
} else if( cchar == 'v' || cchar == 'V' ) {
1282+
1283+
float xvalue = 0.f;
12221284
if( cchar == 'v' ) {
12231285
bRelative = true;
1224-
npositions[0].x = 0.f;
1286+
// npositions[0].x = 0.f;
12251287
} else {
1226-
npositions[0].x = currentPos.x;
1288+
// npositions[0].x = currentPos.x;
1289+
xvalue = currentPos.x;
12271290
}
1228-
1229-
npositions[0].y = ofToFloat(currentString);
1291+
npositions = parsePointsDefaultX(currentString,xvalue);
1292+
// npositions[0].y = ofToFloat(currentString);
12301293
//ofLogVerbose("ofxSvg") << cchar << " line to: " << npositions[0] << " current pos: " << currentPos;
12311294
ctype = ofPath::Command::lineTo;
12321295
} else if( cchar == 'H' || cchar == 'h' ) {
1296+
float yvalue = 0.f;
12331297
if( cchar == 'h' ) {
12341298
bRelative = true;
1235-
npositions[0].y = 0.f;
1299+
// npositions[0].y = 0.f;
12361300
} else {
1237-
npositions[0].y = currentPos.y;
1301+
// npositions[0].y = currentPos.y;
1302+
yvalue = currentPos.y;
12381303
}
1239-
npositions[0].x = ofToFloat(currentString);
1304+
npositions = parsePointsDefaultY(currentString,yvalue);
1305+
// npositions[0].x = ofToFloat(currentString);
12401306

12411307
ctype = ofPath::Command::lineTo;
12421308
} else if( cchar == 'L' || cchar == 'l' ) {
@@ -1300,12 +1366,7 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
13001366

13011367
if( ctype.has_value() ) {
13021368

1303-
// for( auto& np : npositions ) {
1304-
// ofLogNotice("ofxSvg") << cchar << " position: " << np;
1305-
// }
1306-
13071369
auto prevPos = currentPos;
1308-
13091370
auto commandT = ctype.value();
13101371

13111372
if( commandT == ofPath::Command::arc ) {
@@ -1326,84 +1387,99 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
13261387
// TODO: Check quad bezier for poly bezier like cubic bezier
13271388

13281389
} else {
1329-
// if( commandT == ofPath::Command::moveTo ) {
1330-
// ofLogNotice("ofxSvg") << "before current pos is altered: move to: " << npositions[0] << " current Pos: " << currentPos << " relative: " << bRelative;
1331-
// }
13321390
if( npositions.size() > 0 && commandT != ofPath::Command::close ) {
1333-
if( commandT != ofPath::Command::moveTo ) {
1334-
//currentPos = {0.f, 0.f, 0.f};
1335-
}
1391+
if( commandT == ofPath::Command::moveTo ) {
1392+
// going to handle this below;
1393+
// inside the if( commandT == ofPath::Command::moveTo ) { check.
1394+
} else {
13361395
// currentPos = convertToAbsolute(bRelative, currentPos, npositions );
1337-
currentPos = convertToAbsolute(bRelative, currentPos, npositions );
1396+
currentPos = convertToAbsolute(bRelative, currentPos, npositions );
1397+
}
13381398
}
13391399
}
13401400

1341-
// if( npositions.size() > 0 ) {
1342-
// ofLogNotice("ofxSvg") << "before current pos is altered: move to: " << npositions[0] << " current Pos: " << currentPos << " relative: " << bRelative;
1343-
// }
1344-
13451401
if( commandT != ofPath::Command::bezierTo ) {
13461402
secondControlPoint = currentPos;
13471403
}
13481404
if( commandT != ofPath::Command::quadBezierTo ) {
13491405
qControlPoint = currentPos;
13501406
}
1351-
1407+
13521408
if( commandT == ofPath::Command::moveTo ) {
1353-
aSvgPath->path.moveTo(currentPos);
1409+
1410+
if( cchar == 'm' ) {
1411+
if(npositions.size() > 0 ) {
1412+
if(cindex == 0 ) {
1413+
// this is the first m, so the moveTo is absolute but the subsequent points are relative
1414+
currentPos = npositions[0];
1415+
} else {
1416+
currentPos += npositions[0];
1417+
}
1418+
}
1419+
} else {
1420+
if(npositions.size() > 0 ) {
1421+
currentPos = npositions[0];
1422+
}
1423+
}
1424+
1425+
if( npositions.size() > 0 ) {
1426+
ofLogVerbose("ofxSvg::moveTo") << npositions[0] << " currentPos: " << currentPos;// << " path pos: " << aSvgPath->pos;
1427+
aSvgPath->path.moveTo(currentPos);
1428+
// mCenterPoints.push_back(npositions[0] + pathOffset);
1429+
}
1430+
13541431
if(npositions.size() > 1 ) {
1355-
13561432
bool bLineToRelative = bRelative;
13571433
// determine if these points started with m and is the first character
1358-
if( cchar == 'm' && cindex == 0 ) {
1434+
if( cchar == 'm') {
13591435
bLineToRelative = true;
13601436
}
13611437

13621438
if( bLineToRelative ) {
1363-
auto newPoses = npositions;
1364-
newPoses.erase(newPoses.begin());
1365-
lineToRelativeRecursive(prevPos, currentPos, newPoses, aSvgPath);
1439+
for( int ki = 1; ki < npositions.size(); ki++ ) {
1440+
// auto newPos = npositions[ki] + cp;
1441+
currentPos += npositions[ki];
1442+
aSvgPath->path.lineTo(currentPos);
1443+
// cp = newPos;
1444+
}
1445+
// currentPos = cp;
1446+
13661447
} else {
13671448
for( int ki = 1; ki < npositions.size(); ki++ ) {
1449+
// mCPoints.push_back(npositions[ki]);
1450+
// ofLogVerbose("ofxSvg::lineTo") << ki << "--->" << npositions[ki];
13681451
aSvgPath->path.lineTo(npositions[ki]);
1452+
// mCenterPoints.push_back(npositions[ki] + pathOffset);
1453+
}
1454+
if(npositions.size() > 0 ) {
1455+
currentPos = npositions.back();
13691456
}
13701457
}
13711458
}
1459+
1460+
secondControlPoint = currentPos;
1461+
qControlPoint = currentPos;
1462+
13721463
} else if( commandT == ofPath::Command::lineTo ) {
13731464
if( npositions.size() > 0 ) {
13741465
// current pos is already set above
13751466
// so just worry about adding paths
13761467
if( bRelative ) {
1377-
1378-
lineToRelativeRecursive(prevPos, currentPos, npositions, aSvgPath );
1379-
// mCenterPoints.clear();
1380-
// int ncounter = 0;
1381-
// auto cp = prevPos;
1382-
// for( auto& np : npositions ) {
1383-
// auto relativePos = np-prevPos;
1384-
// auto newPos = relativePos + cp;
1385-
//// ofLogNotice("ofxSvg::_parsePath") << ncounter << " - l: " << prevPos << " cp: " << cp << " np: " << np;
1386-
//// ofLogVerbose("ofxSvg::_parsePath") << ncounter << " - l: " << prevPos << " np: " << np << " relative: " << relativePos << " newPos: " << newPos << " currentPos: " << currentPos;
1387-
// aSvgPath->path.lineTo(newPos);
1388-
// mCenterPoints.push_back(newPos);
1389-
// cp = newPos;//relativePos+prevPos;
1390-
// ncounter++;
1391-
// }
1392-
// currentPos = cp;
1393-
// // }
1468+
lineToRelativeFromAbsoluteRecursive(prevPos, currentPos, npositions, aSvgPath );
13941469
} else {
1395-
// int ncounter = 0;
1470+
int ncounter = 0;
13961471
for( auto& np : npositions ) {
1397-
// ofLogVerbose("ofxSvg::_parsePath") << ncounter << " - l: " << prevPos << " np: " << np;
1472+
ofLogVerbose("ofxSvg::lineTo") << ncounter << "--->"<< np << " prevPos:" << prevPos;
13981473
aSvgPath->path.lineTo(np);
1399-
// ncounter++;
1474+
ncounter++;
14001475
}
14011476
}
14021477
}
14031478
// aSvgPath->path.moveTo(currentPos);
14041479
// aSvgPath->path.lineTo(currentPos);
14051480
} else if( commandT == ofPath::Command::close ) {
14061481
// ofLogNotice("ofxSvg") << "Closing the path";
1482+
// TODO: Not sure if we need to draw a line to the start point here
14071483
aSvgPath->path.close();
14081484
} else if( commandT == ofPath::Command::bezierTo ) {
14091485

@@ -1595,6 +1671,7 @@ void ofxSvg::_parsePath( ofXml& tnode, std::shared_ptr<ofxSvgPath> aSvgPath ) {
15951671
}
15961672
}
15971673

1674+
// prevCmd = commandT;
15981675
// mCenterPoints.push_back(currentPos);
15991676
// mCPoints.insert( mCPoints.end(), npositions.begin(), npositions.end() );
16001677
}
@@ -1859,21 +1936,33 @@ glm::mat4 ofxSvg::setTransformFromSvgMatrixString( string aStr, std::shared_ptr<
18591936
vector<float> matrixF;
18601937
for(std::size_t i = 0; i < matrixNum.size(); i++){
18611938
matrixF.push_back(ofToFloat(matrixNum[i]));
1862-
std::cout << aele->getCleanName() << " matrix[" << i << "] = " << matrixF[i] << " string version is " << matrixNum[i] << std::endl;
1939+
ofLogVerbose("ofxSvg::setTransformFromSvgMatrixString") << aele->getCleanName() << " matrix[" << i << "] = " << matrixF[i] << " string version is " << matrixNum[i];
18631940
}
18641941

18651942
if( matrixNum.size() == 6 ) {
18661943

18671944
mat = glm::translate(glm::mat4(1.0f), glm::vec3(matrixF[4], matrixF[5], 0.0f));
18681945

18691946
aele->rotation = glm::degrees( atan2f(matrixF[1],matrixF[0]) );
1947+
1948+
1949+
aele->scale.x = glm::sqrt(matrixF[0] * matrixF[0] + matrixF[1] * matrixF[1]);
1950+
aele->scale.y = glm::sqrt(matrixF[2] * matrixF[2] + matrixF[3] * matrixF[3]);
1951+
1952+
if (matrixF[0] < 0) aele->scale.x *= -1.f;
1953+
if (matrixF[3] < 0) aele->scale.y *= -1.f;
1954+
1955+
// Avoid double-rotating when both scale = -1 and rotation = 180
1956+
if (aele->scale.x < 0 && aele->scale.y < 0 && glm::abs(aele->rotation - 180.0f) < 0.01f) {
1957+
aele->rotation = 0.f;
1958+
}
1959+
18701960
if( aele->rotation != 0.f ) {
18711961
// mat = mat * glm::toMat4((const glm::quat&)glm::angleAxis(glm::radians(aele->rotation), glm::vec3(0.f, 0.f, 1.f)));
18721962
mat = glm::rotate(mat, glm::radians(aele->rotation), glm::vec3(0.f, 0.f, 1.f));
18731963
}
18741964

1875-
aele->scale.x = glm::sqrt(matrixF[0] * matrixF[0] + matrixF[1] * matrixF[1]);
1876-
aele->scale.y = glm::sqrt(matrixF[2] * matrixF[2] + matrixF[3] * matrixF[3]);
1965+
18771966

18781967
mat = glm::scale(mat, glm::vec3(aele->scale.x, aele->scale.y, 1.f));
18791968

@@ -1882,6 +1971,8 @@ glm::mat4 ofxSvg::setTransformFromSvgMatrixString( string aStr, std::shared_ptr<
18821971
aele->pos.x = pos3.x;
18831972
aele->pos.y = pos3.y;
18841973

1974+
ofLogNotice("ofxSvg::setTransformFromSvgMatrixString") << "pos: " << aele->pos << " rotation: " << aele->rotation << " scale: " << aele->scale;
1975+
18851976
// apos.x = matrixF[4];
18861977
// apos.y = matrixF[5];
18871978
//
@@ -2383,7 +2474,7 @@ void ofxSvg::drawDebug() {
23832474
// cindex ++;
23842475
// }
23852476
// ofFill();
2386-
2477+
23872478
for( std::size_t k = 0; k < mCPoints.size(); k += 3 ) {
23882479
ofSetColor( ofColor::orange );
23892480
ofDrawCircle( mCPoints[k+0], 6.f );

addons/ofxSvg/src/ofxSvgElements.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ void ofxSvgPath::draw() {
266266
//--------------------------------------------------------------
267267
void ofxSvgImage::draw() {
268268
if( !bTryLoad ) {
269-
if( getFilePath().empty() ) {
269+
if( !getFilePath().empty() ) {
270270
img.load( getFilePath() );
271271
bTryLoad = true;
272272
}

0 commit comments

Comments
 (0)