实现线的平头样式

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

View File

@ -48,16 +48,16 @@ QPainterPath PainterPathUtil::monotonization(QPainterPath& painterPath) {
vector<vector<Point> > lines = transformToLines(painterPath);
vector<shared_ptr<Line>> 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();
if (line.size() == 2) {
if (line[0] != lastPoint)
resPath.moveTo(line[0]);
if (line.size() == 2)
resPath.lineTo(line[1]);
}
if (line.size() == 4) {
resPath.moveTo(line[0]);
else if (line.size() == 4)
resPath.cubicTo(line[1], line[2], line[3]);
}
lastPoint = line.back();
}
return resPath;
}

View File

@ -3,12 +3,10 @@
#include <QPainterPath>
#include "../Renderer/Painting/Line.h"
using Renderer::Point;
class PainterPathUtil
{
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);
};

View File

@ -38,7 +38,7 @@ namespace Renderer
};
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
{

View File

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

View File

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

View File

@ -9,7 +9,116 @@ using namespace Renderer;
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:
TEST_METHOD(TestStrokePlain)
@ -20,7 +129,15 @@ namespace UnitTest
char* argv[] = { (char*)"" };
int argc = 1;
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);
w.show();
a.exec();
@ -38,13 +155,12 @@ namespace UnitTest
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.25, Material{QColor(255,0,0)}},
{0.50, Material{QColor(0,255,0)}},
{0.75, Material{QColor(0,0,255)}},
{1.00, Material{QColor(255,255,0)}},
{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>(4, StrokeType::kLeftSide, StrokeEndType::kRound,
std::make_shared<MaterialStyleStroke>(60, StrokeType::kBothSides, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
} style;
@ -65,12 +181,12 @@ namespace UnitTest
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.50, Material{QColor(0,255,0)}},
{0.75, Material{QColor(0,0,255)}},
{1.00, Material{QColor(255,255,0)}},
{0.00, Material{QColor(255,255,255)}},
{0.50, Material{QColor(165,176,207)}},
{1.00, Material{QColor(58,64,151)}}
};
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))) };
}
} style;

View File

@ -4,6 +4,7 @@
#include <QPainter>
#include "Renderer/Preview/ElementRenderer.h"
#include "Editor/ThirdPartyLib/qquick/qquicksvgparser_p.h"
#include "Editor/util/PainterPathUtil.h"
#include "Renderer/Painting/MaterialStyleStroke.h"
#include "Renderer/Painting/ElementStyle.h"
@ -25,12 +26,13 @@ namespace UnitTest
};
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);
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;
transform.scale(5, 5);
transform.scale(10, 10);
path = transform.map(path);
float pixelRatio = devicePixelRatioF();
auto [img, pos] = renderer.drawElement(path, style, pixelRatio, false);

View File

@ -69,7 +69,7 @@
<PreprocessorDefinitions>FRAMELESSHELPER_WIDGETS_STATIC;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<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>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">