From 462958f7490c557b140dd89ab2b6d944dbf588ea Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Thu, 3 Dec 2015 21:50:44 +0100 Subject: [PATCH] [DEBUG] correct miter/bevel/rount start cyclic path and some smal bugs --- esvg/Base.cpp | 76 +++++++++++++++++++++++-------------- esvg/Renderer.cpp | 2 +- esvg/render/Path.cpp | 5 ++- esvg/render/SegmentList.cpp | 58 ++++++++++++++++++++++++---- test/testJoin.cpp | 50 ++++++++++++++++++++++++ test/testPath.cpp | 18 +++++++++ 6 files changed, 171 insertions(+), 38 deletions(-) diff --git a/esvg/Base.cpp b/esvg/Base.cpp index 55aaaa1..46266d0 100644 --- a/esvg/Base.cpp +++ b/esvg/Base.cpp @@ -239,13 +239,8 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr& _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& _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& _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& _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& _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); } } diff --git a/esvg/Renderer.cpp b/esvg/Renderer.cpp index 0177801..a68af4d 100644 --- a/esvg/Renderer.cpp +++ b/esvg/Renderer.cpp @@ -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); diff --git a/esvg/render/Path.cpp b/esvg/render/Path.cpp index b1c19b6..03a2589 100644 --- a/esvg/render/Path.cpp +++ b/esvg/render/Path.cpp @@ -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(); diff --git a/esvg/render/SegmentList.cpp b/esvg/render/SegmentList.cpp index 0349524..2f18511 100644 --- a/esvg/render/SegmentList.cpp +++ b/esvg/render/SegmentList.cpp @@ -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"); } diff --git a/test/testJoin.cpp b/test/testJoin.cpp index 37f1e42..47df64b 100644 --- a/test/testJoin.cpp +++ b/test/testJoin.cpp @@ -146,6 +146,31 @@ TEST(TestJoin, miterLimit4) { doc.generateAnImage(ivec2(100, 100), "TestJoin_miterLimit4.bmp", g_visualDebug); } +TEST(TestJoin, miterCornerCasePath) { + std::string data("" + "" + " " + ""); + 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("" + "" + " " + ""); + 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("" + "" + " " + ""); + 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("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::FSNodeWriteAllData("TestJoin_bevelCornerCasePath.svg", data); + doc.generateAnImage(ivec2(100, 100), "TestJoin_bevelCornerCasePath.bmp", g_visualDebug); +} diff --git a/test/testPath.cpp b/test/testPath.cpp index 5745ac7..12a7625 100644 --- a/test/testPath.cpp +++ b/test/testPath.cpp @@ -107,3 +107,21 @@ TEST(TestPath, arc) { // TODO : ... EXPECT_EQ(1, 2); } + + + + +TEST(TestPath, end_path_border_case) { + std::string data("" + "" + " \n" + ""); + 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); +} \ No newline at end of file