实现线的平头样式

dev-VirtualTexture
wuyize 2023-02-15 15:37:30 +08:00
parent 315083cd52
commit 3fe7bab969
9 changed files with 256 additions and 59 deletions

View File

@ -969,6 +969,35 @@ void drawLine(in float d, inout uint styleIndex, out vec4 elementColor, out vec2
} }
} }
bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p3, vec2 tangentBegin, vec2 tangentEndLast)
{
vec2 normal;
if (onVeryBegin)
{
if (endType == 0)
return true;
else if (endType == 1)
normal = normalize(mat2(0, 1, -1, 0) * (-tangentBegin));
}
else
{
vec2 normalLast = normalize(mat2(0, 1, -1, 0) * tangentEndLast);
vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBegin));
normal = normalLast + normalNow;
}
return angleLargeThanPi(normal, localUV - p3);
}
bool shouldFillEndCap(vec2 localUV, int endType, vec2 p0, vec2 tangentEnd)
{
vec2 normal;
if (endType == 0)
return true;
else if (endType == 1)
normal = normalize(mat2(0, 1, -1, 0) * tangentEnd);
return angleLargeThanPi(localUV - p0, normal);
}
void main() void main()
{ {
uvec2 pixelLocation = gl_GlobalInvocationID.xy; uvec2 pixelLocation = gl_GlobalInvocationID.xy;
@ -1025,16 +1054,27 @@ void main()
vec4 styleHead = unpackUnorm4x8(floatBitsToUint(style[styleIndex + 1])); vec4 styleHead = unpackUnorm4x8(floatBitsToUint(style[styleIndex + 1]));
float lineType = floor(styleHead.b * 10); float lineType = floor(styleHead.b * 10);
// float lineType = 2; // float lineType = 2;
int endType = int(round(styleHead.b * 100)) % 10;
//endType = 1;
int debugBegin = 0; int debugBegin = 0;
bool onVeryBegin = false;
vec2 tangentEndLast;
for (uint pathIndex = 0; pathIndex < pathSize; pathIndex++) for (uint pathIndex = 0; pathIndex < pathSize; pathIndex++)
// for (uint pathIndex = 0; pathIndex < 4; pathIndex++)
{ {
vec2 pTemp = path[pathIndex]; vec2 pTemp = path[pathIndex];
if (isinf(pTemp.x)) if (isinf(pTemp.x))
{ {
// TODO: ¼ì²âÊÇ·ñ·â±Õ²¢´¦Àí // TODO: ¼ì²âÊÇ·ñ·â±Õ²¢´¦Àí
if (hitElement && distance(localUV, p3Last) <= strokeWidth)
{
hitElement = shouldFillEndCap(localUV, endType, p3Last, tangentEndLast);
}
pBegin = path[++pathIndex]; pBegin = path[++pathIndex];
p3Last = pBegin; p3Last = pBegin;
p2Last = pBegin;
onVeryBegin = true;
continue; continue;
} }
mat4x2 p = mat4x2(p3Last, pTemp, path[++pathIndex], path[++pathIndex]); mat4x2 p = mat4x2(p3Last, pTemp, path[++pathIndex], path[++pathIndex]);
@ -1043,22 +1083,24 @@ void main()
if (d <= strokeWidth) if (d <= strokeWidth)
{ {
bool onBegin = distance(localUV, p[0]) <= strokeWidth && p3Last == p[0]; bool onBegin = distance(localUV, p[0]) <= strokeWidth && p3Last == p[0];
bool fill = true; vec2 tangentBegin;
if (onBegin) vec2 tangentEnd;
{ if (p[0] != p[1])
vec2 normalLast = normalize(mat2(0, 1, -1, 0) * (p3Last - p2Last)); tangentBegin = normalize(p[0] - p[1]);
vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (p[1] - p[0])); else
vec2 normal = normalLast + normalNow; tangentBegin = normalize(p[0] - p[2]);
fill = angleLargeThanPi(normal, localUV - p[0]); if (p[3] != p[2])
} tangentEnd = normalize(p[3] - p[2]);
if (onBegin ? fill : d < minDistance) else
tangentEnd = normalize(p[3] - p[1]);
if (onBegin ? shouldFillBeginCap(localUV, onVeryBegin, endType, p[0], tangentBegin, p3Last - p2Last)
: d < minDistance)
{ {
minDistance = min(minDistance, d); minDistance = min(minDistance, d);
bool reverse = p[3].y - p[0].y < 0.; bool reverse = p[3].y - p[0].y < 0.;
vec2 tangentBegin = normalize(p[0] - p[1]);
vec2 tangentEnd = normalize(p[3] - p[2]);
if (tangentBegin.y == 0.) if (tangentBegin.y == 0.)
tangentBegin.y = reverse ? eps : -eps; tangentBegin.y = reverse ? eps : -eps;
if (tangentEnd.y == 0.) if (tangentEnd.y == 0.)
@ -1066,7 +1108,8 @@ void main()
int intTest = cubic_bezier_int_test2(localUV, p[0], p[1], p[2], p[3], reverse) + int intTest = cubic_bezier_int_test2(localUV, p[0], p[1], p[2], p[3], reverse) +
ray_int_test(localUV, p[0], tangentBegin, reverse) + ray_int_test(localUV, p[0], tangentBegin, reverse) +
ray_int_test(localUV, p[3], tangentEnd, reverse); ray_int_test(localUV, p[3], tangentEnd, reverse);
if (lineType == 2 || intTest % 2 == int(lineType))
if (lineType == 2 || (intTest % 2 == int(lineType)))
{ {
hitElement = true; hitElement = true;
// elementColor = vec4(1, 1, 0, 1); // elementColor = vec4(1, 1, 0, 1);
@ -1076,9 +1119,15 @@ void main()
else if (p3Last == p[0]) else if (p3Last == p[0])
hitElement = false; hitElement = false;
} }
tangentEndLast = tangentEnd;
} }
p3Last = p[3]; p3Last = p[3];
p2Last = p[2]; p2Last = p[2];
onVeryBegin = false;
}
if (hitElement && distance(localUV, p3Last) <= strokeWidth)
{
hitElement = shouldFillEndCap(localUV, endType, p3Last, tangentEndLast);
} }
} }
if (hitElement) if (hitElement)

View File

@ -48,16 +48,16 @@ QPainterPath PainterPathUtil::monotonization(QPainterPath& painterPath) {
vector<vector<Point> > lines = transformToLines(painterPath); vector<vector<Point> > lines = transformToLines(painterPath);
vector<shared_ptr<Line>> linePtrVector; vector<shared_ptr<Line>> linePtrVector;
Renderer::LineTree::monotonization(lines, linePtrVector); Renderer::LineTree::monotonization(lines, linePtrVector);
for (auto linePtr : linePtrVector) { Point lastPoint(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity());
for (auto& linePtr : linePtrVector) {
vector<Point> line = linePtr->toPointVector(); vector<Point> line = linePtr->toPointVector();
if (line.size() == 2) { if (line[0] != lastPoint)
resPath.moveTo(line[0]); resPath.moveTo(line[0]);
if (line.size() == 2)
resPath.lineTo(line[1]); resPath.lineTo(line[1]);
} else if (line.size() == 4)
if (line.size() == 4) {
resPath.moveTo(line[0]);
resPath.cubicTo(line[1], line[2], line[3]); resPath.cubicTo(line[1], line[2], line[3]);
} lastPoint = line.back();
} }
return resPath; return resPath;
} }

