diff --git a/ArchitectureColoredPainting/res/Shaders/element.comp b/ArchitectureColoredPainting/res/Shaders/element.comp index f50abd8..e215250 100644 --- a/ArchitectureColoredPainting/res/Shaders/element.comp +++ b/ArchitectureColoredPainting/res/Shaders/element.comp @@ -899,6 +899,36 @@ bool angleLargeThanPi(vec2 a, vec2 b) /************************************************************************************/ +float getLocalWidth(float t, vec2 lengthRate, float originWidth, uint widthMapSize, uint widthMapIndex) +{ + if(widthMapSize == 0) + return originWidth; + float width; + vec2 lastData = unpackUnorm2x16(floatBitsToUint(style[widthMapIndex])); + float lastLevel = 0; + float lastWidth = lastData.y; + float currentLengthRate = mix(lengthRate.x, lengthRate.y, t); + bool found = false; + for (uint i = 0; i < widthMapSize; i++) + { + vec2 data = unpackUnorm2x16(floatBitsToUint(style[widthMapIndex + i])); + float level = data.x; + float currentWidth = data.y; + if (currentLengthRate <= level) + { + float a = (currentLengthRate - lastLevel) / (level - lastLevel); + width = mix(lastWidth, currentWidth, a); + found = true; + break; + } + lastWidth = currentWidth; + lastLevel = level; + } + if(!found) + width = lastWidth; + return width * originWidth; +} + void drawLine(in float d, uint styleHeadIndex, out vec4 elementColor, out vec2 metallicRoughness) { elementColor = vec4(1); @@ -1100,6 +1130,7 @@ void main() else // Stroke { uint widthMapSize = floatBitsToUint(style[styleIndex + 1]); + uint widthMapIndex = styleIndex + 2; float minDistance = 1e38; styleIndex += widthMapSize + 2; vec4 styleHead = unpackUnorm4x8(floatBitsToUint(style[styleIndex])); @@ -1177,7 +1208,7 @@ void main() float t; float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true, t); - float localWidth = strokeWidth; // * mix(lengthRate.x, lengthRate.y, t); + float localWidth = getLocalWidth(t, lengthRate, strokeWidth, widthMapSize, widthMapIndex); if (d <= localWidth) { bool hit = d < minDistance; diff --git a/ArchitectureColoredPainting/src/Renderer/Preview/ElementRenderer.cpp b/ArchitectureColoredPainting/src/Renderer/Preview/ElementRenderer.cpp index a7857e0..9c0c54f 100644 --- a/ArchitectureColoredPainting/src/Renderer/Preview/ElementRenderer.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Preview/ElementRenderer.cpp @@ -10,15 +10,55 @@ #include "../Painting/Element.h" #include "../Painting/Painting.h" #include "../Painting/MaterialStyleStroke.h" +#include "../Painting/CubicBezier.h" #include using namespace Renderer; std::vector generatePathBuffer(const QPainterPath& path) { - float lastLength = 0; glm::vec2 lastPoint; - std::vector pathBuffer; + std::vector> lengths; for (int i = 0; i < path.elementCount(); i++) + { + QPainterPath::Element element = path.elementAt(i); + switch (element.type) + { + case QPainterPath::MoveToElement: + { + lastPoint = glm::vec2(element.x, element.y); + lengths.push_back({ 0 }); + break; + } + case QPainterPath::LineToElement: + { + glm::vec2 end = glm::vec2(element.x, element.y); + float length = glm::distance(lastPoint, end); + lengths.back().push_back(lengths.back().back() + length); + break; + } + case QPainterPath::CurveToElement: + { + Point p1(element.x, element.y); + element = path.elementAt(++i); + Point p2(element.x, element.y); + element = path.elementAt(++i); + Point p3(element.x, element.y); + if (glm::vec2(p3.x, p3.y) != lastPoint) + { + float length = CubicBezier(std::vector{ Point(lastPoint.x, lastPoint.y), p1, p2, p3 }).getIntegralByT(1); + lengths.back().push_back(lengths.back().back() + length); + lastPoint = glm::vec2(p3.x, p3.y); + } + break; + } + case QPainterPath::CurveToDataElement: + qCritical() << "Read QPainterPath Error"; + break; + } + } + + std::vector pathBuffer; + for (int i = 0, j = -1, k = 0; i < path.elementCount(); i++) { QPainterPath::Element element = path.elementAt(i); switch (element.type) @@ -28,8 +68,8 @@ std::vector generatePathBuffer(const QPainterPath& path) //qDebug() << element; pathBuffer.emplace_back(std::numeric_limits::infinity()); pathBuffer.emplace_back(element.x, element.y); - lastLength = 0; lastPoint = glm::vec2(element.x, element.y); + j++; break; case QPainterPath::LineToElement: { @@ -40,9 +80,9 @@ std::vector generatePathBuffer(const QPainterPath& path) pathBuffer.push_back(mid); pathBuffer.push_back(mid); pathBuffer.push_back(end); - float length = (i + 1.) / path.elementCount(); - pathBuffer.emplace_back(lastLength, length); - lastLength = length; + pathBuffer.emplace_back(lengths[j][k] / lengths[j].back(), lengths[j][k+1] / lengths[j].back()); + k++; + qDebug() << pathBuffer.back().x << pathBuffer.back().y; lastPoint = end; break; } @@ -62,9 +102,9 @@ std::vector generatePathBuffer(const QPainterPath& path) pathBuffer.push_back(p1); pathBuffer.push_back(p2); pathBuffer.push_back(p3); - float length = (i + 1.) / path.elementCount(); - pathBuffer.emplace_back(lastLength, length); - lastLength = length; + pathBuffer.emplace_back(lengths[j][k] / lengths[j].back(), lengths[j][k+1] / lengths[j].back()); + k++; + qDebug() << pathBuffer.back().x << pathBuffer.back().y; lastPoint = p3; } break; @@ -145,7 +185,7 @@ Renderer::ElementRenderer::ElementRenderer() thread = std::jthread([&](std::stop_token stop) { std::stop_callback cb(stop, [&] { draw.notify_all(); - }); + }); QOpenGLContext context; context.create(); @@ -205,7 +245,7 @@ Renderer::ElementRenderer::ElementRenderer() draw.notify_all(); } context.doneCurrent(); - }); + }); while (!initialized) std::this_thread::yield(); diff --git a/UnitTest/ElementRendererTest.cpp b/UnitTest/ElementRendererTest.cpp index 1982b49..61a2271 100644 --- a/UnitTest/ElementRendererTest.cpp +++ b/UnitTest/ElementRendererTest.cpp @@ -255,9 +255,12 @@ namespace UnitTest } TEST_METHOD(TestBothSidesGradientWidth) { - QPainterPath testPath; + /*QPainterPath testPath; testPath.moveTo(50, 50); - testPath.cubicTo(50, 200, 200, 300, 300, 300); + testPath.cubicTo(50, 200, 200, 300, 300, 300);*/ + QPainterPath testPath; + SvgFileLoader().loadSvgFile("../../svg/3.svg", testPath); + testPath = QTransform::fromScale(10, 10).map(testPath); QApplication a(argc, argv); class StyleStrokeRadialGradient : public Renderer::ElementStyle { @@ -269,8 +272,8 @@ namespace UnitTest {1.00, Material{QColor(58,64,151)}} }; return { BaseStyle(std::make_shared(), - std::make_shared(20, StrokeType::kLeftSide, StrokeEndType::kRound, - std::make_shared(materialMap, false))) }; + std::make_shared(80, StrokeType::kRightSide, StrokeEndType::kFlatRound, + std::make_shared(materialMap, false), std::map{ {0,1},{1,0}})) }; } } style; TestGLWidget w(style, testPath);