diff options
author | Fridrich Štrba <fridrich.strba@bluewin.ch> | 2013-07-11 17:12:32 +0200 |
---|---|---|
committer | Fridrich Štrba <fridrich.strba@bluewin.ch> | 2013-07-11 17:12:32 +0200 |
commit | 20179ba61a98c1b9896c751d847838383e968d23 (patch) | |
tree | 7396489e56c036f474140037fd529d653a60d59f /src | |
parent | df155ad555c5e4eb9c991d7e08b5a7816e399257 (diff) |
Some more code towards handling arrw
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/CDRContentCollector.cpp | 38 | ||||
-rw-r--r-- | src/lib/CDRPath.cpp | 325 | ||||
-rw-r--r-- | src/lib/CDRPath.h | 1 |
3 files changed, 364 insertions, 0 deletions
diff --git a/src/lib/CDRContentCollector.cpp b/src/lib/CDRContentCollector.cpp index ee82c3a..5dd33e2 100644 --- a/src/lib/CDRContentCollector.cpp +++ b/src/lib/CDRContentCollector.cpp @@ -1026,8 +1026,46 @@ void libcdr::CDRContentCollector::_lineProperties(WPXPropertyList &propList) propList.insert("svg:stroke-width", 0.0); propList.insert("svg:stroke-color", "#000000"); } + } + // Deal with line markers (arrows, etc.) + if (!m_currentLineStyle.startMarker.empty()) + { + CDRPath startMarker(m_currentLineStyle.startMarker); + startMarker.transform(m_currentTransforms); + if (!m_groupTransforms.empty()) + startMarker.transform(m_groupTransforms.top()); + CDRTransform tmpTrafo(1.0, 0.0, -m_page.offsetX, 0.0, 1.0, -m_page.offsetY); + startMarker.transform(tmpTrafo); + tmpTrafo = CDRTransform(1.0, 0.0, 0.0, 0.0, -1.0, m_page.height); + startMarker.transform(tmpTrafo); + WPXString path, viewBox; + double width; + startMarker.writeOut(path, viewBox, width); + propList.insert("draw:marker-start-viewbox", viewBox); + propList.insert("draw:marker-start-path", path); + propList.insert("draw:marker-start-width", width); } + if (!m_currentLineStyle.endMarker.empty()) + { + CDRPath endMarker(m_currentLineStyle.endMarker); + endMarker.transform(m_currentTransforms); + if (!m_groupTransforms.empty()) + endMarker.transform(m_groupTransforms.top()); + CDRTransform tmpTrafo(1.0, 0.0, -m_page.offsetX, 0.0, 1.0, -m_page.offsetY); + endMarker.transform(tmpTrafo); + tmpTrafo = CDRTransform(1.0, 0.0, 0.0, 0.0, -1.0, m_page.height); + endMarker.transform(tmpTrafo); + WPXString path, viewBox; + double width; + endMarker.writeOut(path, viewBox, width); + propList.insert("draw:marker-end-viewbox", viewBox); + propList.insert("draw:marker-end-path", path); + propList.insert("draw:marker-end-width", width); + } + + + } void libcdr::CDRContentCollector::_generateBitmapFromPattern(WPXBinaryData &bitmap, const CDRPattern &pattern, const CDRColor &fgColor, const CDRColor &bgColor) diff --git a/src/lib/CDRPath.cpp b/src/lib/CDRPath.cpp index c9e86db..6446096 100644 --- a/src/lib/CDRPath.cpp +++ b/src/lib/CDRPath.cpp @@ -40,6 +40,206 @@ #define DEBUG_SPLINES 0 #endif +namespace +{ + +static inline double getAngle(double bx, double by) +{ + return fmod(2*M_PI + (by > 0.0 ? 1.0 : -1.0) * acos( bx / sqrt(bx * bx + by * by) ), 2*M_PI); +} + +static void getEllipticalArcBBox(double x0, double y0, + double rx, double ry, double phi, bool largeArc, bool sweep, double x, double y, + double &xmin, double &ymin, double &xmax, double &ymax) +{ + phi *= M_PI/180; + if (rx < 0.0) + rx *= -1.0; + if (ry < 0.0) + ry *= -1.0; + + if (rx == 0.0 || ry == 0.0) + { + xmin = (x0 < x ? x0 : x); + xmax = (x0 > x ? x0 : x); + ymin = (y0 < y ? y0 : y); + ymax = (y0 > y ? y0 : y); + return; + } + + const double x1prime = cos(phi)*(x0 - x)/2 + sin(phi)*(y0 - y)/2; + const double y1prime = -sin(phi)*(x0 - x)/2 + cos(phi)*(y0 - y)/2; + + double radicant = (rx*rx*ry*ry - rx*rx*y1prime*y1prime - ry*ry*x1prime*x1prime)/(rx*rx*y1prime*y1prime + ry*ry*x1prime*x1prime); + double cxprime = 0.0; + double cyprime = 0.0; + if (radicant < 0.0) + { + double ratio = rx/ry; + radicant = y1prime*y1prime + x1prime*x1prime/(ratio*ratio); + if (radicant < 0.0) + { + xmin = (x0 < x ? x0 : x); + xmax = (x0 > x ? x0 : x); + ymin = (y0 < y ? y0 : y); + ymax = (y0 > y ? y0 : y); + return; + } + ry=sqrt(radicant); + rx=ratio*ry; + } + else + { + double factor = (largeArc==sweep ? -1.0 : 1.0)*sqrt(radicant); + + cxprime = factor*rx*y1prime/ry; + cyprime = -factor*ry*x1prime/rx; + } + + double cx = cxprime*cos(phi) - cyprime*sin(phi) + (x0 + x)/2; + double cy = cxprime*sin(phi) + cyprime*cos(phi) + (y0 + y)/2; + + double txmin, txmax, tymin, tymax; + + if (phi == 0 || phi == M_PI) + { + xmin = cx - rx; + txmin = getAngle(-rx, 0); + xmax = cx + rx; + txmax = getAngle(rx, 0); + ymin = cy - ry; + tymin = getAngle(0, -ry); + ymax = cy + ry; + tymax = getAngle(0, ry); + } + else if (phi == M_PI / 2.0 || phi == 3.0*M_PI/2.0) + { + xmin = cx - ry; + txmin = getAngle(-ry, 0); + xmax = cx + ry; + txmax = getAngle(ry, 0); + ymin = cy - rx; + tymin = getAngle(0, -rx); + ymax = cy + rx; + tymax = getAngle(0, rx); + } + else + { + txmin = -atan(ry*tan(phi)/rx); + txmax = M_PI - atan (ry*tan(phi)/rx); + xmin = cx + rx*cos(txmin)*cos(phi) - ry*sin(txmin)*sin(phi); + xmax = cx + rx*cos(txmax)*cos(phi) - ry*sin(txmax)*sin(phi); + double tmpY = cy + rx*cos(txmin)*sin(phi) + ry*sin(txmin)*cos(phi); + txmin = getAngle(xmin - cx, tmpY - cy); + tmpY = cy + rx*cos(txmax)*sin(phi) + ry*sin(txmax)*cos(phi); + txmax = getAngle(xmax - cx, tmpY - cy); + + tymin = atan(ry/(tan(phi)*rx)); + tymax = atan(ry/(tan(phi)*rx))+M_PI; + ymin = cy + rx*cos(tymin)*sin(phi) + ry*sin(tymin)*cos(phi); + ymax = cy + rx*cos(tymax)*sin(phi) + ry*sin(tymax)*cos(phi); + double tmpX = cx + rx*cos(tymin)*cos(phi) - ry*sin(tymin)*sin(phi); + tymin = getAngle(tmpX - cx, ymin - cy); + tmpX = cx + rx*cos(tymax)*cos(phi) - ry*sin(tymax)*sin(phi); + tymax = getAngle(tmpX - cx, ymax - cy); + } + if (xmin > xmax) + { + std::swap(xmin,xmax); + std::swap(txmin,txmax); + } + if (ymin > ymax) + { + std::swap(ymin,ymax); + std::swap(tymin,tymax); + } + double angle1 = getAngle(x0 - cx, y0 - cy); + double angle2 = getAngle(x - cx, y - cy); + + if (!sweep) + std::swap(angle1, angle2); + + bool otherArc = false; + if (angle1 > angle2) + { + std::swap(angle1, angle2); + otherArc = true; + } + + if ((!otherArc && (angle1 > txmin || angle2 < txmin)) || (otherArc && !(angle1 > txmin || angle2 < txmin))) + xmin = x0 < x ? x0 : x; + if ((!otherArc && (angle1 > txmax || angle2 < txmax)) || (otherArc && !(angle1 > txmax || angle2 < txmax))) + xmax = x0 > x ? x0 : x; + if ((!otherArc && (angle1 > tymin || angle2 < tymin)) || (otherArc && !(angle1 > tymin || angle2 < tymin))) + ymin = y0 < y ? y0 : y; + if ((!otherArc && (angle1 > tymax || angle2 < tymax)) || (otherArc && !(angle1 > tymax || angle2 < tymax))) + ymax = y0 > y ? y0 : y; +} + +static inline double quadraticExtreme(double t, double a, double b, double c) +{ + return (1.0-t)*(1.0-t)*a + 2.0*(1.0-t)*t*b + t*t*c; +} + +static inline double quadraticDerivative(double a, double b, double c) +{ + double denominator = a - 2.0*b + c; + if (fabs(denominator) != 0.0) + return (a - b)/denominator; + return -1.0; +} + +static void getQuadraticBezierBBox(double x0, double y0, double x1, double y1, double x, double y, + double &xmin, double &ymin, double &xmax, double &ymax) +{ + xmin = x0 < x ? x0 : x; + xmax = x0 > x ? x0 : x; + ymin = y0 < y ? y0 : y; + ymax = y0 > y ? y0 : y; + + double t = quadraticDerivative(x0, x1, x); + if(t>=0 && t<=1) + { + double tmpx = quadraticExtreme(t, x0, x1, x); + xmin = tmpx < xmin ? tmpx : xmin; + xmax = tmpx > xmax ? tmpx : xmax; + } + + t = quadraticDerivative(y0, y1, y); + if(t>=0 && t<=1) + { + double tmpy = quadraticExtreme(t, y0, y1, y); + ymin = tmpy < ymin ? tmpy : ymin; + ymax = tmpy > ymax ? tmpy : ymax; + } +} + +static inline double cubicBase(double t, double a, double b, double c, double d) +{ + return (1.0-t)*(1.0-t)*(1.0-t)*a + 3.0*(1.0-t)*(1.0-t)*t*b + 3.0*(1.0-t)*t*t*c + t*t*t*d; +} + +static void getCubicBezierBBox(double x0, double y0, double x1, double y1, double x2, double y2, double x, double y, + double &xmin, double &ymin, double &xmax, double &ymax) +{ + xmin = x0 < x ? x0 : x; + xmax = x0 > x ? x0 : x; + ymin = y0 < y ? y0 : y; + ymax = y0 > y ? y0 : y; + + for (double t = 0.0; t <= 1.0; t+=0.01) + { + double tmpx = cubicBase(t, x0, x1, x2, x); + xmin = tmpx < xmin ? tmpx : xmin; + xmax = tmpx > xmax ? tmpx : xmax; + double tmpy = cubicBase(t, y0, y1, y2, y); + ymin = tmpy < ymin ? tmpy : ymin; + ymax = tmpy > ymax ? tmpy : ymax; + } +} + +} // anonymous namespace + namespace libcdr { @@ -500,6 +700,131 @@ void libcdr::CDRPath::writeOut(WPXPropertyListVector &vec) const (*iter)->writeOut(vec); } +void libcdr::CDRPath::writeOut(WPXString &path, WPXString &viewBox, double &width) const +{ + WPXPropertyListVector vec; + writeOut(vec); + if(vec.count() == 0) + return; + // This must be a mistake and we do not want to crash lower + if(vec[0]["libwpg:path-action"]->getStr() == "Z") + return; + + // try to find the bounding box + bool isFirstPoint = true; + + double px = 0.0, py = 0.0, qx = 0.0, qy = 0.0; + double lastX = 0.0; + double lastY = 0.0; + + for(unsigned k = 0; k < vec.count(); ++k) + { + if (!vec[k]["svg:x"] || !vec[k]["svg:y"]) + continue; + if (isFirstPoint) + { + px = vec[k]["svg:x"]->getDouble(); + py = vec[k]["svg:y"]->getDouble(); + qx = px; + qy = py; + lastX = px; + lastY = py; + isFirstPoint = false; + } + px = (px > vec[k]["svg:x"]->getDouble()) ? vec[k]["svg:x"]->getDouble() : px; + py = (py > vec[k]["svg:y"]->getDouble()) ? vec[k]["svg:y"]->getDouble() : py; + qx = (qx < vec[k]["svg:x"]->getDouble()) ? vec[k]["svg:x"]->getDouble() : qx; + qy = (qy < vec[k]["svg:y"]->getDouble()) ? vec[k]["svg:y"]->getDouble() : qy; + + double xmin, xmax, ymin, ymax; + + if(vec[k]["libwpg:path-action"]->getStr() == "C") + { + getCubicBezierBBox(lastX, lastY, vec[k]["svg:x1"]->getDouble(), vec[k]["svg:y1"]->getDouble(), + vec[k]["svg:x2"]->getDouble(), vec[k]["svg:y2"]->getDouble(), + vec[k]["svg:x"]->getDouble(), vec[k]["svg:y"]->getDouble(), xmin, ymin, xmax, ymax); + + px = (px > xmin ? xmin : px); + py = (py > ymin ? ymin : py); + qx = (qx < xmax ? xmax : qx); + qy = (qy < ymax ? ymax : qy); + } + if(vec[k]["libwpg:path-action"]->getStr() == "Q") + { + getQuadraticBezierBBox(lastX, lastY, vec[k]["svg:x1"]->getDouble(), vec[k]["svg:y1"]->getDouble(), + vec[k]["svg:x"]->getDouble(), vec[k]["svg:y"]->getDouble(), xmin, ymin, xmax, ymax); + + px = (px > xmin ? xmin : px); + py = (py > ymin ? ymin : py); + qx = (qx < xmax ? xmax : qx); + qy = (qy < ymax ? ymax : qy); + } + if(vec[k]["libwpg:path-action"]->getStr() == "A") + { + getEllipticalArcBBox(lastX, lastY, vec[k]["svg:rx"]->getDouble(), vec[k]["svg:ry"]->getDouble(), + vec[k]["libwpg:rotate"] ? vec[k]["libwpg:rotate"]->getDouble() : 0.0, + vec[k]["libwpg:large-arc"] ? vec[k]["libwpg:large-arc"]->getInt() : 1, + vec[k]["libwpg:sweep"] ? vec[k]["libwpg:sweep"]->getInt() : 1, + vec[k]["svg:x"]->getDouble(), vec[k]["svg:y"]->getDouble(), xmin, ymin, xmax, ymax); + + px = (px > xmin ? xmin : px); + py = (py > ymin ? ymin : py); + qx = (qx < xmax ? xmax : qx); + qy = (qy < ymax ? ymax : qy); + } + lastX = vec[k]["svg:x"]->getDouble(); + lastY = vec[k]["svg:y"]->getDouble(); + } + + + width = qx - px; + viewBox.sprintf("%i %i %i %i", 0, 0, (unsigned)(2540*(qx - px)), (unsigned)(2540*(qy - py))); + + for(unsigned i = 0; i < vec.count(); ++i) + { + WPXString sElement; + if (vec[i]["libwpg:path-action"]->getStr() == "M") + { + // 2540 is 2.54*1000, 2.54 in = 1 inch + sElement.sprintf("M%i %i", (unsigned)((vec[i]["svg:x"]->getDouble()-px)*2540), + (unsigned)((vec[i]["svg:y"]->getDouble()-py)*2540)); + path.append(sElement); + } + else if (vec[i]["libwpg:path-action"]->getStr() == "L") + { + sElement.sprintf("L%i %i", (unsigned)((vec[i]["svg:x"]->getDouble()-px)*2540), + (unsigned)((vec[i]["svg:y"]->getDouble()-py)*2540)); + path.append(sElement); + } + else if (vec[i]["libwpg:path-action"]->getStr() == "C") + { + sElement.sprintf("C%i %i %i %i %i %i", (unsigned)((vec[i]["svg:x1"]->getDouble()-px)*2540), + (unsigned)((vec[i]["svg:y1"]->getDouble()-py)*2540), (unsigned)((vec[i]["svg:x2"]->getDouble()-px)*2540), + (unsigned)((vec[i]["svg:y2"]->getDouble()-py)*2540), (unsigned)((vec[i]["svg:x"]->getDouble()-px)*2540), + (unsigned)((vec[i]["svg:y"]->getDouble()-py)*2540)); + path.append(sElement); + } + else if (vec[i]["libwpg:path-action"]->getStr() == "Q") + { + sElement.sprintf("Q%i %i %i %i", (unsigned)((vec[i]["svg:x1"]->getDouble()-px)*2540), + (unsigned)((vec[i]["svg:y1"]->getDouble()-py)*2540), (unsigned)((vec[i]["svg:x"]->getDouble()-px)*2540), + (unsigned)((vec[i]["svg:y"]->getDouble()-py)*2540)); + path.append(sElement); + } + else if (vec[i]["libwpg:path-action"]->getStr() == "A") + { + sElement.sprintf("A%i %i %i %i %i %i %i", (unsigned)((vec[i]["svg:rx"]->getDouble())*2540), + (unsigned)((vec[i]["svg:ry"]->getDouble())*2540), (vec[i]["libwpg:rotate"] ? vec[i]["libwpg:rotate"]->getInt() : 0), + (vec[i]["libwpg:large-arc"] ? vec[i]["libwpg:large-arc"]->getInt() : 1), + (vec[i]["libwpg:sweep"] ? vec[i]["libwpg:sweep"]->getInt() : 1), + (unsigned)((vec[i]["svg:x"]->getDouble()-px)*2540), (unsigned)((vec[i]["svg:y"]->getDouble()-py)*2540)); + path.append(sElement); + } + else if (vec[i]["libwpg:path-action"]->getStr() == "Z") + path.append(" Z"); + } +} + void libcdr::CDRPath::transform(const CDRTransforms &trafos) { for (std::vector<CDRPathElement *>::iterator iter = m_elements.begin(); iter != m_elements.end(); ++iter) diff --git a/src/lib/CDRPath.h b/src/lib/CDRPath.h index ccff6d2..5faa4a5 100644 --- a/src/lib/CDRPath.h +++ b/src/lib/CDRPath.h @@ -70,6 +70,7 @@ public: void appendPath(const CDRPath &path); void writeOut(WPXPropertyListVector &vec) const; + void writeOut(WPXString &path, WPXString &viewBox, double &width) const; void transform(const CDRTransforms &trafos); void transform(const CDRTransform &trafo); CDRPathElement *clone(); |