View File

@ -3,12 +3,10 @@
#include <QPainterPath> #include <QPainterPath>
#include "../Renderer/Painting/Line.h" #include "../Renderer/Painting/Line.h"
using Renderer::Point;
class PainterPathUtil class PainterPathUtil
{ {
public: public:
static std::vector<std::vector<Point> > transformToLines(QPainterPath& painterPath); static std::vector<std::vector<Renderer::Point> > transformToLines(QPainterPath& painterPath);
static QPainterPath monotonization(QPainterPath& painterPath); static QPainterPath monotonization(QPainterPath& painterPath);
}; };

View File

@ -38,7 +38,7 @@ namespace Renderer
}; };
enum class StrokeType { kBothSides = 2, kLeftSide = 1, kRightSide = 0 }; enum class StrokeType { kBothSides = 2, kLeftSide = 1, kRightSide = 0 };
enum class StrokeEndType { kRound = 0 }; enum class StrokeEndType { kRound = 0, kFlat = 1 };
class MaterialStyleStroke : public MaterialStyle class MaterialStyleStroke : public MaterialStyle
{ {

View File

@ -37,11 +37,15 @@ std::vector<glm::vec2> generatePathBuffer(const QPainterPath& path)
switch (element.type) switch (element.type)
{ {
case QPainterPath::MoveToElement: case QPainterPath::MoveToElement:
qDebug() << "MoveToElement";
qDebug() << element;
pathBuffer.push_back(glm::vec2(std::numeric_limits<float>::infinity())); pathBuffer.push_back(glm::vec2(std::numeric_limits<float>::infinity()));
pathBuffer.push_back(glm::vec2(element.x, element.y)); pathBuffer.push_back(glm::vec2(element.x, element.y));
break; break;
case QPainterPath::LineToElement: case QPainterPath::LineToElement:
{ {
qDebug() << "LineToElement";
qDebug() << element;
glm::vec2 end = glm::vec2(element.x, element.y); glm::vec2 end = glm::vec2(element.x, element.y);
glm::vec2 mid = (pathBuffer.back() + end) / 2.f; glm::vec2 mid = (pathBuffer.back() + end) / 2.f;
pathBuffer.push_back(mid); pathBuffer.push_back(mid);
@ -50,8 +54,24 @@ std::vector<glm::vec2> generatePathBuffer(const QPainterPath& path)
break; break;
} }
case QPainterPath::CurveToElement: case QPainterPath::CurveToElement:
pathBuffer.push_back(glm::vec2(element.x, element.y)); {
qDebug() << "CurveToElement";
qDebug() << element;
glm::vec2 p1 = glm::vec2(element.x, element.y);
element = path.elementAt(++i);
qDebug() << element;
glm::vec2 p2 = glm::vec2(element.x, element.y);
element = path.elementAt(++i);
qDebug() << element;
glm::vec2 p3 = glm::vec2(element.x, element.y);
if (p3 != pathBuffer.back())
{
pathBuffer.push_back(p1);
pathBuffer.push_back(p2);
pathBuffer.push_back(p3);
}
break; break;
}
case QPainterPath::CurveToDataElement: case QPainterPath::CurveToDataElement:
pathBuffer.push_back(glm::vec2(element.x, element.y)); pathBuffer.push_back(glm::vec2(element.x, element.y));
break; break;

View File

@ -23,5 +23,17 @@
已实现由图元数据建立完整彩绘编码 已实现由图元数据建立完整彩绘编码
已实现面的纯色样式,线的纯色、径向渐变/分层、单侧等样式 #### 已实现的图元样式MaterialStyle
##### 面MaterialStyleFill
纯色
##### 线MaterialStyleStroke
双侧/左侧/右侧
圆头/平头
纯色/渐变/分层

View File

@ -9,7 +9,116 @@ using namespace Renderer;
namespace UnitTest namespace UnitTest
{ {
TEST_CLASS(ElementRendererTest) TEST_CLASS(ElementRendererStokeTypeTest)
{
public:
TEST_METHOD(TestBothSidesRound)
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
char* argv[] = { (char*)"" };
int argc = 1;
QApplication a(argc, argv);
class StyleStrokeRadialGradient : public Renderer::ElementStyle
{
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.20, Material{QColor(255,255,255)}},
{0.60, Material{QColor(165,176,207)}},
{1.00, Material{QColor(58,64,151)}}
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(60, StrokeType::kBothSides, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
} style;
TestGLWidget w(style);
w.show();
a.exec();
}
TEST_METHOD(TestBothSidesFlat)
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
char* argv[] = { (char*)"" };
int argc = 1;
QApplication a(argc, argv);
class StyleStrokeRadialGradient : public Renderer::ElementStyle
{
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.20, Material{QColor(255,255,255)}},
{0.60, Material{QColor(165,176,207)}},
{1.00, Material{QColor(58,64,151)}}
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(60, StrokeType::kBothSides, StrokeEndType::kFlat,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
} style;
TestGLWidget w(style);
w.show();
a.exec();
}
TEST_METHOD(TestLeftSideRound)
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
char* argv[] = { (char*)"" };
int argc = 1;
QApplication a(argc, argv);
class StyleStrokeRadialGradient : public Renderer::ElementStyle
{
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.20, Material{QColor(255,255,255)}},
{0.60, Material{QColor(165,176,207)}},
{1.00, Material{QColor(58,64,151)}}
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(60, StrokeType::kLeftSide, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
} style;
TestGLWidget w(style);
w.show();
a.exec();
}
TEST_METHOD(TestLeftSideFlat)
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
char* argv[] = { (char*)"" };
int argc = 1;
QApplication a(argc, argv);
class StyleStrokeRadialGradient : public Renderer::ElementStyle
{
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.20, Material{QColor(255,255,255)}},
{0.60, Material{QColor(165,176,207)}},
{1.00, Material{QColor(58,64,151)}}
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(60, StrokeType::kLeftSide, StrokeEndType::kFlat,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
} style;
TestGLWidget w(style);
w.show();
a.exec();
}
};
TEST_CLASS(ElementRendererStokeMaterialTest)
{ {
public: public:
TEST_METHOD(TestStrokePlain) TEST_METHOD(TestStrokePlain)
@ -20,7 +129,15 @@ namespace UnitTest
char* argv[] = { (char*)"" }; char* argv[] = { (char*)"" };
int argc = 1; int argc = 1;
QApplication a(argc, argv); QApplication a(argc, argv);
Renderer::ElementStyleStrokeDemo style(2); class StyleStrokePlain : public Renderer::ElementStyle
{
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(60, StrokeType::kBothSides, StrokeEndType::kRound,
std::make_shared<StrokePlain>(QColor(255,255,255),1,1))) };
}
} style;
TestGLWidget w(style); TestGLWidget w(style);
w.show(); w.show();
a.exec(); a.exec();
@ -38,13 +155,12 @@ namespace UnitTest
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{ {
std::map<float, Material> materialMap = { std::map<float, Material> materialMap = {
{0.25, Material{QColor(255,0,0)}}, {0.20, Material{QColor(255,255,255)}},
{0.50, Material{QColor(0,255,0)}}, {0.60, Material{QColor(165,176,207)}},
{0.75, Material{QColor(0,0,255)}}, {1.00, Material{QColor(58,64,151)}}
{1.00, Material{QColor(255,255,0)}},
}; };
return { BaseStyle(std::make_shared<TransformStyle>(), return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(4, StrokeType::kLeftSide, StrokeEndType::kRound, std::make_shared<MaterialStyleStroke>(60, StrokeType::kBothSides, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) }; std::make_shared<StrokeRadialGradient>(materialMap, false))) };
} }
} style; } style;
@ -65,12 +181,12 @@ namespace UnitTest
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{ {
std::map<float, Material> materialMap = { std::map<float, Material> materialMap = {
{0.50, Material{QColor(0,255,0)}}, {0.00, Material{QColor(255,255,255)}},
{0.75, Material{QColor(0,0,255)}}, {0.50, Material{QColor(165,176,207)}},
{1.00, Material{QColor(255,255,0)}}, {1.00, Material{QColor(58,64,151)}}
}; };
return { BaseStyle(std::make_shared<TransformStyle>(), return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(30, StrokeType::kLeftSide, StrokeEndType::kRound, std::make_shared<MaterialStyleStroke>(30, StrokeType::kBothSides, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, true))) }; std::make_shared<StrokeRadialGradient>(materialMap, true))) };
} }
} style; } style;

