diff options
author | Fridrich Štrba <fridrich.strba@bluewin.ch> | 2013-07-09 17:15:18 +0200 |
---|---|---|
committer | Fridrich Štrba <fridrich.strba@bluewin.ch> | 2013-07-09 17:15:18 +0200 |
commit | 29d671d0cd7a1b777d9c5172c5794d59b8048402 (patch) | |
tree | 48449fe54ab1e21a39539f11407d943663e1c7df | |
parent | 78fdaaf690c6aa37e56cb8a06f29be02fcd140a1 (diff) |
Handling in a special ways uniform NURBS of degree 2 and 3
-rw-r--r-- | src/lib/VSDContentCollector.cpp | 320 | ||||
-rw-r--r-- | src/lib/VSDContentCollector.h | 8 |
2 files changed, 289 insertions, 39 deletions
diff --git a/src/lib/VSDContentCollector.cpp b/src/lib/VSDContentCollector.cpp index 4c53118..0217597 100644 --- a/src/lib/VSDContentCollector.cpp +++ b/src/lib/VSDContentCollector.cpp @@ -1311,45 +1311,230 @@ void libvisio::VSDContentCollector::collectArcTo(unsigned /* id */, unsigned lev } } -#define VSD_NUM_POLYLINES_PER_NURBS 200 - -void libvisio::VSDContentCollector::collectNURBSTo(unsigned /* id */, unsigned level, double x2, double y2, unsigned char xType, unsigned char yType, unsigned degree, std::vector<std::pair<double, double> > controlPoints, std::vector<double> knotVector, std::vector<double> weights) +void libvisio::VSDContentCollector::_generateCubicBeziersFromNURBS(const std::vector<std::pair<double, double> > &controlPoints, + const std::vector<double> &knotVector) { - _handleLevelChange(level); - - if (knotVector.empty() || controlPoints.empty() || weights.empty()) - // Here, maybe we should just draw line to (x2,y2) + if (controlPoints.empty() || knotVector.empty()) return; - // Fill in end knots - while (knotVector.size() < (controlPoints.size() + degree + 2)) - { - double tmpBack = knotVector.back(); - knotVector.push_back(tmpBack); + WPXPropertyList node; + + node.insert("libwpg:path-action", "M"); + double x = controlPoints[0].first; + double y = controlPoints[0].second; + transformPoint(x,y); + node.insert("svg:x", m_scale*x); + node.insert("svg:y", m_scale*y); + if (!m_noFill && !m_noShow) + m_currentFillGeometry.push_back(node); + if (!m_noLine && !m_noShow) + m_currentLineGeometry.push_back(node); + + /* Decomposition of a spline of 3rd degree into Bezier segments + * adapted from the algorithm DecomposeCurve (Les Piegl, Wayne Tiller: + * The NURBS Book, 2nd Edition, 1997 + */ + + unsigned m = controlPoints.size() + 4; + unsigned a = 3; + unsigned b = 4; + std::vector< std::pair<double, double> > Qw(4), NextQw(4); + unsigned i = 0; + for (; i <= 3; i++) + Qw[i] = controlPoints[i]; + while (b < m) + { + i = b; + while (b < m && knotVector[b+1] == knotVector[b]) + b++; + unsigned mult = b - i + 1; + if (mult < 3) + { + double numer = (double)(knotVector[b] - knotVector[a]); + unsigned j = 3; + std::map<unsigned, double> alphas; + for (; j >mult; j--) + alphas[j-mult-1] = numer/double(knotVector[a+j]-knotVector[a]); + unsigned r = 3 - mult; + for (j=1; j<=r; j++) + { + unsigned save = r - j; + unsigned s = mult+j; + for (unsigned k = 3; k>=s; k--) + { + double alpha = alphas[k-s]; + Qw[k].first = alpha*Qw[k].first + (1.0-alpha)*Qw[k-1].first; + Qw[k].second = alpha*Qw[k].second + (1.0-alpha)*Qw[k-1].second; + } + if (b < m) + { + NextQw[save].first = Qw[3].first; + NextQw[save].second = Qw[3].second; + } + } + } + // Pass the segment to the path + + node.clear(); + node.insert("libwpg:path-action", "C"); + x = Qw[1].first; + y = Qw[1].second; + transformPoint(x, y); + node.insert("svg:x1", m_scale*x); + node.insert("svg:y1", m_scale*y); + x = Qw[2].first; + y = Qw[2].second; + transformPoint(x, y); + node.insert("svg:x2", m_scale*x); + node.insert("svg:y2", m_scale*y); + x = Qw[3].first; + y = Qw[3].second; + transformPoint(x, y); + node.insert("svg:x", m_scale*x); + node.insert("svg:y", m_scale*y); + + if (!m_noFill && !m_noShow) + m_currentFillGeometry.push_back(node); + if (!m_noLine && !m_noShow) + m_currentLineGeometry.push_back(node); + + std::swap(Qw, NextQw); + + if (b < m) + { + for (i=3-mult; i <= 3; i++) + { + Qw[i].first = controlPoints[b-3+i].first; + Qw[i].second = controlPoints[b-3+i].second; + } + a = b; + b++; + } } + m_originalX = controlPoints.back().first; + m_originalY = controlPoints.back().second; + m_x = controlPoints.back().first; + m_y = controlPoints.back().second; + transformPoint(m_x, m_y); +} - // Convert control points to static co-ordinates - for (std::vector<std::pair<double, double> >::iterator it = controlPoints.begin(); - it != controlPoints.end(); ++it) - { - if (xType == 0) // Percentage - (*it).first *= m_xform.width; - if (yType == 0) // Percentage - (*it).second *= m_xform.height; +void libvisio::VSDContentCollector::_generateQuadraticBeziersFromNURBS(const std::vector<std::pair<double, double> > &controlPoints, + const std::vector<double> &knotVector) +{ + if (controlPoints.empty() || knotVector.empty()) + return; + + WPXPropertyList node; + + node.insert("libwpg:path-action", "M"); + double x = controlPoints[0].first; + double y = controlPoints[0].second; + transformPoint(x,y); + node.insert("svg:x", m_scale*x); + node.insert("svg:y", m_scale*y); + if (!m_noFill && !m_noShow) + m_currentFillGeometry.push_back(node); + if (!m_noLine && !m_noShow) + m_currentLineGeometry.push_back(node); + + + /* Decomposition of a spline of 2nd degree into Bezier segments + * adapted from the algorithm DecomposeCurve (Les Piegl, Wayne Tiller: + * The NURBS Book, 2nd Edition, 1997 + */ + + unsigned m = controlPoints.size() + 3; + unsigned a = 2; + unsigned b = 3; + std::vector< std::pair<double, double> > Qw(3), NextQw(3); + unsigned i = 0; + for (; i <= 2; i++) + Qw[i] = controlPoints[i]; + while (b < m) + { + i = b; + while (b < m && knotVector[b+1] == knotVector[b]) + b++; + unsigned mult = b - i + 1; + if (mult < 2) + { + double numer = (double)(knotVector[b] - knotVector[a]); + unsigned j = 2; + std::map<unsigned, double> alphas; + for (; j >mult; j--) + alphas[j-mult-1] = numer/double(knotVector[a+j]-knotVector[a]); + unsigned r = 2 - mult; + for (j=1; j<=r; j++) + { + unsigned save = r - j; + unsigned s = mult+j; + for (unsigned k = 2; k>=s; k--) + { + double alpha = alphas[k-s]; + Qw[k].first = alpha*Qw[k].first + (1.0-alpha)*Qw[k-1].first; + Qw[k].second = alpha*Qw[k].second + (1.0-alpha)*Qw[k-1].second; + } + if (b < m) + { + NextQw[save].first = Qw[2].first; + NextQw[save].second = Qw[2].second; + } + } + } + // Pass the segment to the path + + node.clear(); + node.insert("libwpg:path-action", "Q"); + x = Qw[1].first; + y = Qw[1].second; + transformPoint(x, y); + node.insert("svg:x1", m_scale*x); + node.insert("svg:y1", m_scale*y); + x = Qw[2].first; + y = Qw[2].second; + transformPoint(x, y); + node.insert("svg:x", m_scale*x); + node.insert("svg:y", m_scale*y); + + if (!m_noFill && !m_noShow) + m_currentFillGeometry.push_back(node); + if (!m_noLine && !m_noShow) + m_currentLineGeometry.push_back(node); + + std::swap(Qw, NextQw); + + if (b < m) + { + for (i=2-mult; i <= 2; i++) + { + Qw[i].first = controlPoints[b-2+i].first; + Qw[i].second = controlPoints[b-2+i].second; + } + a = b; + b++; + } } + m_originalX = controlPoints.back().first; + m_originalY = controlPoints.back().second; + m_x = controlPoints.back().first; + m_y = controlPoints.back().second; + transformPoint(m_x, m_y); +} - controlPoints.push_back(std::pair<double,double>(x2, y2)); - controlPoints.insert(controlPoints.begin(), std::pair<double, double>(m_originalX, m_originalY)); +#define VSD_NUM_POLYLINES_PER_NURBS 200 +void libvisio::VSDContentCollector::_generatePolylineFromNURBS(unsigned degree, const std::vector<std::pair<double, double> > &controlPoints, + const std::vector<double> &knotVector, const std::vector<double> &weights) +{ // Generate NURBS using VSD_NUM_POLYLINES_PER_NURBS polylines - WPXPropertyList NURBS; + WPXPropertyList node; double step = (knotVector.back() - knotVector[0]) / VSD_NUM_POLYLINES_PER_NURBS; for (unsigned i = 0; i < VSD_NUM_POLYLINES_PER_NURBS; i++) { - NURBS.clear(); - NURBS.insert("libwpg:path-action", "L"); + node.clear(); + node.insert("libwpg:path-action", "L"); double nextX = 0; double nextY = 0; double denominator = LIBVISIO_EPSILON; @@ -1364,27 +1549,84 @@ void libvisio::VSDContentCollector::collectNURBSTo(unsigned /* id */, unsigned l nextX = (nextX/denominator); nextY = (nextY/denominator); transformPoint(nextX, nextY); - NURBS.insert("svg:x", m_scale*nextX); - NURBS.insert("svg:y", m_scale*nextY); + node.insert("svg:x", m_scale*nextX); + node.insert("svg:y", m_scale*nextY); if (!m_noFill && !m_noShow) - m_currentFillGeometry.push_back(NURBS); + m_currentFillGeometry.push_back(node); if (!m_noLine && !m_noShow) - m_currentLineGeometry.push_back(NURBS); + m_currentLineGeometry.push_back(node); } - m_originalX = x2; - m_originalY = y2; - m_x = x2; - m_y = y2; + m_originalX = controlPoints.back().first; + m_originalY = controlPoints.back().second; + m_x = controlPoints.back().first; + m_y = controlPoints.back().second; transformPoint(m_x, m_y); - NURBS.clear(); - NURBS.insert("libwpg:path-action", "L"); - NURBS.insert("svg:x", m_scale*m_x); - NURBS.insert("svg:y", m_scale*m_y); + node.clear(); + node.insert("libwpg:path-action", "L"); + node.insert("svg:x", m_scale*m_x); + node.insert("svg:y", m_scale*m_y); if (!m_noFill && !m_noShow) - m_currentFillGeometry.push_back(NURBS); + m_currentFillGeometry.push_back(node); if (!m_noLine && !m_noShow) - m_currentLineGeometry.push_back(NURBS); + m_currentLineGeometry.push_back(node); +} + +bool libvisio::VSDContentCollector::_areWeightsUniform(const std::vector<double> weights) const +{ + if (weights.empty()) + return true; + double previousValue = weights[0]; + for (std::vector<double>::size_type i = 0; i < weights.size(); ++i) + { + if (fabs(weights[i] - previousValue < LIBVISIO_EPSILON)) + previousValue = weights[i]; + else + return false; + } + return true; +} + +void libvisio::VSDContentCollector::collectNURBSTo(unsigned /* id */, unsigned level, double x2, double y2, + unsigned char xType, unsigned char yType, unsigned degree, std::vector<std::pair<double, double> > controlPoints, + std::vector<double> knotVector, std::vector<double> weights) +{ + _handleLevelChange(level); + + if (knotVector.empty() || controlPoints.empty() || weights.empty()) + // Here, maybe we should just draw line to (x2,y2) + return; + + // Fill in end knots + while (knotVector.size() < (controlPoints.size() + degree + 3)) + { + double tmpBack = knotVector.back(); + knotVector.push_back(tmpBack); + } + + // Convert control points to static co-ordinates + for (std::vector<std::pair<double, double> >::iterator it = controlPoints.begin(); + it != controlPoints.end(); ++it) + { + if (xType == 0) // Percentage + (*it).first *= m_xform.width; + + if (yType == 0) // Percentage + (*it).second *= m_xform.height; + } + + controlPoints.push_back(std::pair<double,double>(x2, y2)); + controlPoints.insert(controlPoints.begin(), std::pair<double, double>(m_originalX, m_originalY)); + + if ((degree == 2 || degree == 3) && _areWeightsUniform(weights)) + { + if (degree == 2) + _generateQuadraticBeziersFromNURBS(controlPoints, knotVector); + else + _generateCubicBeziersFromNURBS(controlPoints, knotVector); + } + else + _generatePolylineFromNURBS(degree, controlPoints, knotVector, weights); } double libvisio::VSDContentCollector::_NURBSBasis(unsigned knot, unsigned degree, double point, const std::vector<double> &knotVector) diff --git a/src/lib/VSDContentCollector.h b/src/lib/VSDContentCollector.h index d0667e6..0d1fd27 100644 --- a/src/lib/VSDContentCollector.h +++ b/src/lib/VSDContentCollector.h @@ -220,6 +220,14 @@ private: bool parseFormatId( const char *formatString, unsigned short &result ); void _appendField(WPXString &text); + void _generateCubicBeziersFromNURBS(const std::vector<std::pair<double, double> > &controlPoints, + const std::vector<double> &knotVector); + void _generateQuadraticBeziersFromNURBS(const std::vector<std::pair<double, double> > &controlPoints, + const std::vector<double> &knotVector); + void _generatePolylineFromNURBS(unsigned degree, const std::vector<std::pair<double, double> > &controlPoints, + const std::vector<double> &knotVector, const std::vector<double> &weights); + bool _areWeightsUniform(const std::vector<double> weights) const; + bool m_isPageStarted; double m_pageWidth; double m_pageHeight; |