[DEBUG] correct miter/bevel/rount start cyclic path and some smal bugs

This commit is contained in:
Edouard DUPIN 2015-12-03 21:50:44 +01:00
parent bbe79f971d
commit 462958f749
6 changed files with 171 additions and 38 deletions

View File

@ -239,13 +239,8 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr<const exml::Element>& _ele
}
bool fillNone = false;
bool strokeNone = false;
std::string content = _element->getAttribute("fill");
if (content.size()!=0) {
m_paint.fill = parseColor(content);
if (m_paint.fill.a() == 0) {
fillNone = true;
}
}
std::string content;
// ---------------- stroke ----------------
content = _element->getAttribute("stroke");
if (content.size()!=0) {
m_paint.stroke = parseColor(content);
@ -257,31 +252,19 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr<const exml::Element>& _ele
if (content.size()!=0) {
m_paint.strokeWidth = parseLength(content);
}
content = _element->getAttribute("opacity");
if (content.size()!=0) {
m_paint.opacity = parseLength(content);
m_paint.opacity = std::avg(0.0f, m_paint.opacity, 1.0f);
}
content = _element->getAttribute("fill-opacity");
if (content.size()!=0) {
float opacity = parseLength(content);
opacity = std::avg(0.0f, opacity, 1.0f);
m_paint.fill.setA(opacity);
}
content = _element->getAttribute("stroke-opacity");
if (content.size()!=0) {
float opacity = parseLength(content);
opacity = std::avg(0.0f, opacity, 1.0f);
m_paint.stroke.setA(opacity);
}
content = _element->getAttribute("fill-rule");
content = _element->getAttribute("stroke-dasharray");
if (content.size()!=0) {
if (content == "nonzero") {
m_paint.flagEvenOdd = false;
} else if (content == "evenodd" ) {
m_paint.flagEvenOdd = true;
if (content == "none" ) {
// OK, Nothing to do ...
} else {
SVG_ERROR("not know fill-rule value : \"" << content << "\", not in [nonzero,evenodd]");
SVG_TODO(" 'stroke-dasharray' not implemented ...");
}
}
content = _element->getAttribute("stroke-linecap");
@ -315,6 +298,37 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr<const exml::Element>& _ele
float tmp = parseLength(content);
m_paint.miterLimit = std::max(0.0f, tmp);
}
// ---------------- FILL ----------------
content = _element->getAttribute("fill");
if (content.size()!=0) {
m_paint.fill = parseColor(content);
if (m_paint.fill.a() == 0) {
fillNone = true;
}
}
content = _element->getAttribute("fill-opacity");
if (content.size()!=0) {
float opacity = parseLength(content);
opacity = std::avg(0.0f, opacity, 1.0f);
m_paint.fill.setA(opacity);
}
content = _element->getAttribute("fill-rule");
if (content.size()!=0) {
if (content == "nonzero") {
m_paint.flagEvenOdd = false;
} else if (content == "evenodd" ) {
m_paint.flagEvenOdd = true;
} else {
SVG_ERROR("not know fill-rule value : \"" << content << "\", not in [nonzero,evenodd]");
}
}
// ---------------- opacity ----------------
content = _element->getAttribute("opacity");
if (content.size()!=0) {
m_paint.opacity = parseLength(content);
m_paint.opacity = std::avg(0.0f, m_paint.opacity, 1.0f);
}
// ---------------- STYLE ----------------
content = _element->getAttribute("style");
if (content.size()!=0) {
std::string outputType;
@ -383,6 +397,12 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr<const exml::Element>& _ele
m_paint.lineJoin = esvg::join_miter;
SVG_ERROR("not know " << outputType << " value : \"" << outputValue << "\", not in [miter,round,bevel]");
}
} else if (outputType == "stroke-dasharray") {
if (outputValue == "none") {
// OK, Nothing to do ...
} else {
SVG_TODO(" 'stroke-dasharray' not implemented ...");
}
} else if (outputType == "stroke-miterlimit") {
float tmp = parseLength(outputValue);
m_paint.miterLimit = std::max(0.0f, tmp);
@ -394,11 +414,11 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr<const exml::Element>& _ele
}
}
// check if somewere none is set to the filling:
if (true == fillNone) {
m_paint.fill.setA(0);
if (fillNone == true) {
m_paint.fill.setA(0.0f);
}
if (true == strokeNone) {
m_paint.stroke.setA(0);
if (strokeNone == true) {
m_paint.stroke.setA(0.0f);
}
}

View File

@ -26,7 +26,7 @@ esvg::Renderer::Renderer(const ivec2& _size, bool _visualDebug) :
m_nbSubScanLine(8) {
#ifdef DEBUG
if (m_visualDebug == true) {
m_factor = 10;
m_factor = 20;
}
#endif
setSize(_size);

View File

@ -157,8 +157,11 @@ esvg::render::PointList esvg::render::Path::generateListPoints(int32_t _level, i
// find the previous tart of the path ...
tmpListPoint.front().m_type = esvg::render::Point::type_join;
// Remove the last point if it is the same position...
if (tmpListPoint.front().m_pos == tmpListPoint.back().m_pos) {
vec2 delta = (tmpListPoint.front().m_pos - tmpListPoint.back().m_pos).absolute();
if ( delta.x() <= 0.00001
&& delta.y() <= 0.00001) {
tmpListPoint.pop_back();
SVG_VERBOSE(" Remove point Z property : " << tmpListPoint.back().m_pos << " with delta=" << delta);
}
out.addList(tmpListPoint);
tmpListPoint.clear();

View File

@ -189,17 +189,59 @@ void esvg::render::SegmentList::createSegmentListStroke(esvg::render::PointList&
vec2 rightPoint(0,0);
if (itListPoint.size() > 0) {
if (itListPoint.front().m_type == esvg::render::Point::type_join) {
const esvg::render::Point& it = itListPoint.back();
// Calculate the perpendiculary axis ...
leftPoint = itListPoint.back().m_pos
+ itListPoint.back().m_orthoAxePrevious*_width*0.5f;
rightPoint = itListPoint.back().m_pos
- itListPoint.back().m_orthoAxePrevious*_width*0.5f;
// cyclic path...
if ( itListPoint.back().m_type == esvg::render::Point::type_join
|| itListPoint.back().m_type == esvg::render::Point::type_interpolation) {
if (it.m_type == esvg::render::Point::type_interpolation) {
leftPoint = getIntersect(leftPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe);
rightPoint = getIntersect(rightPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe);
} else if (it.m_type == esvg::render::Point::type_join) {
// Calculate the perpendiculary axis ...
leftPoint = itListPoint.back().m_pos
+ itListPoint.back().m_orthoAxePrevious*_width*0.5f;
rightPoint = itListPoint.back().m_pos
- itListPoint.back().m_orthoAxePrevious*_width*0.5f;
leftPoint = it.m_pos
+ it.m_orthoAxePrevious*_width*0.5f;
rightPoint = it.m_pos
- it.m_orthoAxePrevious*_width*0.5f;
// project on the miter Axis ...
leftPoint = getIntersect(leftPoint, itListPoint.back().m_pos-itListPoint.back().m_posPrevious, itListPoint.back().m_pos, itListPoint.back().m_miterAxe);
rightPoint = getIntersect(rightPoint, itListPoint.back().m_pos-itListPoint.back().m_posPrevious, itListPoint.back().m_pos, itListPoint.back().m_miterAxe);
switch (_join) {
case esvg::join_miter:
{
vec2 left = getIntersect(leftPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe);
vec2 right = getIntersect(rightPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe);
// Check the miter limit:
float limitRight = (left - it.m_pos).length() / _width * 2.0f;
float limitLeft = (right - it.m_pos).length() / _width * 2.0f;
SVG_VERBOSE(" miter Limit: " << limitRight << " " << limitLeft << " <= " << _miterLimit);
if ( limitRight <= _miterLimit
&& limitLeft <= _miterLimit) {
leftPoint = left;
rightPoint = right;
break;
} else {
// BEVEL the miter point ...
}
}
case esvg::join_round:
case esvg::join_bevel:
{
vec2 axePrevious = (it.m_pos-it.m_posPrevious).safeNormalize();
vec2 axeNext = (it.m_posNext - it.m_pos).safeNormalize();
float cross = axePrevious.cross(axeNext);
if (cross > 0.0f) {
rightPoint = getIntersect(rightPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe);
leftPoint = it.m_pos
+ it.m_orthoAxeNext*_width*0.5f;
} else {
leftPoint = getIntersect(leftPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe);
rightPoint = it.m_pos
- it.m_orthoAxeNext*_width*0.5f;
}
break;
}
}
} else {
SVG_ERROR("Start list point with a join, but last lement is not a join");
}

View File

@ -146,6 +146,31 @@ TEST(TestJoin, miterLimit4) {
doc.generateAnImage(ivec2(100, 100), "TestJoin_miterLimit4.bmp", g_visualDebug);
}
TEST(TestJoin, miterCornerCasePath) {
std::string data("<?xml version='1.0' encoding='UTF-8' standalone='no'?>"
"<svg height='100' width='100'>"
" <path"
" d='m 37.984608,9.9629707 c 6.211703,0 12.423406,0 18.635109,0 0,2.5633883 0,5.1267763 0,7.6901643 -6.211703,0 -12.423406,0 -18.635109,0 0,-2.563388 0,-5.126776 0,-7.6901643 z'\n"
" stroke='green' stroke-width='5' fill='orange' stroke-linejoin='miter'/>"
"</svg>");
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestJoin_miterCornerCasePath.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestJoin_miterCornerCasePath.bmp", g_visualDebug);
}
TEST(TestJoin, miterCornerCasePathLimit) {
std::string data("<?xml version='1.0' encoding='UTF-8' standalone='no'?>"
"<svg height='100' width='100'>"
" <path"
" d='m 37.984608,9.9629707 c 6.211703,0 12.423406,0 18.635109,0 0,2.5633883 0,5.1267763 0,7.6901643 -6.211703,0 -12.423406,0 -18.635109,0 0,-2.563388 0,-5.126776 0,-7.6901643 z'\n"
" stroke='green' stroke-width='5' fill='orange' stroke-linejoin='miter' stroke-miterlimit='0.3'/>"
"</svg>");
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestJoin_miterCornerCasePathLimit.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestJoin_miterCornerCasePathLimit.bmp", g_visualDebug);
}
// ------------------------------------------------------ Round test -----------------------------------------------------
@ -237,6 +262,19 @@ TEST(TestJoin, roundLeft4) {
doc.generateAnImage(ivec2(100, 100), "TestJoin_roundLeft4.bmp", g_visualDebug);
}
TEST(TestJoin, roundCornerCasePath) {
std::string data("<?xml version='1.0' encoding='UTF-8' standalone='no'?>"
"<svg height='100' width='100'>"
" <path"
" d='m 37.984608,9.9629707 c 6.211703,0 12.423406,0 18.635109,0 0,2.5633883 0,5.1267763 0,7.6901643 -6.211703,0 -12.423406,0 -18.635109,0 0,-2.563388 0,-5.126776 0,-7.6901643 z'\n"
" stroke='green' stroke-width='5' fill='orange' stroke-linejoin='round'/>"
"</svg>");
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestJoin_roundCornerCasePath.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestJoin_roundCornerCasePath.bmp", g_visualDebug);
}
// ------------------------------------------------------ Bevel test -----------------------------------------------------
@ -328,3 +366,15 @@ TEST(TestJoin, bevelLeft4) {
doc.generateAnImage(ivec2(100, 100), "TestJoin_bevelLeft4.bmp", g_visualDebug);
}
TEST(TestJoin, bevelCornerCasePath) {
std::string data("<?xml version='1.0' encoding='UTF-8' standalone='no'?>"
"<svg height='100' width='100'>"
" <path"
" d='m 37.984608,9.9629707 c 6.211703,0 12.423406,0 18.635109,0 0,2.5633883 0,5.1267763 0,7.6901643 -6.211703,0 -12.423406,0 -18.635109,0 0,-2.563388 0,-5.126776 0,-7.6901643 z'\n"
" stroke='green' stroke-width='5' fill='orange' stroke-linejoin='bevel'/>"
"</svg>");
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestJoin_bevelCornerCasePath.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestJoin_bevelCornerCasePath.bmp", g_visualDebug);
}

View File

@ -107,3 +107,21 @@ TEST(TestPath, arc) {
// TODO : ...
EXPECT_EQ(1, 2);
}
TEST(TestPath, end_path_border_case) {
std::string data("<?xml version='1.0' encoding='UTF-8' standalone='no'?>"
"<svg height='100' width='100'>"
" <path\n"
" style='fill:#9fecff;fill-opacity:1;stroke:#1b57df;stroke-width:8.81125546;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.94901961'\n"
" d='M 83.073072,55.099829 C 83.886712,33.687876 60.475404,16.478179 40.263655,23.556532 19.565051,29.111554 9.9926694,56.534855 22.756336,73.74319 c 11.428293,18.124001 40.474216,19.151787 53.156943,1.880953 4.612608,-5.778118 7.177805,-13.13422 7.159793,-20.524314 z'\n"
" id='path3421'\n"
" inkscape:connector-curvature='0' />\n"
"</svg>");
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestPath_end_path_border_case.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestPath_end_path_border_case.bmp", g_visualDebug);
}