View File

@ -4,6 +4,7 @@
#include <QPainter> #include <QPainter>
#include "Renderer/Preview/ElementRenderer.h" #include "Renderer/Preview/ElementRenderer.h"
#include "Editor/ThirdPartyLib/qquick/qquicksvgparser_p.h" #include "Editor/ThirdPartyLib/qquick/qquicksvgparser_p.h"
#include "Editor/util/PainterPathUtil.h"
#include "Renderer/Painting/MaterialStyleStroke.h" #include "Renderer/Painting/MaterialStyleStroke.h"
#include "Renderer/Painting/ElementStyle.h" #include "Renderer/Painting/ElementStyle.h"
@ -25,12 +26,13 @@ namespace UnitTest
}; };
void paintGL() override void paintGL() override
{ {
glClearColor(1.0, 1.0, 1.0, 1.0); glClearColor(219/255., 78/255., 32/255., 1.0);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
QPainterPath path; QPainterPath path;
QQuickSvgParser::parsePathDataFast("M100,100C-.5,100,0,100.5,0,0L40,.07C40,59.5,39.5,60,100,60Z", path); QQuickSvgParser::parsePathDataFast("M292.82,107.78s0,0,0,0,0,3.59,0,7.62c0,3.85,0,5.78.06,6.43a19.94,19.94,0,0,0,2.87,7.58,15.85,15.85,0,0,0,6.61,6.23A14.75,14.75,0,0,0,310,137a11.69,11.69,0,0,0,7.59-2.92,11,11,0,0,0,3.2-6.84c.15-1.27.58-4.84-1.79-7.64a8.54,8.54,0,0,0-3.56-2.44c-1.32-.52-3.32-1.31-5.06-.33a5.41,5.41,0,0,0-2.14,3,3.48,3.48,0,0,0-.16,2.71c.78,1.86,3.36,2.14,3.47,2.15", path);
path = PainterPathUtil::monotonization(path);
QTransform transform; QTransform transform;
transform.scale(5, 5); transform.scale(10, 10);
path = transform.map(path); path = transform.map(path);
float pixelRatio = devicePixelRatioF(); float pixelRatio = devicePixelRatioF();
auto [img, pos] = renderer.drawElement(path, style, pixelRatio, false); auto [img, pos] = renderer.drawElement(path, style, pixelRatio, false);

View File

@ -69,7 +69,7 @@
<PreprocessorDefinitions>FRAMELESSHELPER_WIDGETS_STATIC;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>FRAMELESSHELPER_WIDGETS_STATIC;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile> </ClCompile>
<Link> <Link>
<AdditionalDependencies>$(SolutionDIr)ArchitectureColoredPainting\x64\Debug\*.obj;$(SolutionDIr)FramelessHelper\qmake\FramelessHelperCore\debug\FramelessHelperCore.lib;$(SolutionDIr)FramelessHelper\qmake\FramelessHelperWidgets\debug\FramelessHelperWidgets.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>$(SolutionDIr)ArchitectureColoredPainting\x64\Debug\*.obj;$(SolutionDIr)FramelessHelper\qmake\FramelessHelperCore\debug\FramelessHelperCore.lib;$(SolutionDIr)FramelessHelper\qmake\FramelessHelperWidgets\debug\FramelessHelperWidgets.lib;opengl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">