diff --git a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj index bc422d1..c403fad 100644 --- a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj +++ b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj @@ -104,6 +104,9 @@ + + + @@ -152,6 +155,8 @@ + + @@ -187,12 +192,19 @@ + + + + + + + diff --git a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters index f7e8d97..e198184 100644 --- a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters +++ b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters @@ -65,6 +65,24 @@ {7ead1a66-586a-4584-ae80-9e7a4e667364} + + {be3f4585-c8ba-410f-8619-2adcd4349f02} + + + {b9732a33-aa2e-4f8d-886f-1b1730c66519} + + + {8d846557-8fd5-47d5-8edf-eb3eb77c226b} + + + {22d7f3ef-8185-476e-8fe1-aea24c4faacc} + + + {6fc32493-d5a2-44c3-a283-d2d3181330fb} + + + {e6de889e-8313-4846-8bdf-125b766eef59} + @@ -76,6 +94,9 @@ Form Files + + Form Files + @@ -111,9 +132,6 @@ Source Files\Renderer - - Source Files - Source Files\Renderer\Painting @@ -135,18 +153,6 @@ Source Files\Renderer\Painting - - Source Files\Editor - - - Source Files\Editor - - - Source Files\Editor - - - Source Files\Editor - Source Files\Editor @@ -156,12 +162,6 @@ Source Files\Renderer\Painting - - Source Files - - - Source Files - Source Files\Renderer\Painting @@ -189,9 +189,6 @@ Source Files\Renderer\Preview - - Source Files - Source Files\Renderer @@ -204,22 +201,61 @@ Source Files\Editor - - Source Files - - - Source Files\Editor - Source Files Source Files\Editor\util - - Source Files + + Source Files\Editor\Style + Source Files\Editor\Style + + + Source Files\Editor\Style + + + Source Files\Editor + + + Source Files\Editor + + + Source Files\Editor + + + Source Files\Editor + + + Source Files\Editor + + + Source Files\Editor\Style + + + Source Files\Editor\Element + + + Source Files\Editor\Element + + + Source Files\Editor\Element + + + Source Files\Editor\Layer + + + Source Files\Editor\Layer + + + Source Files\Editor\Layer + + + Source Files\Editor\Layer + + Source Files @@ -233,34 +269,49 @@ Header Files\Renderer - - Header Files - - - Header Files - Header Files\Editor - - Header Files - - - Header Files - - - Header Files - - - Header Files\Editor - Header Files - - Header Files + + Header Files\Editor\Style + Header Files\Editor\Style + + + Header Files\Editor + + + Header Files\Editor + + + Header Files\Editor + + + Header Files\Editor + + + Header Files\Editor + + + Header Files\Editor + + + Header Files\Editor\Style + + + Header Files\Editor\Element + + + Header Files\Editor\Layer + + + Header Files\Editor\Layer + + Header Files @@ -393,15 +444,6 @@ Header Files\Renderer\Painting - - Header Files\Editor - - - Header Files\Editor - - - Header Files\Editor - Header Files\Renderer\Painting @@ -441,9 +483,6 @@ Header Files\Renderer\Preview - - Header Files - Header Files\Renderer @@ -457,8 +496,29 @@ Header Files\Editor\util + Header Files\Editor\Style + + + Header Files\Editor\util + + + Header Files\Editor\util + + + Header Files\Editor\Element + + + Header Files\Editor\Element + + + Header Files\Editor\Layer + + Header Files\Editor + + Header Files\Editor\Layer + diff --git a/ArchitectureColoredPainting/EditorSetting.ui b/ArchitectureColoredPainting/EditorSetting.ui new file mode 100644 index 0000000..3680069 --- /dev/null +++ b/ArchitectureColoredPainting/EditorSetting.ui @@ -0,0 +1,32 @@ + + + EditorSettingWidget + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + + + 设置背景颜色 + + + + + + + + + + diff --git a/ArchitectureColoredPainting/EditorSettingWidget.ui b/ArchitectureColoredPainting/EditorSettingWidget.ui new file mode 100644 index 0000000..fc73390 --- /dev/null +++ b/ArchitectureColoredPainting/EditorSettingWidget.ui @@ -0,0 +1,63 @@ + + + EditorSettingWidget + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + + + + 0 + 0 + + + + + 120 + 16777215 + + + + 设置背景颜色 + + + + + + + + 0 + 0 + + + + + 120 + 16777215 + + + + 设置项目名称 + + + + + + + + + + diff --git a/ArchitectureColoredPainting/EditorWidgetItem.ui b/ArchitectureColoredPainting/EditorWidgetItem.ui index aea28b0..e269e9d 100644 --- a/ArchitectureColoredPainting/EditorWidgetItem.ui +++ b/ArchitectureColoredPainting/EditorWidgetItem.ui @@ -37,38 +37,87 @@ - - - 0 - - - 0 - - - 0 - - - 0 - + - + + + + 0 + 0 + + - 1080 - 1080 + 0 + 0 - 1080 - 1080 + 10801080 + 10801080 + + true + + + + + 0 + 0 + 1024 + 1024 + + + + + 0 + 0 + + + + + 1024 + 1024 + + + + + 10241024 + 10241024 + + + + + + + + 0 + 0 + + + + + 1024 + 1024 + + + + + 1024 + 1024 + + + + + + @@ -98,7 +147,7 @@ - 1 + 2 @@ -110,6 +159,11 @@ 图元池 + + + 设置 + + @@ -128,7 +182,12 @@ - 图层树 + 图层名 + + + + + 关联图元 @@ -138,6 +197,19 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -167,6 +239,12 @@
ElementPoolWidget.h
1 + + EditorSettingWidget + QWidget +
EditorSettingWidget.h
+ 1 +
diff --git a/ArchitectureColoredPainting/RendererWidget.ui b/ArchitectureColoredPainting/RendererWidget.ui index e116c32..e0d5a12 100644 --- a/ArchitectureColoredPainting/RendererWidget.ui +++ b/ArchitectureColoredPainting/RendererWidget.ui @@ -84,10 +84,26 @@ - + QLayout::SetDefaultConstraint + + + + + 0 + 0 + + + + + 0 + 16777215 + + + + diff --git a/ArchitectureColoredPainting/res/Shaders/element.comp b/ArchitectureColoredPainting/res/Shaders/element.comp index 36f4220..fd09af8 100644 --- a/ArchitectureColoredPainting/res/Shaders/element.comp +++ b/ArchitectureColoredPainting/res/Shaders/element.comp @@ -893,7 +893,7 @@ bool angleLargeThanPi(vec2 a, vec2 b) /************************************************************************************/ -void drawLine(in float d, inout uint styleIndex, out vec4 elementColor, out vec2 metallicRoughness) +void drawLine(in float d, uint styleIndex, out vec4 elementColor, out vec2 metallicRoughness) { elementColor = vec4(1); metallicRoughness = vec2(0.8); @@ -969,14 +969,38 @@ 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) +void nextStyleIndex(inout uint styleIndex) +{ + uint headUint = floatBitsToUint(style[styleIndex + 1]); + vec4 head = unpackUnorm4x8(headUint); + switch (int(head.a * 100) % 10) + { + /// Plain + case 0: { + styleIndex += 3; + break; + } + /// RadialGradient + case 1: { + uint size = headUint % (1 << 15); + styleIndex += 2 + size * 2; + break; + } + case 2: { + + break; + } + } +} + +bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p0, vec2 tangentBegin, vec2 tangentEndLast) { vec2 normal; if (onVeryBegin) { - if (endType == 0) + if (endType%2 == 0) return true; - else if (endType == 1) + else if (endType%2 == 1) normal = normalize(mat2(0, 1, -1, 0) * (-tangentBegin)); } else @@ -985,17 +1009,26 @@ bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p3, ve vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBegin)); normal = normalLast + normalNow; } - return angleLargeThanPi(normal, localUV - p3); + return angleLargeThanPi(normal, localUV - p0); } -bool shouldFillEndCap(vec2 localUV, int endType, vec2 p0, vec2 tangentEnd) +bool shouldFillEndCap(vec2 localUV, bool onVeryEnd, int endType, vec2 p3, vec2 tangentEnd, vec2 tangentBeginNext) { vec2 normal; - if (endType == 0) - return true; - else if (endType == 1) - normal = normalize(mat2(0, 1, -1, 0) * tangentEnd); - return angleLargeThanPi(localUV - p0, normal); + if (onVeryEnd) + { + if ((endType/2)%2 == 0) + return true; + else if ((endType/2)%2 == 1) + normal = normalize(mat2(0, 1, -1, 0) * tangentEnd); + } + else + { + vec2 normalLast = normalize(mat2(0, 1, -1, 0) * tangentEnd); + vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBeginNext)); + normal = normalLast + normalNow; + } + return angleLargeThanPi(localUV - p3, normal); } void main() @@ -1006,9 +1039,8 @@ void main() vec4 color = vec4(0); // if(isinf(path[0].x)) imageStore(gBaseColor, ivec2(pixelLocation), // vec4(vec2(pixelLocation)/vec2(imageSize(gBaseColor)), 1,1)); return; - for (uint styleIndex = 0; styleIndex < styleSize; styleIndex++) + for (uint styleIndex = 0; styleIndex < styleSize;) { - styleIndex += 6; vec2 localUV = vec2(pixelLocation) / pixelRatio + leftTop; @@ -1039,14 +1071,15 @@ void main() if (num_its % 2 == 1) { hitElement = true; - elementColor = vec4(1, 1, 0, 0); vec4 head = unpackUnorm4x8(floatBitsToUint(style[styleIndex])); if (head.z == 0) { - elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[++styleIndex])).rgb, 1); + elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex+1])).rgb, 1); } + } + styleIndex += 2; } else // Stroke { @@ -1055,21 +1088,22 @@ void main() float lineType = floor(styleHead.b * 10); // float lineType = 2; int endType = int(round(styleHead.b * 100)) % 10; - //endType = 1; + // endType = 1; int debugBegin = 0; bool onVeryBegin = false; + bool onVeryEnd = false; vec2 tangentEndLast; + uint lastHitIndex = 0; + bool lastHitElement = false; + hitElement = false; for (uint pathIndex = 0; pathIndex < pathSize; pathIndex++) - // for (uint pathIndex = 0; pathIndex < 4; pathIndex++) + //for (uint pathIndex = 0; pathIndex < 46; 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; @@ -1079,10 +1113,32 @@ void main() } mat4x2 p = mat4x2(p3Last, pTemp, path[++pathIndex], path[++pathIndex]); + vec2 tangentBeginNext; + if (pathIndex + 1 < pathSize) + { + vec2 pTemp = path[pathIndex + 1]; + if (isinf(pTemp.x)) + { + onVeryEnd = true; + } + else + { + onVeryEnd = false; + vec2 pNext[3] = {p[3], pTemp, path[pathIndex + 2]}; + if (pNext[0] != pNext[1]) + tangentBeginNext = normalize(pNext[0] - pNext[1]); + else + tangentBeginNext = normalize(pNext[0] - pNext[2]); + } + } + else + onVeryEnd = true; + float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true); if (d <= strokeWidth) { - bool onBegin = distance(localUV, p[0]) <= strokeWidth && p3Last == p[0]; + bool onBegin = distance(localUV, p[0]) <= strokeWidth; + bool onEnd = distance(localUV, p[3]) <= strokeWidth; vec2 tangentBegin; vec2 tangentEnd; if (p[0] != p[1]) @@ -1094,11 +1150,14 @@ void main() else tangentEnd = normalize(p[3] - p[1]); - if (onBegin ? shouldFillBeginCap(localUV, onVeryBegin, endType, p[0], tangentBegin, p3Last - p2Last) - : d < minDistance) + bool hit = d < minDistance; + if (onBegin) + hit = hit && + shouldFillBeginCap(localUV, onVeryBegin, endType, p[0], tangentBegin, tangentEndLast); + if (onEnd) + hit = hit && shouldFillEndCap(localUV, onVeryEnd, endType, p[3], tangentEnd, tangentBeginNext); + if (hit) { - minDistance = min(minDistance, d); - bool reverse = p[3].y - p[0].y < 0.; if (tangentBegin.y == 0.) @@ -1111,13 +1170,14 @@ void main() if (lineType == 2 || (intTest % 2 == int(lineType))) { + minDistance = min(minDistance, d); + lastHitElement = hitElement; + lastHitIndex = pathIndex; hitElement = true; // elementColor = vec4(1, 1, 0, 1); vec2 metallicRoughness; drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness); } - else if (p3Last == p[0]) - hitElement = false; } tangentEndLast = tangentEnd; } @@ -1125,14 +1185,11 @@ void main() p2Last = p[2]; onVeryBegin = false; } - if (hitElement && distance(localUV, p3Last) <= strokeWidth) - { - hitElement = shouldFillEndCap(localUV, endType, p3Last, tangentEndLast); - } + nextStyleIndex(styleIndex); + } if (hitElement) color = elementColor; - styleIndex += 100; } if (color.a != 0) imageStore(gBaseColor, ivec2(pixelLocation), color); diff --git a/ArchitectureColoredPainting/res/Shaders/painting.comp b/ArchitectureColoredPainting/res/Shaders/painting.comp index be24946..6e166cf 100644 --- a/ArchitectureColoredPainting/res/Shaders/painting.comp +++ b/ArchitectureColoredPainting/res/Shaders/painting.comp @@ -1,105 +1,111 @@ #version 450 core -layout (local_size_x = 8, local_size_y = 8) in; +layout(local_size_x = 8, local_size_y = 8) in; layout(location = 0) uniform ivec2 pixelOffset; +layout(std140, binding = 1) uniform ubo +{ + vec3 backgroundColor; +}; + layout(rgba8, binding = 0) uniform image2D gBaseColor; layout(rg8, binding = 1) uniform image2D gMetallicRoughness; layout(std430, binding = 0) buffer bvhBuffer { - uvec2 bvhChildren[]; + uvec2 bvhChildren[]; }; layout(std430, binding = 1) buffer bvhBoundBuffer { - vec4 bvhBound[]; + vec4 bvhBound[]; }; -layout(std430, binding = 2) buffer elementOffsetBuffer +layout(std430, binding = 2) buffer elementTranformBuffer { - /** - * @[0] elementBvhRoot - * @[1] styleOffset - * @[2] pointsOffset - * @[3] linesOffset - - */ - uint elementOffset[][5]; + mat3x2 elementTranform[]; }; -layout(std430, binding = 3) buffer elementIndexBuffer +layout(std430, binding = 3) buffer elementOffsetBuffer { - uint elementIndexs[]; //ߺ + /** + * @[0] elementBvhRoot + * @[1] styleOffset + * @[2] pointsOffset + * @[3] linesOffset + */ + uint elementOffset[][4]; }; -layout(std430, binding = 4) buffer elementDataBuffer +layout(std430, binding = 4) buffer elementIndexBuffer { - float elementData[]; //Style + uint elementIndexs[]; // ߺ +}; +layout(std430, binding = 5) buffer elementDataBuffer +{ + float elementData[]; // Style }; const float PI = 3.14159265358979; - const uint STACK_SIZE = 10; struct Stack { - uint top; - uint data[STACK_SIZE]; + uint top; + uint data[STACK_SIZE]; - bool empty() - { - return top == 0; - } - bool full() - { - return top == STACK_SIZE; - } - bool getTop(out uint x) - { - if (empty()) - return false; - x = data[top - 1]; - return true; - } - bool pop() - { - if (empty()) - return false; - top--; - return true; - } - bool push(in uint x) - { - if (full()) - return false; - data[top] = x; - top++; - return true; - } + bool empty() + { + return top == 0; + } + bool full() + { + return top == STACK_SIZE; + } + bool getTop(out uint x) + { + if (empty()) + return false; + x = data[top - 1]; + return true; + } + bool pop() + { + if (empty()) + return false; + top--; + return true; + } + bool push(in uint x) + { + if (full()) + return false; + data[top] = x; + top++; + return true; + } } stack, elementStack; - // Modified from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c // Credits to Doublefresh for hinting there int solve_quadric(vec2 coeffs, inout vec2 roots) { - // normal form: x^2 + px + q = 0 - float p = coeffs[1] / 2.; - float q = coeffs[0]; + // normal form: x^2 + px + q = 0 + float p = coeffs[1] / 2.; + float q = coeffs[0]; - float D = p * p - q; + float D = p * p - q; - if (D < 0.) - { - return 0; - } - else - { - roots[0] = -sqrt(D) - p; - roots[1] = sqrt(D) - p; + if (D < 0.) + { + return 0; + } + else + { + roots[0] = -sqrt(D) - p; + roots[1] = sqrt(D) - p; - return 2; - } + return 2; + } } // From Trisomie21 @@ -107,490 +113,520 @@ int solve_quadric(vec2 coeffs, inout vec2 roots) int solve_cubic(vec3 coeffs, inout vec3 r) { - float a = coeffs[2]; - float b = coeffs[1]; - float c = coeffs[0]; + float a = coeffs[2]; + float b = coeffs[1]; + float c = coeffs[0]; - float p = b - a * a / 3.0; - float q = a * (2.0 * a * a - 9.0 * b) / 27.0 + c; - float p3 = p * p * p; - float d = q * q + 4.0 * p3 / 27.0; - float offset = -a / 3.0; - if (d >= 0.0) - { // Single solution - float z = sqrt(d); - float u = (-q + z) / 2.0; - float v = (-q - z) / 2.0; - u = sign(u) * pow(abs(u), 1.0 / 3.0); - v = sign(v) * pow(abs(v), 1.0 / 3.0); - r[0] = offset + u + v; + float p = b - a * a / 3.0; + float q = a * (2.0 * a * a - 9.0 * b) / 27.0 + c; + float p3 = p * p * p; + float d = q * q + 4.0 * p3 / 27.0; + float offset = -a / 3.0; + if (d >= 0.0) + { // Single solution + float z = sqrt(d); + float u = (-q + z) / 2.0; + float v = (-q - z) / 2.0; + u = sign(u) * pow(abs(u), 1.0 / 3.0); + v = sign(v) * pow(abs(v), 1.0 / 3.0); + r[0] = offset + u + v; - // Single newton iteration to account for cancellation - float f = ((r[0] + a) * r[0] + b) * r[0] + c; - float f1 = (3. * r[0] + 2. * a) * r[0] + b; + // Single newton iteration to account for cancellation + float f = ((r[0] + a) * r[0] + b) * r[0] + c; + float f1 = (3. * r[0] + 2. * a) * r[0] + b; - r[0] -= f / f1; + r[0] -= f / f1; - return 1; - } - float u = sqrt(-p / 3.0); - float v = acos(-sqrt(-27.0 / p3) * q / 2.0) / 3.0; - float m = cos(v), n = sin(v) * 1.732050808; + return 1; + } + float u = sqrt(-p / 3.0); + float v = acos(-sqrt(-27.0 / p3) * q / 2.0) / 3.0; + float m = cos(v), n = sin(v) * 1.732050808; - // Single newton iteration to account for cancellation - //(once for every root) - r[0] = offset + u * (m + m); - r[1] = offset - u * (n + m); - r[2] = offset + u * (n - m); + // Single newton iteration to account for cancellation + //(once for every root) + r[0] = offset + u * (m + m); + r[1] = offset - u * (n + m); + r[2] = offset + u * (n - m); - vec3 f = ((r + a) * r + b) * r + c; - vec3 f1 = (3. * r + 2. * a) * r + b; + vec3 f = ((r + a) * r + b) * r + c; + vec3 f1 = (3. * r + 2. * a) * r + b; - r -= f / f1; + r -= f / f1; - return 3; + return 3; } - int segment_int_test(vec2 uv, vec2 p0, vec2 p1) { - p0 -= uv; - p1 -= uv; + p0 -= uv; + p1 -= uv; - int ret; + int ret; - if (p0.y * p1.y < 0.) - { - vec2 nor = p0 - p1; - nor = vec2(nor.y, -nor.x); + if (p0.y * p1.y < 0.) + { + vec2 nor = p0 - p1; + nor = vec2(nor.y, -nor.x); - float sgn; + float sgn; - if (p0.y > p1.y) - { - sgn = 1.; - } - else - { - sgn = -1.; - } + if (p0.y > p1.y) + { + sgn = 1.; + } + else + { + sgn = -1.; + } - if (dot(nor, p0) * sgn < 0.) - { - ret = 0; - } - else - { - ret = 1; - } - } - else - { - ret = 0; - } + if (dot(nor, p0) * sgn < 0.) + { + ret = 0; + } + else + { + ret = 1; + } + } + else + { + ret = 0; + } - return ret; + return ret; } int cubic_bezier_int_test(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3) { - float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y); - float qu = (3. * p0.y - 6. * p1.y + 3. * p2.y); - float li = (-3. * p0.y + 3. * p1.y); - float co = p0.y - uv.y; + float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y); + float qu = (3. * p0.y - 6. * p1.y + 3. * p2.y); + float li = (-3. * p0.y + 3. * p1.y); + float co = p0.y - uv.y; - vec3 roots = vec3(1e38); - int n_roots; + vec3 roots = vec3(1e38); + int n_roots; - int n_ints = 0; + int n_ints = 0; - if (uv.x < min(min(p0.x, p1.x), min(p2.x, p3.x))) - { - if (uv.y >= min(p0.y, p3.y) && uv.y <= max(p0.y, p3.y)) - { - n_ints = 1; - } - } - else - { + if (uv.x < min(min(p0.x, p1.x), min(p2.x, p3.x))) + { + if (uv.y >= min(p0.y, p3.y) && uv.y <= max(p0.y, p3.y)) + { + n_ints = 1; + } + } + else + { - if (abs(cu) < .0001) - { - n_roots = solve_quadric(vec2(co / qu, li / qu), roots.xy); - } - else - { - n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots); - } + if (abs(cu) < .0001) + { + n_roots = solve_quadric(vec2(co / qu, li / qu), roots.xy); + } + else + { + n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots); + } - for (int i = 0; i < n_roots; i++) - { - if (roots[i] >= 0. && roots[i] <= 1.) - { - float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x; - x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x; - x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; - x_pos = x_pos * roots[i] + p0.x; + for (int i = 0; i < n_roots; i++) + { + if (roots[i] >= 0. && roots[i] <= 1.) + { + float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x; + x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x; + x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; + x_pos = x_pos * roots[i] + p0.x; - if (x_pos > uv.x) - { - n_ints++; - } - } - } - } + if (x_pos > uv.x) + { + n_ints++; + } + } + } + } - return n_ints; + return n_ints; } bvec3 segment_sign_test(vec2 uv, vec2 p0, vec2 p1) { - p0 -= uv; - p1 -= uv; - bvec3 ret; - vec2 nor = p0 - p1; - nor = vec2(nor.y, -nor.x); + p0 -= uv; + p1 -= uv; + bvec3 ret; + vec2 nor = p0 - p1; + nor = vec2(nor.y, -nor.x); - float sgn; + float sgn; - if (p0.y > p1.y) - { - sgn = 1.; - } - else - { - sgn = -1.; - } + if (p0.y > p1.y) + { + sgn = 1.; + } + else + { + sgn = -1.; + } - if (dot(nor, p0) * sgn < 0.) - { - if (p0.y * p1.y < 0.) - ret.y = false; - else - ret.y = false; - ret.xz = bvec2(false); - } - else - { - if (p0.y * p1.y < 0.) - ret.y = true; - else - ret.y = false; - ret.xz = bvec2(true); - } - return ret; + if (dot(nor, p0) * sgn < 0.) + { + if (p0.y * p1.y < 0.) + ret.y = false; + else + ret.y = false; + ret.xz = bvec2(false); + } + else + { + if (p0.y * p1.y < 0.) + ret.y = true; + else + ret.y = false; + ret.xz = bvec2(true); + } + return ret; } bvec3 cubic_bezier_sign_test(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3) { -// if(abs(p3.y-p0.y)< 1e-4) -// { -// return segment_sign_test(uv, p0,p3); -// } + // if(abs(p3.y-p0.y)< 1e-4) + // { + // return segment_sign_test(uv, p0,p3); + // } - float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y); - float qu = (3. * p0.y - 6. * p1.y + 3. * p2.y); - float li = (-3. * p0.y + 3. * p1.y); - float co = p0.y - uv.y; + float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y); + float qu = (3. * p0.y - 6. * p1.y + 3. * p2.y); + float li = (-3. * p0.y + 3. * p1.y); + float co = p0.y - uv.y; - vec3 roots = vec3(1e38); - int n_roots = solve_cubic(vec3(co/cu,li/cu,qu/cu),roots); + vec3 roots = vec3(1e38); + int n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots); - //int n_ints = 0; - bvec3 result = bvec3(false); - for(int i=0;i<3;i++){ - if(i < n_roots){ - if(roots[i] >= 0. && roots[i] <= 1.){ - float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x; - x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x; - x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; - x_pos = x_pos * roots[i] + p0.x; + // int n_ints = 0; + bvec3 result = bvec3(false); + for (int i = 0; i < 3; i++) + { + if (i < n_roots) + { + if (roots[i] >= 0. && roots[i] <= 1.) + { + float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x; + x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x; + x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; + x_pos = x_pos * roots[i] + p0.x; - if(x_pos > uv.x){ - result[1] = !result[1]; - } - } - } - } + if (x_pos > uv.x) + { + result[1] = !result[1]; + } + } + } + } - vec2 tang1 = p0.xy - p1.xy; - vec2 tang2 = p2.xy - p3.xy; + vec2 tang1 = p0.xy - p1.xy; + vec2 tang2 = p2.xy - p3.xy; - vec2 nor1 = vec2(tang1.y,-tang1.x); - vec2 nor2 = vec2(tang2.y,-tang2.x); + vec2 nor1 = vec2(tang1.y, -tang1.x); + vec2 nor2 = vec2(tang2.y, -tang2.x); - if(p0.y < p1.y){ - if((uv.y<=p0.y) && (dot(uv-p0.xy,nor1)>0.)){ - result[0]=!result[0]; - } - } - else{ - if(!(uv.y<=p0.y) && !(dot(uv-p0.xy,nor1)>0.)){ - result[0]=!result[0]; - } - } + if (p0.y < p1.y) + { + if ((uv.y <= p0.y) && (dot(uv - p0.xy, nor1) > 0.)) + { + result[0] = !result[0]; + } + } + else + { + if (!(uv.y <= p0.y) && !(dot(uv - p0.xy, nor1) > 0.)) + { + result[0] = !result[0]; + } + } - if(p2.y0.){ - result[2]=!result[2]; - } - } - else{ - if((uv.y<=p3.y) && !(dot(uv-p3.xy,nor2)>0.)){ - result[2]=!result[2]; - } - } - - return result; - // if(n_ints==0 || n_ints==2 || n_ints==4){ - // return 1; - // } - // else{ - // return 0; - // } + if (p2.y < p3.y) + { + if (!(uv.y <= p3.y) && dot(uv - p3.xy, nor2) > 0.) + { + result[2] = !result[2]; + } + } + else + { + if ((uv.y <= p3.y) && !(dot(uv - p3.xy, nor2) > 0.)) + { + result[2] = !result[2]; + } + } + + return result; + // if(n_ints==0 || n_ints==2 || n_ints==4){ + // return 1; + // } + // else{ + // return 0; + // } } const float eps = .000005; const int halley_iterations = 8; -//lagrange positive real root upper bound -//see for example: https://doi.org/10.1016/j.jsc.2014.09.038 -float upper_bound_lagrange5(float a0, float a1, float a2, float a3, float a4){ +// lagrange positive real root upper bound +// see for example: https://doi.org/10.1016/j.jsc.2014.09.038 +float upper_bound_lagrange5(float a0, float a1, float a2, float a3, float a4) +{ - vec4 coeffs1 = vec4(a0,a1,a2,a3); + vec4 coeffs1 = vec4(a0, a1, a2, a3); - vec4 neg1 = max(-coeffs1,vec4(0)); - float neg2 = max(-a4,0.); + vec4 neg1 = max(-coeffs1, vec4(0)); + float neg2 = max(-a4, 0.); - const vec4 indizes1 = vec4(0,1,2,3); - const float indizes2 = 4.; + const vec4 indizes1 = vec4(0, 1, 2, 3); + const float indizes2 = 4.; - vec4 bounds1 = pow(neg1,1./(5.-indizes1)); - float bounds2 = pow(neg2,1./(5.-indizes2)); + vec4 bounds1 = pow(neg1, 1. / (5. - indizes1)); + float bounds2 = pow(neg2, 1. / (5. - indizes2)); - vec2 min1_2 = min(bounds1.xz,bounds1.yw); - vec2 max1_2 = max(bounds1.xz,bounds1.yw); + vec2 min1_2 = min(bounds1.xz, bounds1.yw); + vec2 max1_2 = max(bounds1.xz, bounds1.yw); - float maxmin = max(min1_2.x,min1_2.y); - float minmax = min(max1_2.x,max1_2.y); + float maxmin = max(min1_2.x, min1_2.y); + float minmax = min(max1_2.x, max1_2.y); - float max3 = max(max1_2.x,max1_2.y); + float max3 = max(max1_2.x, max1_2.y); - float max_max = max(max3,bounds2); - float max_max2 = max(min(max3,bounds2),max(minmax,maxmin)); + float max_max = max(max3, bounds2); + float max_max2 = max(min(max3, bounds2), max(minmax, maxmin)); - return max_max + max_max2; + return max_max + max_max2; } -//lagrange upper bound applied to f(-x) to get lower bound -float lower_bound_lagrange5(float a0, float a1, float a2, float a3, float a4){ +// lagrange upper bound applied to f(-x) to get lower bound +float lower_bound_lagrange5(float a0, float a1, float a2, float a3, float a4) +{ - vec4 coeffs1 = vec4(-a0,a1,-a2,a3); + vec4 coeffs1 = vec4(-a0, a1, -a2, a3); - vec4 neg1 = max(-coeffs1,vec4(0)); - float neg2 = max(-a4,0.); + vec4 neg1 = max(-coeffs1, vec4(0)); + float neg2 = max(-a4, 0.); - const vec4 indizes1 = vec4(0,1,2,3); - const float indizes2 = 4.; + const vec4 indizes1 = vec4(0, 1, 2, 3); + const float indizes2 = 4.; - vec4 bounds1 = pow(neg1,1./(5.-indizes1)); - float bounds2 = pow(neg2,1./(5.-indizes2)); + vec4 bounds1 = pow(neg1, 1. / (5. - indizes1)); + float bounds2 = pow(neg2, 1. / (5. - indizes2)); - vec2 min1_2 = min(bounds1.xz,bounds1.yw); - vec2 max1_2 = max(bounds1.xz,bounds1.yw); + vec2 min1_2 = min(bounds1.xz, bounds1.yw); + vec2 max1_2 = max(bounds1.xz, bounds1.yw); - float maxmin = max(min1_2.x,min1_2.y); - float minmax = min(max1_2.x,max1_2.y); + float maxmin = max(min1_2.x, min1_2.y); + float minmax = min(max1_2.x, max1_2.y); - float max3 = max(max1_2.x,max1_2.y); + float max3 = max(max1_2.x, max1_2.y); - float max_max = max(max3,bounds2); - float max_max2 = max(min(max3,bounds2),max(minmax,maxmin)); + float max_max = max(max3, bounds2); + float max_max2 = max(min(max3, bounds2), max(minmax, maxmin)); - return -max_max - max_max2; + return -max_max - max_max2; } -vec2 parametric_cub_bezier(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3){ - vec2 a0 = (-p0 + 3. * p1 - 3. * p2 + p3); - vec2 a1 = (3. * p0 -6. * p1 + 3. * p2); - vec2 a2 = (-3. * p0 + 3. * p1); - vec2 a3 = p0; +vec2 parametric_cub_bezier(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3) +{ + vec2 a0 = (-p0 + 3. * p1 - 3. * p2 + p3); + vec2 a1 = (3. * p0 - 6. * p1 + 3. * p2); + vec2 a2 = (-3. * p0 + 3. * p1); + vec2 a3 = p0; - return (((a0 * t) + a1) * t + a2) * t + a3; + return (((a0 * t) + a1) * t + a2) * t + a3; } -void sort_roots3(inout vec3 roots){ - vec3 tmp; +void sort_roots3(inout vec3 roots) +{ + vec3 tmp; - tmp[0] = min(roots[0],min(roots[1],roots[2])); - tmp[1] = max(roots[0],min(roots[1],roots[2])); - tmp[2] = max(roots[0],max(roots[1],roots[2])); + tmp[0] = min(roots[0], min(roots[1], roots[2])); + tmp[1] = max(roots[0], min(roots[1], roots[2])); + tmp[2] = max(roots[0], max(roots[1], roots[2])); - roots=tmp; + roots = tmp; } -void sort_roots4(inout vec4 roots){ - vec4 tmp; +void sort_roots4(inout vec4 roots) +{ + vec4 tmp; - vec2 min1_2 = min(roots.xz,roots.yw); - vec2 max1_2 = max(roots.xz,roots.yw); + vec2 min1_2 = min(roots.xz, roots.yw); + vec2 max1_2 = max(roots.xz, roots.yw); - float maxmin = max(min1_2.x,min1_2.y); - float minmax = min(max1_2.x,max1_2.y); + float maxmin = max(min1_2.x, min1_2.y); + float minmax = min(max1_2.x, max1_2.y); - tmp[0] = min(min1_2.x,min1_2.y); - tmp[1] = min(maxmin,minmax); - tmp[2] = max(minmax,maxmin); - tmp[3] = max(max1_2.x,max1_2.y); + tmp[0] = min(min1_2.x, min1_2.y); + tmp[1] = min(maxmin, minmax); + tmp[2] = max(minmax, maxmin); + tmp[3] = max(max1_2.x, max1_2.y); - roots = tmp; + roots = tmp; } -float eval_poly5(float a0, float a1, float a2, float a3, float a4, float x){ +float eval_poly5(float a0, float a1, float a2, float a3, float a4, float x) +{ - float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0; + float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0; - return f; + return f; } -//halley's method -//basically a variant of newton raphson which converges quicker and has bigger basins of convergence -//see http://mathworld.wolfram.com/HalleysMethod.html -//or https://en.wikipedia.org/wiki/Halley%27s_method -float halley_iteration5(float a0, float a1, float a2, float a3, float a4, float x){ +// halley's method +// basically a variant of newton raphson which converges quicker and has bigger basins of convergence +// see http://mathworld.wolfram.com/HalleysMethod.html +// or https://en.wikipedia.org/wiki/Halley%27s_method +float halley_iteration5(float a0, float a1, float a2, float a3, float a4, float x) +{ - float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0; - float f1 = (((5. * x + 4. * a4) * x + 3. * a3) * x + 2. * a2) * x + a1; - float f2 = ((20. * x + 12. * a4) * x + 6. * a3) * x + 2. * a2; + float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0; + float f1 = (((5. * x + 4. * a4) * x + 3. * a3) * x + 2. * a2) * x + a1; + float f2 = ((20. * x + 12. * a4) * x + 6. * a3) * x + 2. * a2; - return x - (2. * f * f1) / (2. * f1 * f1 - f * f2); + return x - (2. * f * f1) / (2. * f1 * f1 - f * f2); } -float halley_iteration4(vec4 coeffs, float x){ +float halley_iteration4(vec4 coeffs, float x) +{ - float f = (((x + coeffs[3]) * x + coeffs[2]) * x + coeffs[1]) * x + coeffs[0]; - float f1 = ((4. * x + 3. * coeffs[3]) * x + 2. * coeffs[2]) * x + coeffs[1]; - float f2 = (12. * x + 6. * coeffs[3]) * x + 2. * coeffs[2]; + float f = (((x + coeffs[3]) * x + coeffs[2]) * x + coeffs[1]) * x + coeffs[0]; + float f1 = ((4. * x + 3. * coeffs[3]) * x + 2. * coeffs[2]) * x + coeffs[1]; + float f2 = (12. * x + 6. * coeffs[3]) * x + 2. * coeffs[2]; - return x - (2. * f * f1) / (2. * f1 * f1 - f * f2); + return x - (2. * f * f1) / (2. * f1 * f1 - f * f2); } // Modified from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c // Credits to Doublefresh for hinting there -int solve_quartic(vec4 coeffs, inout vec4 s){ +int solve_quartic(vec4 coeffs, inout vec4 s) +{ - float a = coeffs[3]; - float b = coeffs[2]; - float c = coeffs[1]; - float d = coeffs[0]; + float a = coeffs[3]; + float b = coeffs[2]; + float c = coeffs[1]; + float d = coeffs[0]; - /* substitute x = y - A/4 to eliminate cubic term: - x^4 + px^2 + qx + r = 0 */ + /* substitute x = y - A/4 to eliminate cubic term: + x^4 + px^2 + qx + r = 0 */ - float sq_a = a * a; - float p = - 3./8. * sq_a + b; - float q = 1./8. * sq_a * a - 1./2. * a * b + c; - float r = - 3./256.*sq_a*sq_a + 1./16.*sq_a*b - 1./4.*a*c + d; + float sq_a = a * a; + float p = -3. / 8. * sq_a + b; + float q = 1. / 8. * sq_a * a - 1. / 2. * a * b + c; + float r = -3. / 256. * sq_a * sq_a + 1. / 16. * sq_a * b - 1. / 4. * a * c + d; - int num; + int num; - /* doesn't seem to happen for me */ - //if(abs(r) -eps){ - u = sqrt(abs(u)); - } - else{ - return 0; - } + if (u > -eps) + { + u = sqrt(abs(u)); + } + else + { + return 0; + } - if(v > -eps){ - v = sqrt(abs(v)); - } - else{ - return 0; - } + if (v > -eps) + { + v = sqrt(abs(v)); + } + else + { + return 0; + } - vec2 quad_coeffs; + vec2 quad_coeffs; - quad_coeffs[0] = z - u; - quad_coeffs[1] = q < 0. ? -v : v; + quad_coeffs[0] = z - u; + quad_coeffs[1] = q < 0. ? -v : v; - num = solve_quadric(quad_coeffs, s.xy); + num = solve_quadric(quad_coeffs, s.xy); - quad_coeffs[0]= z + u; - quad_coeffs[1] = q < 0. ? v : -v; + quad_coeffs[0] = z + u; + quad_coeffs[1] = q < 0. ? v : -v; - vec2 tmp=vec2(1e38); - int old_num=num; + vec2 tmp = vec2(1e38); + int old_num = num; - num += solve_quadric(quad_coeffs, tmp); - if(old_num!=num){ - if(old_num == 0){ - s[0] = tmp[0]; - s[1] = tmp[1]; - } - else{//old_num == 2 - s[2] = tmp[0]; - s[3] = tmp[1]; - } - } - } + num += solve_quadric(quad_coeffs, tmp); + if (old_num != num) + { + if (old_num == 0) + { + s[0] = tmp[0]; + s[1] = tmp[1]; + } + else + { // old_num == 2 + s[2] = tmp[0]; + s[3] = tmp[1]; + } + } + } - /* resubstitute */ + /* resubstitute */ - float sub = 1./4. * a; + float sub = 1. / 4. * a; - /* single halley iteration to fix cancellation */ - for(int i=0;i<4;i+=2){ - if(i < num){ - s[i] -= sub; - s[i] = halley_iteration4(coeffs,s[i]); + /* single halley iteration to fix cancellation */ + for (int i = 0; i < 4; i += 2) + { + if (i < num) + { + s[i] -= sub; + s[i] = halley_iteration4(coeffs, s[i]); - s[i+1] -= sub; - s[i+1] = halley_iteration4(coeffs,s[i+1]); - } - } + s[i + 1] -= sub; + s[i + 1] = halley_iteration4(coeffs, s[i + 1]); + } + } - return num; + return num; } -float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEnd){ +float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEnd) +{ - //switch points when near to end point to minimize numerical error - //only needed when control point(s) very far away - #if 0 +// switch points when near to end point to minimize numerical error +// only needed when control point(s) very far away +#if 0 vec2 mid_curve = parametric_cub_bezier(.5,p0,p1,p2,p3); vec2 mid_points = (p0 + p3)/2.; @@ -606,322 +642,355 @@ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEn p2 = p1; p1 = tmp; } - #endif +#endif - vec2 a3 = (-p0 + 3. * p1 - 3. * p2 + p3); - vec2 a2 = (3. * p0 - 6. * p1 + 3. * p2); - vec2 a1 = (-3. * p0 + 3. * p1); - vec2 a0 = p0 - uv; - - //compute polynomial describing distance to current pixel dependent on a parameter t - float bc6 = dot(a3,a3); - float bc5 = 2.*dot(a3,a2); - float bc4 = dot(a2,a2) + 2.*dot(a1,a3); - float bc3 = 2.*(dot(a1,a2) + dot(a0,a3)); - float bc2 = dot(a1,a1) + 2.*dot(a0,a2); - float bc1 = 2.*dot(a0,a1); - float bc0 = dot(a0,a0); + vec2 a3 = (-p0 + 3. * p1 - 3. * p2 + p3); + vec2 a2 = (3. * p0 - 6. * p1 + 3. * p2); + vec2 a1 = (-3. * p0 + 3. * p1); + vec2 a0 = p0 - uv; - bc5 /= bc6; - bc4 /= bc6; - bc3 /= bc6; - bc2 /= bc6; - bc1 /= bc6; - bc0 /= bc6; - - //compute derivatives of this polynomial + // compute polynomial describing distance to current pixel dependent on a parameter t + float bc6 = dot(a3, a3); + float bc5 = 2. * dot(a3, a2); + float bc4 = dot(a2, a2) + 2. * dot(a1, a3); + float bc3 = 2. * (dot(a1, a2) + dot(a0, a3)); + float bc2 = dot(a1, a1) + 2. * dot(a0, a2); + float bc1 = 2. * dot(a0, a1); + float bc0 = dot(a0, a0); - float b0 = bc1 / 6.; - float b1 = 2. * bc2 / 6.; - float b2 = 3. * bc3 / 6.; - float b3 = 4. * bc4 / 6.; - float b4 = 5. * bc5 / 6.; + bc5 /= bc6; + bc4 /= bc6; + bc3 /= bc6; + bc2 /= bc6; + bc1 /= bc6; + bc0 /= bc6; - vec4 c1 = vec4(b1,2.*b2,3.*b3,4.*b4)/5.; - vec3 c2 = vec3(c1[1],2.*c1[2],3.*c1[3])/4.; - vec2 c3 = vec2(c2[1],2.*c2[2])/3.; - float c4 = c3[1]/2.; + // compute derivatives of this polynomial - vec4 roots_drv = vec4(1e38); + float b0 = bc1 / 6.; + float b1 = 2. * bc2 / 6.; + float b2 = 3. * bc3 / 6.; + float b3 = 4. * bc4 / 6.; + float b4 = 5. * bc5 / 6.; - int num_roots_drv = solve_quartic(c1,roots_drv); - sort_roots4(roots_drv); + vec4 c1 = vec4(b1, 2. * b2, 3. * b3, 4. * b4) / 5.; + vec3 c2 = vec3(c1[1], 2. * c1[2], 3. * c1[3]) / 4.; + vec2 c3 = vec2(c2[1], 2. * c2[2]) / 3.; + float c4 = c3[1] / 2.; - float ub = upper_bound_lagrange5(b0,b1,b2,b3,b4); - float lb = lower_bound_lagrange5(b0,b1,b2,b3,b4); + vec4 roots_drv = vec4(1e38); - vec3 a = vec3(1e38); - vec3 b = vec3(1e38); + int num_roots_drv = solve_quartic(c1, roots_drv); + sort_roots4(roots_drv); - vec3 roots = vec3(1e38); + float ub = upper_bound_lagrange5(b0, b1, b2, b3, b4); + float lb = lower_bound_lagrange5(b0, b1, b2, b3, b4); - int num_roots = 0; - - //compute root isolating intervals by roots of derivative and outer root bounds - //only roots going form - to + considered, because only those result in a minimum - if(num_roots_drv==4){ - if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[0]) > 0.){ - a[0]=lb; - b[0]=roots_drv[0]; - num_roots=1; - } + vec3 a = vec3(1e38); + vec3 b = vec3(1e38); - if(sign(eval_poly5(b0,b1,b2,b3,b4,roots_drv[1])) != sign(eval_poly5(b0,b1,b2,b3,b4,roots_drv[2]))){ - if(num_roots == 0){ - a[0]=roots_drv[1]; - b[0]=roots_drv[2]; - num_roots=1; - } - else{ - a[1]=roots_drv[1]; - b[1]=roots_drv[2]; - num_roots=2; - } - } + vec3 roots = vec3(1e38); - if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[3]) < 0.){ - if(num_roots == 0){ - a[0]=roots_drv[3]; - b[0]=ub; - num_roots=1; - } - else if(num_roots == 1){ - a[1]=roots_drv[3]; - b[1]=ub; - num_roots=2; - } - else{ - a[2]=roots_drv[3]; - b[2]=ub; - num_roots=3; - } - } - } - else{ - if(num_roots_drv==2){ - if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[0]) < 0.){ - num_roots=1; - a[0]=roots_drv[1]; - b[0]=ub; - } - else if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[1]) > 0.){ - num_roots=1; - a[0]=lb; - b[0]=roots_drv[0]; - } - else{ - num_roots=2; + int num_roots = 0; - a[0]=lb; - b[0]=roots_drv[0]; + // compute root isolating intervals by roots of derivative and outer root bounds + // only roots going form - to + considered, because only those result in a minimum + if (num_roots_drv == 4) + { + if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[0]) > 0.) + { + a[0] = lb; + b[0] = roots_drv[0]; + num_roots = 1; + } - a[1]=roots_drv[1]; - b[1]=ub; - } + if (sign(eval_poly5(b0, b1, b2, b3, b4, roots_drv[1])) != sign(eval_poly5(b0, b1, b2, b3, b4, roots_drv[2]))) + { + if (num_roots == 0) + { + a[0] = roots_drv[1]; + b[0] = roots_drv[2]; + num_roots = 1; + } + else + { + a[1] = roots_drv[1]; + b[1] = roots_drv[2]; + num_roots = 2; + } + } - } - else{//num_roots_drv==0 - vec3 roots_snd_drv=vec3(1e38); - int num_roots_snd_drv=solve_cubic(c2,roots_snd_drv); + if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[3]) < 0.) + { + if (num_roots == 0) + { + a[0] = roots_drv[3]; + b[0] = ub; + num_roots = 1; + } + else if (num_roots == 1) + { + a[1] = roots_drv[3]; + b[1] = ub; + num_roots = 2; + } + else + { + a[2] = roots_drv[3]; + b[2] = ub; + num_roots = 3; + } + } + } + else + { + if (num_roots_drv == 2) + { + if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[0]) < 0.) + { + num_roots = 1; + a[0] = roots_drv[1]; + b[0] = ub; + } + else if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[1]) > 0.) + { + num_roots = 1; + a[0] = lb; + b[0] = roots_drv[0]; + } + else + { + num_roots = 2; - vec2 roots_trd_drv=vec2(1e38); - int num_roots_trd_drv=solve_quadric(c3,roots_trd_drv); - num_roots=1; + a[0] = lb; + b[0] = roots_drv[0]; - a[0]=lb; - b[0]=ub; - } - - //further subdivide intervals to guarantee convergence of halley's method - //by using roots of further derivatives - vec3 roots_snd_drv=vec3(1e38); - int num_roots_snd_drv=solve_cubic(c2,roots_snd_drv); - sort_roots3(roots_snd_drv); + a[1] = roots_drv[1]; + b[1] = ub; + } + } + else + { // num_roots_drv==0 + vec3 roots_snd_drv = vec3(1e38); + int num_roots_snd_drv = solve_cubic(c2, roots_snd_drv); - int num_roots_trd_drv=0; - vec2 roots_trd_drv=vec2(1e38); + vec2 roots_trd_drv = vec2(1e38); + int num_roots_trd_drv = solve_quadric(c3, roots_trd_drv); + num_roots = 1; - if(num_roots_snd_drv!=3){ - num_roots_trd_drv=solve_quadric(c3,roots_trd_drv); - } + a[0] = lb; + b[0] = ub; + } - for(int i=0;i<3;i++){ - if(i < num_roots){ - for(int j=0;j<3;j+=2){ - if(j < num_roots_snd_drv){ - if(a[i] < roots_snd_drv[j] && b[i] > roots_snd_drv[j]){ - if(eval_poly5(b0,b1,b2,b3,b4,roots_snd_drv[j]) > 0.){ - b[i]=roots_snd_drv[j]; - } - else{ - a[i]=roots_snd_drv[j]; - } - } - } - } - for(int j=0;j<2;j++){ - if(j < num_roots_trd_drv){ - if(a[i] < roots_trd_drv[j] && b[i] > roots_trd_drv[j]){ - if(eval_poly5(b0,b1,b2,b3,b4,roots_trd_drv[j]) > 0.){ - b[i]=roots_trd_drv[j]; - } - else{ - a[i]=roots_trd_drv[j]; - } - } - } - } - } - } - } + // further subdivide intervals to guarantee convergence of halley's method + // by using roots of further derivatives + vec3 roots_snd_drv = vec3(1e38); + int num_roots_snd_drv = solve_cubic(c2, roots_snd_drv); + sort_roots3(roots_snd_drv); - float d0 = 1e38; + int num_roots_trd_drv = 0; + vec2 roots_trd_drv = vec2(1e38); - //compute roots with halley's method - - for(int i=0;i<3;i++){ - if(i < num_roots){ - roots[i] = .5 * (a[i] + b[i]); + if (num_roots_snd_drv != 3) + { + num_roots_trd_drv = solve_quadric(c3, roots_trd_drv); + } - for(int j=0;j roots_snd_drv[j]) + { + if (eval_poly5(b0, b1, b2, b3, b4, roots_snd_drv[j]) > 0.) + { + b[i] = roots_snd_drv[j]; + } + else + { + a[i] = roots_snd_drv[j]; + } + } + } + } + for (int j = 0; j < 2; j++) + { + if (j < num_roots_trd_drv) + { + if (a[i] < roots_trd_drv[j] && b[i] > roots_trd_drv[j]) + { + if (eval_poly5(b0, b1, b2, b3, b4, roots_trd_drv[j]) > 0.) + { + b[i] = roots_trd_drv[j]; + } + else + { + a[i] = roots_trd_drv[j]; + } + } + } + } + } + } + } - //compute squared distance to nearest point on curve - if(roundEnd) - { - roots[i] = clamp(roots[i],0.,1.); - vec2 to_curve = uv - parametric_cub_bezier(roots[i],p0,p1,p2,p3); - d0 = min(d0,dot(to_curve,to_curve)); - } - else - { - if(roots[i]<0.||roots[i]>1.) d0=min(d0,1e38); - else - { - vec2 to_curve = uv - parametric_cub_bezier(roots[i],p0,p1,p2,p3); - d0 = min(d0,dot(to_curve,to_curve)); - } - } - - } - } + float d0 = 1e38; - return sqrt(d0); + // compute roots with halley's method + + for (int i = 0; i < 3; i++) + { + if (i < num_roots) + { + roots[i] = .5 * (a[i] + b[i]); + + for (int j = 0; j < halley_iterations; j++) + { + roots[i] = halley_iteration5(b0, b1, b2, b3, b4, roots[i]); + } + + // compute squared distance to nearest point on curve + if (roundEnd) + { + roots[i] = clamp(roots[i], 0., 1.); + vec2 to_curve = uv - parametric_cub_bezier(roots[i], p0, p1, p2, p3); + d0 = min(d0, dot(to_curve, to_curve)); + } + else + { + if (roots[i] < 0. || roots[i] > 1.) + d0 = min(d0, 1e38); + else + { + vec2 to_curve = uv - parametric_cub_bezier(roots[i], p0, p1, p2, p3); + d0 = min(d0, dot(to_curve, to_curve)); + } + } + } + } + + return sqrt(d0); } - int cubic_bezier_int_test2(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool reverse) { - float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y); - float qu = (3. * p0.y - 6. * p1.y + 3. * p2.y); - float li = (-3. * p0.y + 3. * p1.y); - float co = p0.y - uv.y; + float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y); + float qu = (3. * p0.y - 6. * p1.y + 3. * p2.y); + float li = (-3. * p0.y + 3. * p1.y); + float co = p0.y - uv.y; - vec3 roots = vec3(1e38); - int n_roots; + vec3 roots = vec3(1e38); + int n_roots; - int n_ints = 0; + int n_ints = 0; - if (reverse? uv.x > max(max(p0.x, p1.x), max(p2.x, p3.x)): uv.x < min(min(p0.x, p1.x), min(p2.x, p3.x))) - { - if (uv.y >= min(p0.y, p3.y) && uv.y <= max(p0.y, p3.y)) - n_ints = 1; - } - else - { - if (abs(cu) < .0001) n_roots = solve_quadric(vec2(co / qu, li / qu), roots.xy); - else n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots); - - for (int i = 0; i < n_roots; i++) - { - if (roots[i] >= 0. && roots[i] <= 1.) - { - float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x; - x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x; - x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; - x_pos = x_pos * roots[i] + p0.x; + if (reverse ? uv.x > max(max(p0.x, p1.x), max(p2.x, p3.x)) : uv.x < min(min(p0.x, p1.x), min(p2.x, p3.x))) + { + if (uv.y >= min(p0.y, p3.y) && uv.y <= max(p0.y, p3.y)) + n_ints = 1; + } + else + { + if (abs(cu) < .0001) + n_roots = solve_quadric(vec2(co / qu, li / qu), roots.xy); + else + n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots); - if (reverse? x_pos < uv.x: x_pos > uv.x) n_ints++; - } - } - } - return n_ints; + for (int i = 0; i < n_roots; i++) + { + if (roots[i] >= 0. && roots[i] <= 1.) + { + float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x; + x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x; + x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; + x_pos = x_pos * roots[i] + p0.x; + + if (reverse ? x_pos < uv.x : x_pos > uv.x) + n_ints++; + } + } + } + return n_ints; } int ray_int_test(vec2 uv, vec2 p0, vec2 direction, bool reverse) { - p0 -= uv; - if (-p0.y * direction.y > 0.) - { - vec2 nor = -direction; - nor = vec2(nor.y, -nor.x); - float sgn = p0.y > direction.y? 1.: -1.; - if(reverse) sgn = -sgn; - return dot(nor, p0) * sgn < 0.? 0: 1; - } - else return 0; + p0 -= uv; + if (-p0.y * direction.y > 0.) + { + vec2 nor = -direction; + nor = vec2(nor.y, -nor.x); + float sgn = p0.y > direction.y ? 1. : -1.; + if (reverse) + sgn = -sgn; + return dot(nor, p0) * sgn < 0. ? 0 : 1; + } + else + return 0; } vec2 bezierTangent(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3) { - float u = 1 - t; - float uu = u * u; - float tu = t * u; - float tt = t * t; + float u = 1 - t; + float uu = u * u; + float tu = t * u; + float tt = t * t; - vec2 P = p0 * 3 * uu * (-1.0); - P += p1 * 3 * (uu - 2 * tu); - P += p2 * 3 * (2 * tu - tt); - P += p3 * 3 * tt; + vec2 P = p0 * 3 * uu * (-1.0); + P += p1 * 3 * (uu - 2 * tu); + P += p2 * 3 * (2 * tu - tt); + P += p3 * 3 * tt; - //صλ - return normalize(P); + // صλ + return normalize(P); } -//жн(Aʱ뵽B)Ƿ180㣬180㷵棬򷵻ؼ +// жн(Aʱ뵽B)Ƿ180㣬180㷵棬򷵻ؼ bool angleLargeThanPi(vec2 a, vec2 b) { - return a.x * b.y - b.x * a.y < 0; + return a.x * b.y - b.x * a.y < 0; } void drawLine(in float d, in uint styleIndex, out vec4 elementColor, out vec2 metallicRoughness) { - elementColor = vec4(1); - metallicRoughness = vec2(0.8); - uint headUint = floatBitsToUint(elementData[styleIndex+1]); + elementColor = vec4(1); + metallicRoughness = vec2(0.8); + uint headUint = floatBitsToUint(elementData[styleIndex + 1]); vec4 head = unpackUnorm4x8(headUint); - switch (int(head.a*100)%10) - //switch (2) + switch (int(head.a * 100) % 10) + // switch (2) { /// Plain case 0: { metallicRoughness = head.rg; - elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+2])).rgb, 1); + elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 2])).rgb, 1); break; } /// RadialGradient case 1: { - uint size = headUint%(1<<15); - bool gradual = (headUint&(1<<15))!=0; + uint size = headUint % (1 << 15); + bool gradual = (headUint & (1 << 15)) != 0; - uint lastData = floatBitsToUint(elementData[styleIndex+2+0*2]); + uint lastData = floatBitsToUint(elementData[styleIndex + 2 + 0 * 2]); float lastLevel = 0; vec2 lastMetallicRoughness = unpackUnorm4x8(lastData).rg; - vec4 lastColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+3+0*2])).rgb, 1); + vec4 lastColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 3 + 0 * 2])).rgb, 1); - for(uint i = 0; i < size; i++) + for (uint i = 0; i < size; i++) { - uint data = floatBitsToUint(elementData[styleIndex+2+i*2]); + uint data = floatBitsToUint(elementData[styleIndex + 2 + i * 2]); float level = unpackUnorm2x16(data).y; - vec4 currentColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+3+i*2])).rgb, 1); + vec4 currentColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 3 + i * 2])).rgb, 1); vec2 currentMetallicRoughness = unpackUnorm4x8(data).rg; if (d <= level) { - if(gradual) + if (gradual) { - float a = (d-lastLevel)/(level-lastLevel); + float a = (d - lastLevel) / (level - lastLevel); elementColor = mix(lastColor, currentColor, a); metallicRoughness = mix(lastMetallicRoughness, currentMetallicRoughness, a); } @@ -962,307 +1031,385 @@ void drawLine(in float d, in uint styleIndex, out vec4 elementColor, out vec2 me } } -bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, out vec2 metallicRoughness, inout vec3 debugBVH = vec3(0)) +bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p0, vec2 tangentBegin, vec2 tangentEndLast) { - bool hitElement = false; - vec4 elementColor = vec4(-1); - metallicRoughness = vec2(0, 0.8); - - uint currentOffset[] = elementOffset[elementIndex]; - uint elementBvhRoot = currentOffset[0]; - uint styleIndex = currentOffset[1]; - uint elementBvhLength = 0x80000000; - uint pointsOffset = currentOffset[2]; - uint linesOffset = currentOffset[3]; - - elementStack.top = 0; - uint elementBvhIndex = 0; - while (elementBvhIndex < elementBvhLength || !elementStack.empty()) - { - - while (elementBvhIndex < elementBvhLength) - { - vec4 bound = bvhBound[elementBvhRoot + elementBvhIndex]; - uint leftChild = bvhChildren[elementBvhRoot + elementBvhIndex].x; - - if (all(lessThan(bound.xy, localUV)) && all(lessThan(localUV, bound.zw))) - { - if (leftChild >= elementBvhLength) - { - if (any(greaterThan(bound.xy+vec2(0.003), localUV)) || any(greaterThan(localUV, bound.zw-vec2(0.003)))) - { - debugBVH.g = 0; - debugBVH.r += 1; - } - //uint styleIndex = bvhChildren[elementBvhRoot + elementBvhIndex].y; - //uint elementType = bvhChildren[elementBvhRoot + elementBvhIndex].y; - //float elementType = elementData[styleIndex]; - bool isFillStyle = elementData[styleIndex]<=0; - // for(int i = 0; i<200;i++) - if (isFillStyle) // - { - uint contourIndex = linesOffset + leftChild - 0x80000000; - - uint num_its = 0; - - uint lineCount = elementIndexs[contourIndex]; - - for ( uint contourIterator = contourIndex + 1;contourIterator < contourIndex + 1 + lineCount; contourIterator++) - { - uint lineIndex = elementIndexs[contourIterator]; - uint pLocation = linesOffset + 2 * lineIndex; - uvec4 pxIndex = uvec4(pointsOffset)+2*uvec4(elementIndexs[pLocation]>>16, elementIndexs[pLocation]&0xFFFF, elementIndexs[pLocation+1]>>16, elementIndexs[pLocation+1]&0xFFFF); - uvec4 pyIndex = uvec4(1)+pxIndex; - mat4x2 p = mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], - elementData[pxIndex[1]], elementData[pyIndex[1]], - elementData[pxIndex[2]],elementData[pyIndex[2]], - elementData[pxIndex[3]], elementData[pyIndex[3]]); -// vec2 p[4] = {vec2(elementData[pxIndex[0]], elementData[pyIndex[0]]), -// vec2(elementData[pxIndex[1]], elementData[pyIndex[1]]), -// vec2(elementData[pxIndex[2]],elementData[pyIndex[2]]), -// vec2(elementData[pxIndex[3]], elementData[pyIndex[3]])}; -// if( bound.z==p[0].x && distance(localUV, p[3])<0.01) -// { -// debugBVH = vec3(0,0,1); -// } -// else if(distance(localUV, p[0])<0.01) -// debugBVH = vec3(1,1,1); -// if (p0 == p1 && p2 == p3) -// { -// num_its += segment_int_test(localUV, p0, p3); -// } -// -// else - num_its += cubic_bezier_int_test(localUV, p[0], p[1], p[2], p[3]); - } - - if (num_its % 2 == 1 && elementColor.a<1) - { - hitElement = true; - elementColor = vec4(1,1,0,0); - - vec4 head = unpackUnorm4x8(floatBitsToUint(elementData[styleIndex])); - if(head.z==0) - { - elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+1])).rgb,0); - metallicRoughness = head.xy; - } - - - } - } - else // - { - float strokeWidth = elementData[styleIndex]; - float widthHeightRatio = uintBitsToFloat(currentOffset[4]); - - vec2 size = normalize(vec2(widthHeightRatio, 1)) + vec2(2 * strokeWidth); - vec2 ratio = widthHeightRatio < 1 ? vec2(widthHeightRatio, 1) : vec2(1, 1 / widthHeightRatio); - localUV *= ratio; - - - uint contourIndex = linesOffset + leftChild - 0x80000000; - float minDistance = 1e38; - uint lineCount = elementIndexs[contourIndex]; - vec4 styleHead = unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+1])); - float lineType = floor(styleHead.b*10); - //float lineType = 2; - vec2 p3Last = vec2(1e38); - vec2 p2Last = vec2(1e38); - int debugBegin = 0; - for ( uint contourIterator_ = contourIndex + 1;contourIterator_ <= contourIndex + 1 + lineCount; contourIterator_++) - { - uint contourIterator = contourIterator_; - if(contourIterator_==contourIndex + 1 + lineCount) - contourIterator = contourIndex + 1; - uint lineIndex = elementIndexs[contourIterator]; - uint pLocation = linesOffset + 3 * lineIndex; - uvec4 pxIndex = uvec4(pointsOffset)+2*uvec4(elementIndexs[pLocation]>>16, elementIndexs[pLocation]&0xFFFF, elementIndexs[pLocation+1]>>16, elementIndexs[pLocation+1]&0xFFFF); - uvec4 pyIndex = uvec4(1)+pxIndex; - - mat4x2 p = mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], - elementData[pxIndex[1]], elementData[pyIndex[1]], - elementData[pxIndex[2]], elementData[pyIndex[2]], - elementData[pxIndex[3]], elementData[pyIndex[3]]); - - p[0] *= ratio; - p[1] *= ratio; - p[2] *= ratio; - p[3] *= ratio; - - if(p[0]==p[1]&&p[2]==p[3]) - { - p[1] = (p[0]+p[3])/2; - p[2] = p[1]; - } - - if(distance(localUV,p[0])<=0.001) - { - if(p3Last==p[0]) debugBegin = 2; - else debugBegin = 1; - } - - float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true); - 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> 16, elementIndexs[pLocation] & 0xFFFF, + elementIndexs[pLocation + 1] >> 16, elementIndexs[pLocation + 1] & 0xFFFF); + uvec4 pyIndex = uvec4(1) + pxIndex; + mat4x2 p = + mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], elementData[pxIndex[1]], elementData[pyIndex[1]], + elementData[pxIndex[2]], elementData[pyIndex[2]], elementData[pxIndex[3]], elementData[pyIndex[3]]); + + // if (bound.z == p[0].x && distance(localUV, p[3]) < 0.01) + // { + // debugBVH = vec3(0, 0, 1); + // } + // else if (distance(localUV, p[0]) < 0.01) + // debugBVH = vec3(1, 1, 1); + // if (p0 == p1 && p2 == p3) + // { + // num_its += segment_int_test(localUV, p0, p3); + // } + // else + num_its += cubic_bezier_int_test(localUV, p[0], p[1], p[2], p[3]); + } + + if (num_its % 2 == 1 && elementColor.a < 1) + { + hitElement = true; + elementColor = vec4(1, 1, 0, 0); + + vec4 head = unpackUnorm4x8(floatBitsToUint(elementData[styleIndex])); + if (head.z == 0) + { + elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 1])).rgb, 0); + metallicRoughness = head.xy; + } + } + return hitElement; +} + +bool strokeElement(vec2 localUV, uint contourIndex, uint linesOffset, uint pointsOffset, uint styleIndex, + inout vec4 elementColor, inout vec2 metallicRoughness) +{ + bool hitElement = false; + float strokeWidth = elementData[styleIndex]; + + float minDistance = 1e38; + uint lineCount = elementIndexs[contourIndex]; + vec4 styleHead = unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 1])); + float lineType = floor(styleHead.b * 10); + int endType = int(round(styleHead.b * 100)) % 10; + vec2 p3Last = vec2(1e38); + vec2 p2Last = vec2(1e38); + vec2 tangentEndLast; + int debugBegin = 0; + for (uint contourIterator_ = contourIndex + 1; contourIterator_ < contourIndex + 1 + lineCount; contourIterator_++) + { + uint contourIterator = contourIterator_; + if (contourIterator_ == contourIndex + 1 + lineCount) + contourIterator = contourIndex + 1; + uint lineIndex = elementIndexs[contourIterator]; + uint pLocation = linesOffset + 3 * lineIndex; + vec2 percent = unpackUnorm2x16(elementIndexs[pLocation + 2]); + uvec4 pxIndex = + uvec4(pointsOffset) + 2 * uvec4(elementIndexs[pLocation] >> 16, elementIndexs[pLocation] & 0xFFFF, + elementIndexs[pLocation + 1] >> 16, elementIndexs[pLocation + 1] & 0xFFFF); + uvec4 pyIndex = uvec4(1) + pxIndex; + + mat4x2 p = + mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], elementData[pxIndex[1]], elementData[pyIndex[1]], + elementData[pxIndex[2]], elementData[pyIndex[2]], elementData[pxIndex[3]], elementData[pyIndex[3]]); + + vec2 tangentBeginNext = vec2(0); + if (contourIterator + 1 < contourIndex + 1 + lineCount) + { + uint lineIndex = elementIndexs[contourIterator + 1]; + uint pLocation = linesOffset + 3 * lineIndex; + // vec2 percent = unpackUnorm2x16(elementIndexs[pLocation + 2]); + uvec4 pxIndex = uvec4(pointsOffset) + + 2 * uvec4(elementIndexs[pLocation] >> 16, elementIndexs[pLocation] & 0xFFFF, + elementIndexs[pLocation + 1] >> 16, elementIndexs[pLocation + 1] & 0xFFFF); + uvec4 pyIndex = uvec4(1) + pxIndex; + + mat4x2 pNext = mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], elementData[pxIndex[1]], + elementData[pyIndex[1]], elementData[pxIndex[2]], elementData[pyIndex[2]], + elementData[pxIndex[3]], elementData[pyIndex[3]]); + + if (pNext[0] == pNext[1] && pNext[2] == pNext[3]) + { + pNext[1] = (pNext[0] + pNext[3]) / 2; + pNext[2] = pNext[1]; + } + + // if(pNext[0]!=p[3]) + // break; + if (pNext[0] != pNext[1]) + tangentBeginNext = normalize(pNext[0] - pNext[1]); + else + tangentBeginNext = normalize(pNext[0] - pNext[2]); + } + + if (p[0] == p[1] && p[2] == p[3]) + { + p[1] = (p[0] + p[3]) / 2; + p[2] = p[1]; + } + + if (distance(localUV, p[0]) <= 0.001) + { + if (p3Last == p[0]) + debugBegin = 2; + else + debugBegin = 1; + } + + float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true); + if (d <= strokeWidth) + { + bool onBegin = + distance(localUV, p[0]) <= strokeWidth; //&& (p3Last == p[0] || contourIterator == contourIndex + 1); + bool onEnd = distance(localUV, p[3]) <= strokeWidth; + + 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]); + + bool hit = d < minDistance; + if (onBegin) + hit = hit && shouldFillBeginCap(localUV, contourIterator == contourIndex + 1, endType, p[0], + tangentBegin, p3Last - p2Last); + if (onEnd) + hit = hit && shouldFillEndCap(localUV, tangentBeginNext == vec2(0), endType, p[3], tangentEnd, + tangentBeginNext); + if (hit) + { + + bool reverse = p[3].y - p[0].y < 0.; + + // if (tangentBegin.y == 0.) + // tangentBegin.y = reverse ? eps : -eps; + // if (tangentEnd.y == 0.) + // tangentEnd.y = reverse ? -eps : eps; + 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))) + { + minDistance = min(minDistance, d); + hitElement = true; + // elementColor = vec4(1, 1, 0, 1); + vec2 metallicRoughness; + drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness); + } + // else if (p3Last == p[0]) + // hitElement = false; + } + tangentEndLast = tangentEnd; + } + p3Last = p[3]; + p2Last = p[2]; + } + if (hitElement && distance(localUV, p3Last) <= strokeWidth) + { + // hitElement = shouldFillEndCap(localUV, percent[1] > 1 - 1e-5, endType, p3Last, tangentEndLast, vec2(0)); + } + + // if (minDistance <= 0.001) + // { + // hitElement = true; + // elementColor = vec4(0, 0, 0, 1); + // if (debugBegin == 1) + // elementColor = vec4(1, 1, 0, 1); + // else if (debugBegin == 2) + // elementColor = vec4(0, 1, 0, 1); + // } + return hitElement; +} + +bool drawElement(uint elementIndex, vec2 localUV, out vec3 color, out vec2 metallicRoughness, + inout vec3 debugBVH = vec3(0)) +{ + bool hitElement = false; + vec4 elementColor = vec4(-1); + metallicRoughness = vec2(0, 0.8); + + uint currentOffset[] = elementOffset[elementIndex]; + uint elementBvhRoot = currentOffset[0]; + uint styleIndex = currentOffset[1]; + uint pointsOffset = currentOffset[2]; + uint linesOffset = currentOffset[3]; + // float widthHeightRatio = uintBitsToFloat(currentOffset[4]); + + elementStack.top = 0; + uint elementBvhIndex = 0; + uint elementBvhLength = 0x80000000; + while (elementBvhIndex < elementBvhLength || !elementStack.empty()) + { + while (elementBvhIndex < elementBvhLength) + { + vec4 bound = bvhBound[elementBvhRoot + elementBvhIndex]; + uint leftChild = bvhChildren[elementBvhRoot + elementBvhIndex].x; + + if (all(lessThan(bound.xy, localUV)) && all(lessThan(localUV, bound.zw))) + { + if (leftChild >= elementBvhLength) + { + if (any(greaterThan(bound.xy + vec2(0.003), localUV)) || + any(greaterThan(localUV, bound.zw - vec2(0.003)))) + { + debugBVH.g = 0; + debugBVH.r += 1; + } + + uint contourIndex = linesOffset + leftChild - 0x80000000; + bool isFillStyle = elementData[styleIndex] <= 0; + if (isFillStyle) // + { + hitElement = fillElement(localUV, contourIndex, linesOffset, pointsOffset, styleIndex, + elementColor, metallicRoughness); + } + else // + { + hitElement = strokeElement(localUV, contourIndex, linesOffset, pointsOffset, styleIndex, + elementColor, metallicRoughness); + } + + elementBvhIndex = elementBvhLength; + color = elementColor.xyz; + return hitElement; + } + else + { + // debugBVH.b += 0.2; + elementStack.push(elementBvhIndex); + elementBvhIndex = leftChild; + } + } + else + elementBvhIndex = elementBvhLength; + } + if (!elementStack.empty()) + { + elementStack.getTop(elementBvhIndex); + elementStack.pop(); + elementBvhIndex = bvhChildren[elementBvhRoot + elementBvhIndex].y; + } + } + color = elementColor.xyz; + return hitElement; +} void main() { - ivec2 pixelLocation = ivec2(pixelOffset + gl_GlobalInvocationID.xy); - vec2 uv = (pixelLocation + vec2(0.5)) / imageSize(gBaseColor); - //imageStore(gBaseColor, pixelLocation, vec4(uv,1,1)); - //imageStore(gMetallicRoughness, pixelLocation, vec4(uv,1,1)); - //return; - uv = uv*2-vec2(1); - //vec2 uv = imageLoad(gPaintingTexCoord, pixelLocation).rg; + ivec2 pixelLocation = ivec2(pixelOffset + gl_GlobalInvocationID.xy); + vec2 uv = (pixelLocation + vec2(0.5)) / imageSize(gBaseColor); + // imageStore(gBaseColor, pixelLocation, vec4(uv,1,1)); + // imageStore(gMetallicRoughness, pixelLocation, vec4(uv,1,1)); + // return; + uv = uv * 2 - vec2(1); + // vec2 uv = imageLoad(gPaintingTexCoord, pixelLocation).rg; - vec3 debugBVH = vec3(0); - //bool debugHit = false; - vec4 color = vec4(1,1,1,-1); - vec2 metallicRoughness = vec2(0, 0.8); - stack.top = 0; - uint index = 0, visitTime = 0; - //uint bvhLength = paintingOffsets[paintingIndex-1].y; - uint bvhLength = 0x80000000; - while (index < bvhLength || !stack.empty()) - { - while (index < bvhLength) - { - visitTime++; - vec4 bound = bvhBound[index]; - uint leftChild = bvhChildren[index].x; - if (leftChild >= bvhLength) - { - uint zIndex = bvhChildren[index].y >> 18; - bvec2 flip = bvec2(bvhChildren[index].y & (1<<16), bvhChildren[index].y & (1<<17)); - float angle = (float(bvhChildren[index].y&((1<<16)-1)) / 65535.0) * 2 * PI; - mat2 rotation = {{cos(angle), -sin(angle)}, {sin(angle), cos(angle)}}; - vec2 localUV = uv - (bound.xy + bound.zw) / 2; - localUV = rotation * localUV; - vec2 scale = (bound.zw - bound.xy) / 2; - localUV /= scale; - if (all(lessThan(vec2(-1), localUV)) && all(lessThan(localUV, vec2(1))) && zIndex>color.w) - { - //if (any(greaterThan(bound.xy+vec2(0.005), uv)) || any(greaterThan(uv, bound.zw-vec2(0.005)))) - if (any(greaterThan(vec2(-1)+vec2(0.005), localUV)) || any(greaterThan(localUV, vec2(1)-vec2(0.005)))) - debugBVH.g += 0.3; - //uint elementIndex = leftChild - bvhLength; - //debugBVH.bg += 0.5 * (localUV + vec2(1)); + vec3 debugBVH = vec3(0); + // bool debugHit = false; + // vec4 color = vec4(0.76, 0.33, 0.15, -1); + vec4 color = vec4(backgroundColor, -1); + vec2 metallicRoughness = vec2(0, 0.8); + stack.top = 0; + uint index = 0, visitTime = 0; + // uint bvhLength = paintingOffsets[paintingIndex-1].y; + uint bvhLength = 0x80000000; + while (index < bvhLength || !stack.empty()) + { + while (index < bvhLength) + { + visitTime++; + vec4 bound = bvhBound[index]; + uint leftChild = bvhChildren[index].x; - //debugBVH = vec3(0); - if(flip.x) localUV.x = -localUV.x; - if(flip.y) localUV.y = -localUV.y; - vec3 elementColor; - vec2 elementMetallicRoughness; - if(drawElement(leftChild - 0x80000000, localUV, scale, elementColor, elementMetallicRoughness, debugBVH)) - { - color = vec4(elementColor, zIndex); - metallicRoughness = elementMetallicRoughness; - } - - } + if (all(lessThan(bound.xy, uv)) && all(lessThan(uv, bound.zw))) + { + if (any(greaterThan(bound.xy + vec2(0.005), uv)) || any(greaterThan(uv, bound.zw - vec2(0.005)))) + debugBVH.g += 0.3; - index = bvhLength; - } - else if (all(lessThan(bound.xy, uv)) && all(lessThan(uv, bound.zw))) - { - if (any(greaterThan(bound.xy+vec2(0.005), uv)) || any(greaterThan(uv, bound.zw-vec2(0.005)))) - debugBVH.g += 0.3; - //debugBVH.r += 0.02; - stack.push(index); - index = leftChild; - } - else - index = bvhLength; - } - if (!stack.empty()) - { - stack.getTop(index); - stack.pop(); - index = bvhChildren[index].y; - } - } + if (leftChild >= bvhLength) + { + uint transformIndex = leftChild - 0x80000000; + uint zIndex = bvhChildren[index].y >> 18; + uint elementIndex = bvhChildren[index].y - zIndex; + mat3x2 transform = elementTranform[transformIndex]; + vec2 localUV = + (mat3(vec3(transform[0], 0), vec3(transform[1], 0), vec3(transform[2], 1)) * vec3(uv, 1)).xy; + + vec3 elementColor; + vec2 elementMetallicRoughness; + if (drawElement(elementIndex, localUV, elementColor, elementMetallicRoughness, + debugBVH)) + { + color = vec4(elementColor, zIndex); + metallicRoughness = elementMetallicRoughness; + } + //if(elementIndex == 1 && transformIndex==1) + // color = vec4(1,1,0,1); + + index = bvhLength; + } + else + { + stack.push(index); + index = leftChild; + } + } + else + index = bvhLength; + } + if (!stack.empty()) + { + stack.getTop(index); + stack.pop(); + index = bvhChildren[index].y; + } + } imageStore(gBaseColor, pixelLocation, vec4(color.rgb, 1)); imageStore(gMetallicRoughness, pixelLocation, vec4(metallicRoughness, 0, 1)); - return; + //return; if (/*color.a!=-1&&*/ debugBVH == vec3(0)) { // imageStore(gBaseColor, pixelLocation, vec4(vec3(1, 1, 0),1)); diff --git a/ArchitectureColoredPainting/res/Shaders/painting.frag b/ArchitectureColoredPainting/res/Shaders/painting.frag index 5fed343..4f69ed9 100644 --- a/ArchitectureColoredPainting/res/Shaders/painting.frag +++ b/ArchitectureColoredPainting/res/Shaders/painting.frag @@ -36,11 +36,6 @@ void main() lod++; gMetallicRoughness = mt.rg; -// int pageSize = textureSize(texture_basecolor, levels-1).x; - -// uint pageId = 0; -// for(uint i = 0; i < lodExpect; i++) -// pageId += 1<<(2*(levels-1-i)); uint w = 1<<(levels-1-lodExpect); ivec2 page = ivec2(TexCoords * vec2(w)); page = clamp(page, ivec2(0), ivec2(w-1)); @@ -49,7 +44,6 @@ void main() gPaintingIndex = uvec2(0); else gPaintingIndex = uvec2((paintingId<<4)+lodExpect, (page.y<<8)+page.x); - gPaintingTexCoord = vec2(1., 1.) - TexCoords * 2; return; } \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidget.cpp b/ArchitectureColoredPainting/src/Editor/EditorWidget.cpp index 2c990de..97898fb 100644 --- a/ArchitectureColoredPainting/src/Editor/EditorWidget.cpp +++ b/ArchitectureColoredPainting/src/Editor/EditorWidget.cpp @@ -24,7 +24,8 @@ EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent) }); connect(this->openButton, &QPushButton::clicked, this, [this]() { QString fileName = QFileDialog::getOpenFileName(this->saveAsButton, QString::fromLocal8Bit(""), "", QString::fromLocal8Bit("JSONļ(*.json)")); - this->tabWidget->addTab(new EditorWidgetItem(fileName, this), fileName); + if(!fileName.isEmpty()) + this->tabWidget->addTab(new EditorWidgetItem(fileName, this), fileName); }); connect(this->closeButton, &QPushButton::clicked, this, [this]() { this->tabWidget->removeTab(this->tabWidget->currentIndex()); @@ -46,3 +47,11 @@ EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent) }); } +void EditorWidget::renameTab(QWidget* target, QString name) +{ + int index = this->tabWidget->indexOf(target); + if (index != -1) + { + this->tabWidget->setTabText(index, name); + } +} \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidget.h b/ArchitectureColoredPainting/src/Editor/EditorWidget.h index b0cfe2f..0986609 100644 --- a/ArchitectureColoredPainting/src/Editor/EditorWidget.h +++ b/ArchitectureColoredPainting/src/Editor/EditorWidget.h @@ -18,6 +18,6 @@ private: public: EditorWidget(QWidget* parent = nullptr); ~EditorWidget()=default; - + void renameTab(QWidget* target, QString name); }; diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/ColorPicker.cpp b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/ColorPicker.cpp new file mode 100644 index 0000000..7010ae4 --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/ColorPicker.cpp @@ -0,0 +1,38 @@ +#include "ColorPicker.h" +#include + +QString getStyleSheet(const QColor& color) +{ + return + "QPushButton#colorPicker {" + " border-style: solid;" + " border-width: 1px;" + " border-color: black;" + " background-color: " + color.name() + ";" + "}"; +} + +ColorPicker::ColorPicker(const QColor& color, QWidget* parent) : QPushButton(parent), color(color) +{ + this->setObjectName("colorPicker"); + this->setStyleSheet(getStyleSheet(color)); + connect(this, &QPushButton::clicked, this, &ColorPicker::onClicked); +} + +QColor ColorPicker::getColor() const +{ + return color; +} + +void ColorPicker::onClicked() +{ + QColorDialog dialog(this->color, this); + dialog.exec(); + QColor newColor = dialog.selectedColor(); + if (newColor.isValid() && this->color != newColor) + { + this->color = newColor; + this->setStyleSheet(getStyleSheet(newColor)); + emit colorChanged(newColor); + } +} \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/ColorPicker.h b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/ColorPicker.h new file mode 100644 index 0000000..a8edd67 --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/ColorPicker.h @@ -0,0 +1,16 @@ +#pragma once +#include +class ColorPicker : public QPushButton +{ + Q_OBJECT +private: + QColor color; +public: + ColorPicker(const QColor& color = QColor::fromRgb(0, 0, 0), QWidget* parent = nullptr); + QColor getColor() const; +public slots: + void onClicked(); +signals: + void colorChanged(QColor newColor); +}; + diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/FillStyleWidget.cpp b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/FillStyleWidget.cpp new file mode 100644 index 0000000..bf78dbc --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/FillStyleWidget.cpp @@ -0,0 +1,48 @@ +#include "FillStyleWidget.h" +#include "ColorPicker.h" +#include +#include +#include + +FillStyleWidget::FillStyleWidget(std::shared_ptr fill, QWidget* parent) +: QWidget(parent), fill(fill) +{ + auto* layout = new QGridLayout(this); + this->setLayout(layout); + + // ɫ + auto* material = &plainFill(fill)->material; + auto* colorLabel = new QLabel(QStringLiteral("ɫ"), this); + layout->addWidget(colorLabel, 0, 0); + auto* colorPicker = new ColorPicker(material->color); + connect(colorPicker, &ColorPicker::colorChanged, + [material](QColor color) + { + material->color = color; + }); + layout->addWidget(colorPicker, 0, 1); + + // + auto* metallicLabel = new QLabel(QStringLiteral(""), this); + layout->addWidget(metallicLabel, 1, 0); + auto* metallicInput = new QtMaterialTextField(this); + metallicInput->setText(QString::number(material->metallicF(), 'g', 3)); + connect(metallicInput, &QtMaterialTextField::textChanged, + [material](const QString& text) + { + material->setMetallicF(text.toFloat()); + }); + layout->addWidget(metallicInput, 1, 1); + + // ֲڶ + auto* roughnessLabel = new QLabel(QStringLiteral("ֲڶ"), this); + layout->addWidget(roughnessLabel, 2, 0); + auto* roughnessInput = new QtMaterialTextField(this); + roughnessInput->setText(QString::number(material->roughnessF(), 'g', 3)); + connect(roughnessInput, &QtMaterialTextField::textChanged, + [material](const QString& text) + { + material->setRoughnessF(text.toFloat()); + }); + layout->addWidget(roughnessInput, 2, 1); +} diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/FillStyleWidget.h b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/FillStyleWidget.h new file mode 100644 index 0000000..bff2724 --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/FillStyleWidget.h @@ -0,0 +1,12 @@ +#pragma once +#include "../Renderer/Painting/MaterialStyleFill.h" +#include "LayerStyle.h" +#include +class FillStyleWidget : public QWidget +{ + Q_OBJECT +public: + FillStyleWidget(std::shared_ptr fill, QWidget* parent = nullptr); + std::shared_ptr fill; +}; + diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerCreateWidget.cpp b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerCreateWidget.cpp index ed6743a..45d8dfe 100644 --- a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerCreateWidget.cpp +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerCreateWidget.cpp @@ -1,15 +1,46 @@ #include "LayerCreateWidget.h" #include -LayerCreateWidget::LayerCreateWidget(ElementManager* elementManager, QWidget* parent) : +LayerCreateWidget::LayerCreateWidget(ElementManager* elementManager, FolderLayerWrapper* folderLayer, QWidget* parent) : QDialog(parent) { + this->elementManager = elementManager; ui.setupUi(this); connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(ui.comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int))); elementPool = new ElementPoolWidget(ui.elementPool); - elementPool->setElementList(elementManager->elements); + elements = elementManager->elements; + elementsCheck(folderLayer); + elementPool->setElementList(elements); +} + +void LayerCreateWidget::elementsCheck(FolderLayerWrapper* parent) +{ + std::set upSet, downSet; + parent->collectUpReachable(upSet); + for (auto it = elements.begin(); it != elements.end();) + { + bool valid = true; + auto ele = dynamic_cast(*it); + if (ele != nullptr) + { + downSet.clear(); + ele->collectReachable(downSet); + for (auto& layer : downSet) + { + if (upSet.find(layer) != upSet.end()) + { + valid = false; + break; + } + } + } + if (valid) + ++it; + else + it = elements.erase(it); + } } LayerCreateWidget::~LayerCreateWidget() @@ -21,8 +52,20 @@ void LayerCreateWidget::accept() QJsonObject jsonObj; jsonObj.insert("name", ui.name->text()); if (ui.comboBox->currentIndex() == 0) { + auto currentEle = elements[elementPool->currentIndex]; + int index = -1; + for (int i = 0; i < elementManager->elements.size(); i++) + { + if (elementManager->elements[i] == currentEle) + { + index = i; + break; + } + } + if (index == -1) + return; jsonObj.insert("is-folder", false); - jsonObj.insert("element", elementPool->currentIndex); + jsonObj.insert("element", index); } else { jsonObj.insert("is-folder", true); diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerCreateWidget.h b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerCreateWidget.h index c9b7c89..38456b2 100644 --- a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerCreateWidget.h +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerCreateWidget.h @@ -11,12 +11,15 @@ class LayerCreateWidget : Q_OBJECT private: + ElementManager* elementManager; + std::vector elements; Ui::LayerCreateWidget ui; ElementPoolWidget* elementPool; + void elementsCheck(FolderLayerWrapper*); public: - LayerCreateWidget(ElementManager* elementManager,QWidget* parent = nullptr); + LayerCreateWidget(ElementManager* elementManager, FolderLayerWrapper* folderLayer, QWidget* parent = nullptr); ~LayerCreateWidget(); void accept() override; diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerStyleDialog.cpp b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerStyleDialog.cpp index 7a7db8d..33a3568 100644 --- a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerStyleDialog.cpp +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerStyleDialog.cpp @@ -3,22 +3,20 @@ #include #include #include -#include -#include LayerStyleDialog::LayerStyleDialog( - QWidget* parent, - std::shared_ptr existedStyle, - std::vector>* excludeStyles -) : QDialog(parent) + LayerStyleContainer& styles, + std::shared_ptr existedStyle, + QWidget* parent +) : QDialog(parent), styles(&styles) { - QVBoxLayout* dialogLayout = new QVBoxLayout(this); + auto* dialogLayout = new QVBoxLayout(this); dialogLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter); this->setLayout(dialogLayout); - if (existedStyle) + if (existedStyle) { - this->modifyingStyle = existedStyle->clonePtr(); + this->modifyingStyle = existedStyle->clone(); this->styleContainer = nullptr; this->styleWidget = modifyingStyle->getInputWidget(); @@ -28,28 +26,14 @@ LayerStyleDialog::LayerStyleDialog( } else { - std::unordered_set excludeStyleNames; - for(auto &style : *excludeStyles) - { - excludeStyleNames.insert(style->getStyleName()); - } + QStringList unusedStyleNames = styles.unusedStyleNames(); + auto* typeSelector = new QComboBox(this); - QComboBox* typeSelector = new QComboBox(this); - - for (auto& pair : LayerStyle::types) + if (!unusedStyleNames.empty()) { - if (!excludeStyleNames.contains(pair.first)) - { - typeSelector->addItem(pair.first); - if (!this->modifyingStyle) - { - this->modifyingStyle = std::move(pair.second()); - } - } - } + typeSelector->addItems(unusedStyleNames); + this->modifyingStyle = std::move(styles.makeUnusedStyle(unusedStyleNames[0])); - if (typeSelector->count() > 0) - { dialogLayout->addWidget(typeSelector); this->styleContainer = new QGridLayout(this); dialogLayout->addLayout(styleContainer); @@ -57,11 +41,11 @@ LayerStyleDialog::LayerStyleDialog( this->styleWidget = this->modifyingStyle->getInputWidget(); this->styleWidget->setParent(this); this->styleContainer->addWidget(styleWidget); - connect(typeSelector, QOverload::of(&QComboBox::currentIndexChanged), + connect(typeSelector, &QComboBox::currentTextChanged, this, &LayerStyleDialog::onStyleTypeSelectorChanged); } } - QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttonBox, &QDialogButtonBox::accepted, this, &LayerStyleDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &LayerStyleDialog::reject); dialogLayout->addWidget(buttonBox); @@ -73,7 +57,7 @@ void LayerStyleDialog::accept() QDialog::accept(); } -void LayerStyleDialog::onStyleTypeSelectorChanged(int index) +void LayerStyleDialog::onStyleTypeSelectorChanged(const QString& current) { if (this->styleWidget) { @@ -81,8 +65,10 @@ void LayerStyleDialog::onStyleTypeSelectorChanged(int index) this->styleWidget->setParent(nullptr); delete styleWidget; } - this->modifyingStyle = std::move(LayerStyle::types[index].second()); + this->modifyingStyle = std::move(styles->makeUnusedStyle(current)); this->styleWidget = this->modifyingStyle->getInputWidget(); this->styleWidget->setParent(this); this->styleContainer->addWidget(styleWidget, 0, 0, 1, 1); + this->styleWidget->adjustSize(); + this->adjustSize(); } diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerStyleDialog.h b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerStyleDialog.h index 8246cca..b703e80 100644 --- a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerStyleDialog.h +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerStyleDialog.h @@ -9,15 +9,17 @@ class LayerStyleDialog : public QDialog private: QWidget* styleWidget; QGridLayout* styleContainer; + LayerStyleContainer* styles; std::unique_ptr modifyingStyle; public: LayerStyleDialog( - QWidget* parent = nullptr, - std::shared_ptr existedStyle = nullptr, - std::vector>* excludeStyles = nullptr); + LayerStyleContainer& styles, + std::shared_ptr existedStyle = nullptr, + QWidget* parent = nullptr + ); std::shared_ptr layerStyle; private slots: - void onStyleTypeSelectorChanged(int index); + void onStyleTypeSelectorChanged(const QString& current); void accept() override; }; diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleListView.cpp b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleListView.cpp new file mode 100644 index 0000000..5b011a7 --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleListView.cpp @@ -0,0 +1,108 @@ +#include "StrokeStyleListView.h" +#include "ColorPicker.h" +#include + +constexpr int COLUMN_WIDTH = 0; +constexpr int COLUMN_COLOR = 1; +constexpr int COLUMN_METALLIC = 2; +constexpr int COLUMN_ROUGHNESS = 3; +constexpr int COLUMN_OPERATIONS = 4; + +// TODO: Ϊ̳QWidgettableתΪеһԪأаťͿ +StrokeStyleListView::StrokeStyleListView( + std::shared_ptr stroke, + QWidget* parent +) : QTableWidget(parent), stroke(stroke) +{ + this->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow); + this->setColumnCount(5); + this->setRowCount(stroke->materialMap.size()); + QStringList headers; + headers.append(QStringLiteral("ľռ")); + headers.append(QStringLiteral("ɫ")); + headers.append(QStringLiteral("")); + headers.append(QStringLiteral("ֲڶ")); + headers.append(QStringLiteral("")); + this->setHorizontalHeaderLabels(headers); + for (int row = 0; auto& strokePair : stroke->materialMap) + { + QTableWidgetItem* widthItem = new QTableWidgetItem; + widthItem->setData(Qt::EditRole, strokePair.first); + this->setItem(row, COLUMN_WIDTH, widthItem); + + QColor* colorPtr = &(strokePair.second.color); + QTableWidgetItem* colorItem = new QTableWidgetItem; + colorItem->setData(Qt::DisplayRole, *colorPtr); + this->setItem(row, COLUMN_COLOR, colorItem); + ColorPicker* colorPicker = new ColorPicker(*colorPtr, this); + this->setCellWidget(row, COLUMN_COLOR, colorPicker); + connect(colorPicker, &ColorPicker::colorChanged, [this, colorPtr](QColor color) { + *colorPtr = color; + this->update(); + }); + + QTableWidgetItem* metallicItem = new QTableWidgetItem; + metallicItem->setData(Qt::EditRole, strokePair.second.metallic); + this->setItem(row, COLUMN_METALLIC, metallicItem); + + QTableWidgetItem* roughnessItem = new QTableWidgetItem; + roughnessItem->setData(Qt::EditRole, strokePair.second.roughness); + this->setItem(row, COLUMN_ROUGHNESS, roughnessItem); + + QtMaterialRaisedButton* removeButton = new QtMaterialRaisedButton("-", this); + removeButton->setFixedSize(20, 20); + this->setCellWidget(row, COLUMN_OPERATIONS, removeButton); + connect(removeButton, &QtMaterialRaisedButton::clicked, [this, row]() { + this->stroke->materialMap.erase(this->item(row, COLUMN_WIDTH)->text().toFloat()); + this->removeRow(row); + }); + + row++; + } + connect(this, &StrokeStyleListView::currentItemChanged, this, &StrokeStyleListView::onCurrentItemChanged); + connect(this, &StrokeStyleListView::cellChanged, this, &StrokeStyleListView::onCellChanged); + this->adjustSize(); +} + +void StrokeStyleListView::onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous) +{ + if (!current) return; + int column = current->column(); + if (column != COLUMN_COLOR && column != COLUMN_OPERATIONS) + { + this->currentItemValue = current->text(); + } +} + +void StrokeStyleListView::onCellChanged(int row, int column) +{ + auto changedItem = this->item(row, column); + auto changedItemValue = changedItem->text().toFloat(); + if (changedItemValue < 0 || 1 < changedItemValue) + { + changedItem->setData(Qt::EditRole, this->currentItemValue.toFloat()); + return; + } + auto changedWidth = this->item(row, COLUMN_WIDTH)->text().toFloat(); + switch (row) + { + case COLUMN_WIDTH: + { + float oldWidth = this->currentItemValue.toFloat(); + auto node = stroke->materialMap.extract(oldWidth); + node.key() = changedWidth; + stroke->materialMap.insert(std::move(node)); + break; + } + case COLUMN_METALLIC: + { + stroke->materialMap[changedWidth].metallic = changedItemValue; + break; + } + case COLUMN_ROUGHNESS: + { + stroke->materialMap[changedWidth].roughness = changedItemValue; + break; + } + } +} \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleListView.h b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleListView.h new file mode 100644 index 0000000..f12e400 --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleListView.h @@ -0,0 +1,21 @@ +#pragma once +#include "LayerStyle.h" +#include "../Renderer/Painting/MaterialStyleStroke.h" +#include +#include +#include +class StrokeStyleListView : public QTableWidget +{ + Q_OBJECT +private: + QString currentItemValue; + +public: + StrokeStyleListView(std::shared_ptr stroke, QWidget* parent = nullptr); + std::shared_ptr stroke; + +protected slots: + void onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous); + void onCellChanged(int row, int column); +}; + diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleWidget.cpp b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleWidget.cpp new file mode 100644 index 0000000..e2c9ce7 --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleWidget.cpp @@ -0,0 +1,218 @@ +#include "StrokeStyleWidget.h" +#include "ColorPicker.h" +#include +#include +#include + +constexpr int COLUMN_WIDTH = 0; +constexpr int COLUMN_COLOR = 1; +constexpr int COLUMN_METALLIC = 2; +constexpr int COLUMN_ROUGHNESS = 3; +constexpr int COLUMN_OPERATIONS = 4; + +StrokeStyleWidget::StrokeStyleWidget( + std::shared_ptr stroke, + QWidget* parent +) : QWidget(parent), stroke(stroke) +{ + auto* viewLayout = new QVBoxLayout(this); + this->setLayout(viewLayout); + + initStrokeSettings(); + auto* strokeProperties = new QWidget(this); + auto* strokePropertiesLayout = new QHBoxLayout(strokeProperties); + strokePropertiesLayout->setMargin(0); + + strokeProperties->setLayout(strokePropertiesLayout); + strokePropertiesLayout->addWidget(enableGradual); + strokePropertiesLayout->addWidget(endTypeBox); + + viewLayout->addWidget(strokeProperties); + viewLayout->addWidget(widthField); + + initTable(std::dynamic_pointer_cast(stroke->materialStroke)); + viewLayout->addWidget(strokeTable); + this->adjustSize(); +} + +void StrokeStyleWidget::initStrokeSettings() +{ + this->enableGradual = new QtMaterialCheckBox(this); + enableGradual->setText(QStringLiteral("")); + enableGradual->setChecked(radialStroke(stroke)->gradual); + connect(enableGradual, &QtMaterialCheckBox::toggled, [this](bool checked) { + radialStroke(this->stroke)->gradual = checked; + }); + +#define endTypeBoxLabel(start, end) QStringLiteral(start##" -> "##end) + this->endTypeBox = new QComboBox(this); + endTypeBox->addItem(endTypeBoxLabel("Բͷ", "Բͷ")); // kRound + endTypeBox->addItem(endTypeBoxLabel("ƽͷ", "Բͷ")); // kFlatRound + endTypeBox->addItem(endTypeBoxLabel("Բͷ", "ƽͷ")); // kRoundFlat + endTypeBox->addItem(endTypeBoxLabel("ƽͷ", "ƽͷ")); // kFlat + endTypeBox->setCurrentIndex(static_cast(this->stroke->endType)); + connect(endTypeBox, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { + switch (index) + { + case 0: + this->stroke->endType = Renderer::StrokeEndType::kRound; + break; + case 1: + this->stroke->endType = Renderer::StrokeEndType::kFlatRound; + break; + case 2: + this->stroke->endType = Renderer::StrokeEndType::kRoundFlat; + break; + case 3: + this->stroke->endType = Renderer::StrokeEndType::kFlat; + break; + } + }); + + this->widthField = new QtMaterialTextField(this); + widthField->setLabel(QStringLiteral("߿")); + widthField->setText(QString::number(stroke->halfWidth)); + auto* widthValidator = new QDoubleValidator(0.1, std::numeric_limits::max(), 3, widthField); + widthValidator->setNotation(QDoubleValidator::StandardNotation); + widthField->setValidator(widthValidator); + connect(widthField, &QtMaterialTextField::textChanged, [this](const QString& changed) { + if (this->widthField->hasAcceptableInput()) + { + this->stroke->halfWidth = changed.toFloat(); + } + }); +} + +// TODO: ʱУ +void StrokeStyleWidget::initTable(std::shared_ptr materialStroke) +{ + this->strokeTable = new QTableWidget(this); + strokeTable->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow); + strokeTable->setColumnCount(5); + strokeTable->setRowCount(materialStroke->materialMap.size() + 1); + QStringList headers; + headers << QStringLiteral("ľռ") + << QStringLiteral("ɫ") + << QStringLiteral("") + << QStringLiteral("ֲڶ") + << QStringLiteral(""); + strokeTable->setHorizontalHeaderLabels(headers); + int row = 0; + // + for (auto & strokePair : materialStroke->materialMap) + { + setTableRow(row, strokePair.first, strokePair.second); + row++; + } + // ť + QtMaterialRaisedButton* addButton = new QtMaterialRaisedButton("+", strokeTable); + addButton->setBackgroundColor(QtMaterialStyle::instance().themeColor("primary1")); + strokeTable->setSpan(row, 0, 1, 5); + strokeTable->setCellWidget(row, 0, addButton); + strokeTable->setMinimumHeight(strokeTable->rowHeight(row) * 5); + strokeTable->setMinimumWidth(strokeTable->sizeHint().width()); + addButton->setFixedHeight(strokeTable->rowHeight(row)); + connect(addButton, &QtMaterialRaisedButton::clicked, [this]() { + handlingRowInsert = true; + auto materialMap = &(radialStroke(this->stroke)->materialMap); + float newWidth = 0; + if (materialMap->size() == 0) + { + newWidth = 0.1; + } + else + { + auto lastPair = materialMap->rbegin(); + newWidth = lastPair->first + 0.01; + } + Renderer::Material newMaterial(QColor::fromRgb(0, 0, 0)); + (*materialMap)[newWidth] = newMaterial; + int newRow = this->strokeTable->rowCount() - 1; + this->strokeTable->insertRow(newRow); + setTableRow(newRow, newWidth, (*materialMap)[newWidth]); + this->strokeTable->update(); + handlingRowInsert = false; + }); + connect(strokeTable, &QTableWidget::currentItemChanged, this, &StrokeStyleWidget::onCurrentItemChanged); + connect(strokeTable, &QTableWidget::cellChanged, this, &StrokeStyleWidget::onCellChanged); +} + +void StrokeStyleWidget::setTableRow(int row, float width, Renderer::Material& material) +{ + auto* widthItem = new QTableWidgetItem; + widthItem->setData(Qt::EditRole, width); + strokeTable->setItem(row, COLUMN_WIDTH, widthItem); + + QColor* colorPtr = &(material.color); + auto* colorItem = new QTableWidgetItem; + colorItem->setData(Qt::DisplayRole, *colorPtr); + strokeTable->setItem(row, COLUMN_COLOR, colorItem); + auto* colorPicker = new ColorPicker(*colorPtr, strokeTable); + strokeTable->setCellWidget(row, COLUMN_COLOR, colorPicker); + connect(colorPicker, &ColorPicker::colorChanged, [this, colorPtr](QColor color) { + *colorPtr = color; + this->strokeTable->update(); + }); + + auto* metallicItem = new QTableWidgetItem; + metallicItem->setData(Qt::EditRole, material.metallicF()); + strokeTable->setItem(row, COLUMN_METALLIC, metallicItem); + + auto* roughnessItem = new QTableWidgetItem; + roughnessItem->setData(Qt::EditRole, material.roughnessF()); + strokeTable->setItem(row, COLUMN_ROUGHNESS, roughnessItem); + + auto* removeButton = new QtMaterialRaisedButton("-", strokeTable); + removeButton->setBackgroundColor(QtMaterialStyle::instance().themeColor("primary1")); + removeButton->setFixedSize(20, 20); + strokeTable->setCellWidget(row, COLUMN_OPERATIONS, removeButton); + connect(removeButton, &QtMaterialRaisedButton::clicked, [this, row]() { + radialStroke(this->stroke)->materialMap.erase(this->strokeTable->item(row, COLUMN_WIDTH)->text().toFloat()); + this->strokeTable->removeRow(row); + }); +} + +void StrokeStyleWidget::onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous) +{ + if (!current) return; + int column = current->column(); + if (column != COLUMN_COLOR && column != COLUMN_OPERATIONS) + { + this->currentItemValue = current->data(Qt::EditRole); + } +} + +void StrokeStyleWidget::onCellChanged(int row, int column) +{ + if (handlingRowInsert) return; + auto changedItem = strokeTable->item(row, column); + auto changedItemValue = changedItem->text().toFloat(); + if (changedItemValue < 0 || 1 < changedItemValue) + { + changedItem->setData(Qt::EditRole, this->currentItemValue.toFloat()); + return; + } + auto changedWidth = strokeTable->item(row, COLUMN_WIDTH)->data(Qt::EditRole).toFloat(); + switch (column) + { + case COLUMN_WIDTH: + { + float oldWidth = this->currentItemValue.toFloat(); + auto node = radialStroke(stroke)->materialMap.extract(oldWidth); + node.key() = changedWidth; + radialStroke(stroke)->materialMap.insert(std::move(node)); + strokeTable->sortItems(COLUMN_WIDTH); + break; + } + case COLUMN_METALLIC: + { + radialStroke(stroke)->materialMap[changedWidth].setMetallicF(changedItemValue); + break; + } + case COLUMN_ROUGHNESS: + { + radialStroke(stroke)->materialMap[changedWidth].setRoughnessF(changedItemValue); + break; + } + } +} \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleWidget.h b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleWidget.h new file mode 100644 index 0000000..6d5dbdd --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleWidget.h @@ -0,0 +1,33 @@ +#pragma once +#include "LayerStyle.h" +#include "../Renderer/Painting/MaterialStyleStroke.h" +#include +#include +#include +#include +#include +class StrokeStyleWidget : public QWidget +{ + Q_OBJECT +private: + QVariant currentItemValue; + + QtMaterialCheckBox* enableGradual; + QComboBox* endTypeBox; + QtMaterialTextField* widthField; + QTableWidget* strokeTable; + bool handlingRowInsert = false; + + void initStrokeSettings(); + void initTable(std::shared_ptr materialStroke); + void setTableRow(int row, float width, Renderer::Material& material); + +public: + StrokeStyleWidget(std::shared_ptr stroke, QWidget* parent = nullptr); + std::shared_ptr stroke; + +protected slots: + void onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous); + void onCellChanged(int row, int column); +}; + diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.cpp b/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.cpp index 0a3f01b..eb5f228 100644 --- a/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.cpp +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.cpp @@ -1,8 +1,11 @@ #include "EditorWidgetItem.h" +#include "EditorWidget.h" +#include EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(parent) { QImage x; + this->parent = parent; displayLayer = nullptr; displayElement = nullptr; ui.setupUi(this); @@ -12,8 +15,22 @@ EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(p this->filePath = filePath; layerInfoDisplayWidget = dynamic_cast(tabWidget->widget(0)); elementInfoDisplayWidget = dynamic_cast(tabWidget->widget(1)); + editorSettingWidget = dynamic_cast(tabWidget->widget(2)); + elementInfoDisplayWidget->enableEdit(); qDebug() << layerInfoDisplayWidget; qDebug() << elementInfoDisplayWidget; + auto centralRefresh = [this]() { + layerInfoDisplayWidget->refresh(); + elementInfoDisplayWidget->refresh(); + treeWidget->refresh(); + previewWindow->refresh(); + }; + connect(layerInfoDisplayWidget, &InfoDisplayWidget::triggerCentralRefresh, centralRefresh); + connect(elementInfoDisplayWidget, &ElementPoolWidget::triggerCentralRefresh, centralRefresh); + connect(treeWidget, &LayerTreeWidget::triggerCentralRefresh, centralRefresh); + connect(editorSettingWidget, &EditorSettingWidget::backgroundColorChanged, this, &EditorWidgetItem::handleBackgroundColorChange); + connect(editorSettingWidget, &EditorSettingWidget::projectNameChanged, this, &EditorWidgetItem::handleProjectNameChange); + connect(previewWindow, &PreviewWindow::refreshElementPreviewByIndex, elementInfoDisplayWidget, &ElementPoolWidget::refreshPictureByIndex); connect(previewWindow, &PreviewWindow::layerInfoChanged, layerInfoDisplayWidget, &InfoDisplayWidget::triggerSelfRefresh); connect(treeWidget, &LayerTreeWidget::displayLayerChange, previewWindow, &PreviewWindow::currentLayerChanged); connect(treeWidget, &LayerTreeWidget::requireRefreshElementWidget, elementInfoDisplayWidget, &ElementPoolWidget::refresh); @@ -24,6 +41,7 @@ EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(p connect(treeWidget, &LayerTreeWidget::requireRefreshPreview, this, &EditorWidgetItem::triggerRefreshPreview); connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireSelfRefresh, layerInfoDisplayWidget, &InfoDisplayWidget::triggerSelfRefresh); + connect(elementInfoDisplayWidget, &ElementPoolWidget::refreshLayerTree, treeWidget, &LayerTreeWidget::refresh); // &EditorWidget::triggerRefreshPreview); // test QFile settingFile; @@ -37,11 +55,11 @@ EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(p qDebug() << jError.errorString(); // end test QJsonObject source = jsonDoc.object(); - elementManager = new ElementManager(source,previewWindow->getRenderer()); + elementManager = new ElementManager(source,Renderer::ElementRenderer::instance()); layerManager = new LayerManager(source, elementManager); elementInfoDisplayWidget->setElementManager(elementManager); treeWidget->elementManager = elementManager; - qDebug() << layerManager->toJson(); + //qDebug() << layerManager->toJson(); previewWindow->initialize(layerManager,QSize(jsonDoc.object().value("width").toDouble(),jsonDoc.object().value("height").toDouble())); @@ -51,6 +69,15 @@ EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(p treeWidget->refresh(); treeWidget->addTopLevelItem(treeWidget->root->getQTreeItem()); } + this->backgroundColor = source.value("background-color").toVariant().value(); + this->projectName = source.value("project-name").toString(); + qDebug() << this->backgroundColor; + qDebug() << this->projectName; + QTimer::singleShot(300, this, [this]() { + handleBackgroundColorChange(this->backgroundColor); + handleProjectNameChange(this->projectName); + }); + } EditorWidgetItem::~EditorWidgetItem() @@ -98,9 +125,28 @@ void EditorWidgetItem::saveImpl(QString filePath) const json.insert("height", 1080); json.insert("root-layer", source1.value("root-layer")); json.insert("elements", source2.value("elements")); + json.insert("project-name", this->projectName); + json.insert("background-color", QJsonValue::fromVariant(QVariant::fromValue(this->backgroundColor))); QJsonDocument doc(json); QFile file(filePath); file.open(QIODevice::WriteOnly); file.write(doc.toJson()); file.close(); +} + +void EditorWidgetItem::handleBackgroundColorChange(QColor color) +{ + this->backgroundColor = color; + previewWindow->setBackgroundColor(color); +} + +void EditorWidgetItem::handleProjectNameChange(QString name) +{ + this->projectName = name; + auto parent = dynamic_cast(this->parent); + qDebug() << name << " " << parent<<" "<renameTab(this, name); + } } \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.h b/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.h index 9fc5be8..409374a 100644 --- a/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.h +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.h @@ -6,6 +6,7 @@ #include "LayerManager.h" #include "LayerTreeWidget.h" #include "PreviewWindow.h" +#include "EditorSettingWidget.h" #include "ui_EditorWidgetItem.h" #include #include @@ -26,11 +27,17 @@ class EditorWidgetItem : public QWidget QTabWidget *tabWidget; InfoDisplayWidget* layerInfoDisplayWidget; ElementPoolWidget* elementInfoDisplayWidget; + EditorSettingWidget* editorSettingWidget; // QT DATA PART LayerWrapper *displayLayer; GraphicElement *displayElement; - QString filePath; + QWidget* parent; void saveImpl(QString filePath)const; +public: + // PROJECT INFO + QString filePath; + QString projectName; + QColor backgroundColor; public: EditorWidgetItem(QString filePath, QWidget *parent = nullptr); @@ -38,6 +45,8 @@ class EditorWidgetItem : public QWidget void paintEvent(QPaintEvent *event) override; void save() const; void saveAs(QString filePath)const; + void handleBackgroundColorChange(QColor color); + void handleProjectNameChange(QString name); private slots: void onLayerChange(LayerWrapper *layer); diff --git a/ArchitectureColoredPainting/src/Editor/ElementManager.cpp b/ArchitectureColoredPainting/src/Editor/ElementManager.cpp index daed263..b711794 100644 --- a/ArchitectureColoredPainting/src/Editor/ElementManager.cpp +++ b/ArchitectureColoredPainting/src/Editor/ElementManager.cpp @@ -2,7 +2,6 @@ ElementManager::ElementManager(QJsonObject source,Renderer::ElementRenderer* renderer) { auto elementsJson = source.value("elements").toArray(); - qDebug() << elementsJson.size(); int index = 0; for (auto elementJson : elementsJson) { @@ -68,9 +67,6 @@ int ElementManager::getLayerReferencedBy(const FolderLayerWrapper* layer) { for (int i = 0; i < elements.size(); i++) if (typeid(*elements[i]) == typeid(GroupElement)) { - qDebug() << ((GroupElement*)elements[i])->sourceLayer; - qDebug() << layer; - qDebug() << "------------"; if (((GroupElement*)elements[i])->sourceLayer == layer) return i; } diff --git a/ArchitectureColoredPainting/src/Editor/ElementPoolWidget.cpp b/ArchitectureColoredPainting/src/Editor/ElementPoolWidget.cpp index 64fadfd..74453d0 100644 --- a/ArchitectureColoredPainting/src/Editor/ElementPoolWidget.cpp +++ b/ArchitectureColoredPainting/src/Editor/ElementPoolWidget.cpp @@ -1,11 +1,16 @@ #include "ElementPoolWidget.h" +#include +#include +#include +#include ElementPoolWidget::ElementPoolWidget(QWidget* parent) : QWidget(parent) { elementManager = nullptr; iconWidth = 120, iconHeight = 90; - pictureList = new QListWidget(); + pictureList = new QListWidget(this); + pictureList->setContextMenuPolicy(Qt::CustomContextMenu); pictureList->setIconSize(QSize(iconWidth, iconHeight)); pictureList->setWindowFlags(Qt::FramelessWindowHint); pictureList->setResizeMode(QListWidget::Adjust); @@ -32,23 +37,8 @@ void ElementPoolWidget::setElementList(std::vector elements) { pictureList->clear(); this->elements = elements; for (int index = 0; index < elements.size(); index++) { - // - //QString strPath = QString("C:\\Users\\86177\\Pictures\\Screenshots\\test.png"); - //QPixmap itemPixmap(strPath); - //QPixmap itemPixmap(QSize(200, 200)); - //itemPixmap.fill(Qt::red); - QPixmap itemPixmap = elements[index]->getPaintObject().getDetail(); - qDebug() << this->parentWidget()->size(); - //auto p = new QWidget(); - //auto lb = new QLabel(p); - //lb->setPixmap(itemPixmap); - //lb->setFixedSize(1920, 1080); - //p->setFixedSize(1920, 1080); - //lb->show(); - //p->show(); - QListWidgetItem* pItem = new QListWidgetItem( - itemPixmap.scaled(QSize(iconWidth - 25, iconHeight - 25)), + elements[index]->getPreview(QSize(iconWidth - 25, iconHeight - 25)), elements[index]->name); pItem->setSizeHint(QSize(iconWidth, iconHeight)); pictureList->insertItem(index, pItem); @@ -78,4 +68,69 @@ void ElementPoolWidget::setElementManager(ElementManager* element) void ElementPoolWidget::refresh() { this->setElementList(this->elementManager->elements); // update(); +} + +void ElementPoolWidget::refreshPicture(GraphicElement* element) { + for (int i = 0; i < elements.size(); i++) { + if (element == elements[i]) { + pictureList->item(i)->setIcon(elements[i]->getPreview(QSize(iconWidth - 25, iconHeight - 25))); + // update(); + return; + } + } +} + +void ElementPoolWidget::refreshPictureByIndex(int index) { + if (index >= 0 && index < elements.size()) + { + pictureList->item(index)->setIcon(elements[index]->getPreview(QSize(iconWidth - 25, iconHeight - 25))); + // update(); + } +} + +void ElementPoolWidget::enableEdit() +{ + connect(this->pictureList, &QListWidget::customContextMenuRequested, this, &ElementPoolWidget::popMenu); +} + +void ElementPoolWidget::popMenu(const QPoint& pos) +{ + QListWidgetItem* item = pictureList->itemAt(pos); + int currentIndex = -1; + if (item != nullptr) + { + currentIndex = pictureList->row(item); + } + QMenu* menu = new QMenu(this); + if (currentIndex >= 0 && currentIndex < elementManager->elements.size()) + { + auto currentElement = this->elementManager->elements[currentIndex]; + menu->addAction(QString::fromLocal8Bit(""), this, [this, currentElement]() { + bool bOk = false; + QString sName = + QInputDialog::getText(this, QString::fromLocal8Bit(""), QString::fromLocal8Bit(":"), QLineEdit::Normal, currentElement->name, &bOk); + if (bOk && !sName.isEmpty()) + { + currentElement->name = sName; + emit triggerCentralRefresh(); + } + }); + menu->addAction(QString::fromLocal8Bit("ɾ"), this, [this, currentElement]() { + this->elementManager->removeElement(currentElement); + emit triggerCentralRefresh(); + }); + menu->actions().last()->setDisabled(currentElement->referencedCount > 0); + } + else + { + menu->addAction(QString::fromLocal8Bit("Ԫأsvg룩"), this, [this]() { + QString filePath = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("ļ"), "", "SVG Files (*.svg)"); + QFileInfo fileInfo(filePath); + QString fileName = fileInfo.fileName(); + qDebug() << fileName << " " << filePath; + this->elementManager->createSimpleElement(fileName, filePath); + emit triggerCentralRefresh(); + }); + } + menu->popup(mapToGlobal(pos)); } \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/ElementPoolWidget.h b/ArchitectureColoredPainting/src/Editor/ElementPoolWidget.h index 2a880a7..472b1ba 100644 --- a/ArchitectureColoredPainting/src/Editor/ElementPoolWidget.h +++ b/ArchitectureColoredPainting/src/Editor/ElementPoolWidget.h @@ -21,12 +21,18 @@ public: void setElementList(std::vector elementList); void setElementManager(ElementManager* element); ~ElementPoolWidget(); + void enableEdit(); signals: void elementSelected(GraphicElement* element); + void refreshLayerTree(); + void triggerCentralRefresh(); public slots: int pictureItemClicked(QListWidgetItem* item); void refresh(); + void refreshPicture(GraphicElement* element); + void refreshPictureByIndex(int index); + void popMenu(const QPoint& pos); }; diff --git a/ArchitectureColoredPainting/src/Editor/GraphicElement.cpp b/ArchitectureColoredPainting/src/Editor/GraphicElement.cpp index 403ae3f..4cc2ef3 100644 --- a/ArchitectureColoredPainting/src/Editor/GraphicElement.cpp +++ b/ArchitectureColoredPainting/src/Editor/GraphicElement.cpp @@ -1,11 +1,14 @@ #include "GraphicElement.h" #include "util/SvgFileLoader.h" +#include +#include using namespace std; PixelPath SimpleElement::getPaintObject() const { PixelPath result; result.addPath(painterPath); + //qDebug() << result.getPainterPath(); return result; } @@ -14,14 +17,19 @@ void SimpleElement::loadSvgFile(const QString& filePath) // TODO ʽ SvgFileLoader loader; loader.loadSvgFile(filePath, painterPath); - qDebug() << "load svg file success " << painterPath.elementCount(); + auto startPoint = static_cast(painterPath.elementAt(0)); + auto endPoint = static_cast(painterPath.elementAt(painterPath.elementCount() - 1)); + this->closed = startPoint == endPoint; + qDebug() << "load svg file success " << painterPath.elementCount() << (isClosed() ? "is" : "not") << "closed"; } SimpleElement::SimpleElement(QJsonObject jsonSource) : jsonSource(jsonSource) { painterPath.clear(); + filePath = jsonSource["data"].toObject()["include"].toString(); //loadSvgFile("D:\\Projects\\BigC\\svg\\3.svg"); - loadSvgFile("../"/*TODO: ijjsonļļ·*/ + jsonSource.value("data").toObject().value("include").toString()); + QFileInfo info(filePath); + loadSvgFile(filePath); } GroupElement::GroupElement(FolderLayerWrapper* sourceLayer) @@ -43,24 +51,11 @@ PixelPath GroupElement::getPaintObject() const } //TODO: apply styles and send back -PixelPath SimpleElement::getPaintObject(std::vector>* styles) const { - PixelPath result; - Renderer::ElementStyleStrokeDemo demo(2); - auto [img, mov] = renderer->drawElement(painterPath, demo, 1.0); - //qDebug() << mov; - //qDebug() << img << " ------"; - result.addImage(img, mov); - //result.addPath(painterPath); - // QImage img(80,80,QImage::Format_ARGB32); - // QPainter pt(&img); - //pt.setPen(QPen(Qt::red, 2)); - //pt.drawLine(0, 0, 80, 80); - //pt.end(); - //result.addImage(img, QPoint(0, 0)); - return result; +PixelPath SimpleElement::getPaintObject(const LayerStyleContainer& styles) const { + return this->getPaintObject(); } -PixelPath GroupElement::getPaintObject(std::vector>* styles) const { +PixelPath GroupElement::getPaintObject(const LayerStyleContainer& styles) const { return getPaintObject(); } @@ -78,9 +73,100 @@ PixelPath GroupElement::getPaintObject(std::vector>* // } //} //TODO : ϸ -QJsonObject GraphicElement::toJson() const +QJsonObject SimpleElement::toJson() const { QJsonObject result; - result.insert("name", name); + QJsonObject data; + data["include"] = filePath; + result["type"] = "svg-file"; + result["data"] = data; + result["name"] = name; return result; +} + +QJsonObject GroupElement::toJson() const +{ + QJsonObject result; + QJsonObject data; + data["reference-layer"] = "0.0"; + result["type"] = "group"; + result["data"] = data; + result["name"] = name; + return result; +} + +void SimpleElement::paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) +{ + painter->save(); + painter->setTransform(transform); + if (styles.empty()) + { + painter->drawPath(painterPath); + } + else + { + double angle = atan(transform.m12() / transform.m11()); + double maxScale; + if (fabs(cos(angle))>1e-5) + maxScale = std::max(fabs(transform.m11() / cos(angle)), fabs(transform.m22() / cos(angle))); + else + maxScale = std::max(fabs(transform.m12() / sin(angle)), fabs(transform.m21() / sin(angle))); + double pixelRatio = maxScale * QGuiApplication::primaryScreen()->devicePixelRatio(); + auto [img, mov] = Renderer::ElementRenderer::instance()->drawElement(painterPath, styles, pixelRatio); + transform.translate(mov.x(), mov.y()); + painter->setTransform(transform.scale(1 / pixelRatio, 1 / pixelRatio)); + painter->drawImage(0, 0, img); + } + painter->restore(); +} + +bool SimpleElement::isClosed() const +{ + return closed; +} + +void GroupElement::paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) +{ + sourceLayer->paint(painter, transform); +} + +bool GroupElement::isClosed() const +{ + return false; +} + + +QPixmap SimpleElement::getPreview(QSize size) +{ + QPixmap result(size + QSize(5, 5)); + QPainter painter(&result); + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.scale(size.width() / painterPath.boundingRect().width(), size.height() / painterPath.boundingRect().height()); + painter.drawPath(painterPath); + return result; +} + +QPixmap GroupElement::getPreview(QSize size) +{ + auto cache = sourceLayer->getCache(); + QPixmap result(QSize(1024, 1024)); + QPainter painter(&result); + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + sourceLayer->paint(&painter, QTransform(), true); + painter.end(); + QRect rect(cache.getBoundingRect().toRect()); + rect.setTopLeft(rect.topLeft() - QPoint(5, 5)); + rect.setBottomRight(rect.bottomRight() + QPoint(5, 5)); + result = result.copy(rect); + return result.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); +} + +void GroupElement::collectReachable(std::set& set) const +{ + if (sourceLayer != nullptr) + { + sourceLayer->collectDownReachable(set); + } } \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/GraphicElement.h b/ArchitectureColoredPainting/src/Editor/GraphicElement.h index c0b3c09..8ce9bf2 100644 --- a/ArchitectureColoredPainting/src/Editor/GraphicElement.h +++ b/ArchitectureColoredPainting/src/Editor/GraphicElement.h @@ -2,7 +2,6 @@ #include "LayerWrapper.h" #include "LayerStyle.h" -#include #include #include #include @@ -21,29 +20,40 @@ class ComposedPainterPath; class GraphicElement { public: + size_t referencedCount = 0; Renderer::ElementRenderer *renderer; QString name = ""; int index; // TODO: ΪBitmapPath - virtual QJsonObject toJson() const; + virtual QJsonObject toJson() const = 0; virtual PixelPath getPaintObject() const = 0; - virtual PixelPath getPaintObject(std::vector>*) const = 0; + virtual PixelPath getPaintObject(const LayerStyleContainer& styles) const = 0; + virtual void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) = 0; + virtual bool isClosed() const = 0; + virtual QPixmap getPreview(QSize size) = 0; }; class SimpleElement : public GraphicElement { private: + bool closed; +public: QJsonObject jsonSource; // TODO: ΪComposedPainterPath QPainterPath painterPath; + QString filePath; void loadSvgFile(const QString& filePath); public: + QJsonObject toJson() const override; SimpleElement(QJsonObject jsonSource); SimpleElement(QString filePath); ~SimpleElement() = default; PixelPath getPaintObject() const override; - PixelPath getPaintObject(std::vector>*) const override; + PixelPath getPaintObject(const LayerStyleContainer& styles) const override; + void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) override; + bool isClosed() const override; + QPixmap getPreview(QSize size) override; }; class GroupElement : public GraphicElement @@ -52,12 +62,17 @@ public: FolderLayerWrapper* sourceLayer; public: + QJsonObject toJson() const override; GroupElement() = default; GroupElement(FolderLayerWrapper* mSourceLayer); ~GroupElement() = default; PixelPath getPaintObject() const override; - PixelPath getPaintObject(std::vector>*) const override; + PixelPath getPaintObject(const LayerStyleContainer& styles) const override; void setSourceLayer(FolderLayerWrapper* sourceLayer); + void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) override; + bool isClosed() const override; + QPixmap getPreview(QSize size) override; + void collectReachable(std::set& set) const; }; //******************************** BitmapPath ********************************// diff --git a/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp b/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp index 912f653..2e13f68 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp +++ b/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp @@ -1,60 +1,298 @@ #include "LayerStyle.h" +#include "./EditorWidgetComponent/StrokeStyleWidget.h" +#include "./EditorWidgetComponent/FillStyleWidget.h" +#include "./util/EncodeUtil.hpp" +#include #include -#include #include #include -#include - -const std::vector()>>> LayerStyle::types = { - { - QStringLiteral(""), - []() { return std::make_unique(); } - }, - { - QStringLiteral(""), - []() { return std::make_unique(); } - } -}; +#include +#include +#define _USE_JOIN_VIEW_INPUT_RANGE +#include +#include std::vector StrokeElementLayerStyle::toBaseStyles() const { - return std::vector(); + std::vector baseStyles; + if (enableEachSideIndependent) + { + if (!radialStroke(strokePair.first)->materialMap.empty()) + { + baseStyles.push_back({ std::make_shared(), strokePair.first }); + } + if (!radialStroke(strokePair.second)->materialMap.empty()) + { + baseStyles.push_back({ std::make_shared(), strokePair.second }); + } + } + else if (!radialStroke(strokePair.first)->materialMap.empty()) + { + const auto material = std::shared_ptr(std::move(strokePair.first->clone())); + std::static_pointer_cast(material)->strokeType = Renderer::StrokeType::kBothSides; + + baseStyles.push_back({ std::make_shared(), material }); + } + return baseStyles; } -QString StrokeElementLayerStyle::getStyleName() const +QWidget* StrokeElementLayerStyle::getInputWidget() { - return QStringLiteral(""); -} + auto* w = new QWidget; + auto* materialList = new QListView; -QWidget* StrokeElementLayerStyle::getInputWidget() const -{ - // TODO - QLabel* le = new QLabel; - le->setText(QStringLiteral("")); - return le; + auto* layout = new QVBoxLayout(w); + layout->setMargin(0); + + auto* leftStrokeView = new StrokeStyleWidget(this->strokePair.first, w); + layout->addWidget(leftStrokeView); + + auto* checkEachSideIndependent = new QtMaterialCheckBox(w); + checkEachSideIndependent->setText(QStringLiteral("Ҳ")); + checkEachSideIndependent->setChecked(enableEachSideIndependent); + layout->addWidget(checkEachSideIndependent); + + auto* rightStrokeView = new StrokeStyleWidget(this->strokePair.second, w); + layout->addWidget(rightStrokeView); + rightStrokeView->setDisabled(!this->enableEachSideIndependent); + + QObject::connect(checkEachSideIndependent, &QtMaterialCheckBox::toggled, [this, rightStrokeView](bool toggled) { + this->enableEachSideIndependent = toggled; + rightStrokeView->setDisabled(!toggled); + }); + return w; } QWidget* StrokeElementLayerStyle::getListDisplayWidget() const { - QWidget* w = new QWidget; - QLabel* name = new QLabel(w); + auto* w = new QWidget; + auto* name = new QLabel(w); name->setText(QStringLiteral("")); - QHBoxLayout* layout = new QHBoxLayout(w); + auto* layout = new QHBoxLayout(w); layout->setMargin(0); layout->addWidget(name); return w; } -StrokeElementLayerStyle::StrokeElementLayerStyle(const StrokeElementLayerStyle& other) +void LayerStyleContainer::computeNewHash() { - materialStyles = std::vector>(other.materialStyles.size()); - for (size_t i = 0; i < other.materialStyles.size(); i++) + hash = 0; + for (auto& f : styles + | std::views::values + | std::views::transform(&LayerStyle::toBaseStyles) + | std::views::join + | std::views::transform(&Renderer::BaseStyle::material) + | std::views::transform(&MaterialStyle::encoded) + | std::views::join) { - materialStyles[i] = std::make_shared(*other.materialStyles[i]); + const unsigned int u = *reinterpret_cast(&f); + hash ^= u + 0x9e3779b9 + (hash << 6) + (hash >> 2); } } -std::unique_ptr StrokeElementLayerStyle::clonePtr() const +LayerStyleContainer LayerStyleContainer::fromJson(bool isClosedElement, const QJsonArray& jsonArray) +{ + LayerStyleContainer container(isClosedElement); + for (const auto& style : jsonArray) + { + container.useStyle(LayerStyle::fromJson(style.toObject())); + } + return container; +} + +LayerStyleContainer::LayerStyleContainer(bool isClosedElement) : hash(0) +{ + for (const auto& style : commonStyles) + { + unusedStyles.insert(style); + } + if (isClosedElement) + { + for (const auto& style : closedOnlyStyles) + { + unusedStyles.insert(style); + } + } + else + { + for (const auto& style : unclosedOnlyStyles) + { + unusedStyles.insert(style); + } + } +} + +std::vector LayerStyleContainer::toBaseStyles() const +{ + std::vector result; + for (const auto& style : styles | std::views::values) + { + auto baseStyles = style->toBaseStyles(); + result.insert(result.end(), + std::make_move_iterator(baseStyles.begin()), + std::make_move_iterator(baseStyles.end())); + } + return result; +} + +QJsonArray LayerStyleContainer::toJson() const +{ + QJsonArray json; + for (const auto& style : styles | std::views::values) + { + json.append(style->toJson()); + } + return json; +} + +QStringList LayerStyleContainer::unusedStyleNames() const +{ + QStringList result; + for(const auto& name : unusedStyles | std::views::keys) + { + result << name; + } + return result; +} + +std::unique_ptr LayerStyleContainer::makeUnusedStyle(const QString& styleName) const +{ + return unusedStyles.at(styleName)(); +} + +bool LayerStyleContainer::empty() const +{ + return styles.empty(); +} + +bool LayerStyleContainer::full() const +{ + return unusedStyles.empty(); +} + +std::map>::iterator LayerStyleContainer::begin() +{ + return styles.begin(); +} + +std::map>::iterator LayerStyleContainer::end() +{ + return styles.end(); +} + +bool LayerStyleContainer::useStyle(const std::shared_ptr& style) +{ + auto styleNode = unusedStyles.extract(style->getDisplayName()); + if (styleNode.empty()) + { + return false; + } + styles[styleNode.key()] = style; + usedStyles.insert(std::move(styleNode)); + return true; +} + +bool LayerStyleContainer::dropStyle(const QString& styleName) +{ + auto styleNode = usedStyles.extract(styleName); + if (styleNode.empty()) + { + return false; + } + styles.erase(styleName); + unusedStyles.insert(std::move(styleNode)); + return true; +} + +float LayerStyleContainer::boundingBoxAffectValue() const { + float maxLineWidth = 0; + const auto strokeStyle = styles.at(StrokeElementLayerStyle::displayName()); + if (strokeStyle != nullptr) + { + if (const auto strokeElementLayerStyle = + std::dynamic_pointer_cast(strokeStyle); + strokeElementLayerStyle != nullptr) + { + const auto& leftStyleStroke = strokeElementLayerStyle->strokePair.first; + const auto& rightStyleStroke = strokeElementLayerStyle->strokePair.second; + if (leftStyleStroke != nullptr) + { + maxLineWidth = std::max(maxLineWidth, leftStyleStroke->halfWidth); + } + if (rightStyleStroke != nullptr) + { + maxLineWidth = std::max(maxLineWidth, rightStyleStroke->halfWidth); + } + } + } + return maxLineWidth; +} + +size_t LayerStyleContainer::getHash() const +{ + return hash; +} + +std::unique_ptr StrokeElementLayerStyle::fromJson(const QJsonObject& json) +{ + auto ptr = std::make_unique( + std::static_pointer_cast( + std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64(json["left"].toString())))) + ), + std::static_pointer_cast( + std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64(json["right"].toString())))) + ) + ); + ptr->enableEachSideIndependent = json["enableEachSideIndependent"].toBool(); + return ptr; +} + +StrokeElementLayerStyle::StrokeElementLayerStyle() +{ + const auto materialMap = std::map(); + this->strokePair.first = std::make_shared( + 7, + Renderer::StrokeType::kLeftSide, Renderer::StrokeEndType::kFlat, + std::make_shared(materialMap, false) + ); + + this->strokePair.second = std::make_shared( + 7, + Renderer::StrokeType::kRightSide, Renderer::StrokeEndType::kFlat, + std::make_shared(materialMap, false) + ); + +} + +StrokeElementLayerStyle::StrokeElementLayerStyle(const PMaterialStyleStroke& left, const PMaterialStyleStroke& right) +{ + this->strokePair.first = left; + this->strokePair.second = right ? right : std::static_pointer_cast( + std::shared_ptr(std::move(left->clone())) + ); +} + +StrokeElementLayerStyle::StrokeElementLayerStyle(const StrokeElementLayerStyle& other) +{ + strokePair.first = std::static_pointer_cast( + std::shared_ptr(std::move(other.strokePair.first->clone())) + ); + strokePair.second = std::static_pointer_cast( + std::shared_ptr(std::move(other.strokePair.second->clone())) + ); + enableEachSideIndependent = other.enableEachSideIndependent; +} + +QJsonObject StrokeElementLayerStyle::toJson() const +{ + auto json = LayerStyle::toJson(); + json["enableEachSideIndependent"] = enableEachSideIndependent; + json["left"] = EncodeUtil::toBase64(strokePair.first->encoded()); + json["right"] = EncodeUtil::toBase64(strokePair.second->encoded()); + return json; +} + +std::unique_ptr StrokeElementLayerStyle::clone() const { LayerStyleContainer container(isClosedElement); for (const auto& style : jsonArray) @@ -261,45 +499,84 @@ std::unique_ptr StrokeElementLayerStyle::clone() const return std::make_unique(StrokeElementLayerStyle(*this)); } +std::unique_ptr FillElementLayerStyle::fromJson(const QJsonObject& json) +{ + auto ptr = std::make_unique( + std::static_pointer_cast( + std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64(json["material"].toString())))) + ) + ); + return ptr; +} + std::vector FillElementLayerStyle::toBaseStyles() const { - return std::vector(); + return { {std::make_shared(), fillMaterialStyle} }; } -QString FillElementLayerStyle::getStyleName() const +QWidget* FillElementLayerStyle::getInputWidget() { - return QStringLiteral(""); -} - -QWidget* FillElementLayerStyle::getInputWidget() const -{ - // TODO - QLineEdit* name = new QLineEdit; - name->setText(QStringLiteral("")); - return name; + return new FillStyleWidget(fillMaterialStyle); } QWidget* FillElementLayerStyle::getListDisplayWidget() const { - QWidget* w = new QWidget; - QLabel* name = new QLabel(w); + auto* w = new QWidget; + auto* name = new QLabel(w); name->setText(QStringLiteral("")); - QHBoxLayout* layout = new QHBoxLayout(w); + auto* layout = new QHBoxLayout(w); layout->setMargin(0); layout->addWidget(name); return w; } -FillElementLayerStyle::FillElementLayerStyle(const FillElementLayerStyle& other) +FillElementLayerStyle::FillElementLayerStyle(const PMaterialStyleFill& fillMaterialStyle) + : fillMaterialStyle(fillMaterialStyle) { - materialStyles = std::vector>(other.materialStyles.size()); - for (size_t i = 0; i < other.materialStyles.size(); i++) - { - materialStyles[i] = std::make_shared(*other.materialStyles[i]); - } + if (!fillMaterialStyle) + { + this->fillMaterialStyle = std::make_shared( + std::make_shared(Renderer::Material(QColor::fromRgb(0, 0, 0))) + ); + } } -std::unique_ptr FillElementLayerStyle::clonePtr() const +FillElementLayerStyle::FillElementLayerStyle(const FillElementLayerStyle& other) +{ + this->fillMaterialStyle = std::static_pointer_cast( + std::shared_ptr(std::move(other.fillMaterialStyle->clone())) + ); +} + +QJsonObject FillElementLayerStyle::toJson() const +{ + auto json = LayerStyle::toJson(); + json["material"] = EncodeUtil::toBase64(fillMaterialStyle->encoded()); + return json; +} + +std::unique_ptr FillElementLayerStyle::clone() const { return std::make_unique(FillElementLayerStyle(*this)); } + +std::unique_ptr LayerStyle::fromJson(const QJsonObject& json) +{ + QString type = json["type"].toString(); + if (type == StrokeElementLayerStyle::typeName()) + { + return StrokeElementLayerStyle::fromJson(json); + } + if (type == FillElementLayerStyle::typeName()) + { + return FillElementLayerStyle::fromJson(json); + } + return nullptr; +} + +QJsonObject LayerStyle::toJson() const +{ + QJsonObject json; + json["type"] = this->getTypeName(); + return json; +} diff --git a/ArchitectureColoredPainting/src/Editor/LayerStyle.h b/ArchitectureColoredPainting/src/Editor/LayerStyle.h index 79a49ec..9274732 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerStyle.h +++ b/ArchitectureColoredPainting/src/Editor/LayerStyle.h @@ -1,56 +1,130 @@ #pragma once -#include #include #include -#include -#include +#include +#include #include +#include +#include #include "../Renderer/Painting/ElementStyle.h" #include "../Renderer/Painting/MaterialStyleStroke.h" #include "../Renderer/Painting/MaterialStyleFill.h" -/** - * ڽStyleʱȴնȻֱӵgetInputWidget() - * ڽStyle޸ʱֱӵgetInputWidget() - * - * LayerStyleʵ࣬ӦͬʱʵElementStyleͬģ߻䣩styleϳһ࣬ - * ͬ࣬һͼӦֻӵһ - */ -class LayerStyle +using Renderer::MaterialStyle; +using Renderer::MaterialStyleStroke; +using Renderer::MaterialStyleFill; + +#define STYLE_NAME(display_name, type_name) \ + static QString displayName() { return QStringLiteral(display_name); } \ + QString getDisplayName() const override { return QStringLiteral(display_name); } \ + static QString typeName() { return type_name; } \ + QString getTypeName() const override { return type_name; } +#define radialStroke(stroke) std::static_pointer_cast(stroke->materialStroke) +#define plainFill(fill) std::static_pointer_cast(fill->materialFill) + +class LayerStyle : public Renderer::ElementStyle { public: - const static std::vector()>>> types; - virtual QString getStyleName() const = 0; - virtual QWidget* getInputWidget() const = 0; + static std::unique_ptr fromJson(const QJsonObject& json); + virtual ~LayerStyle() = default; + + virtual QString getDisplayName() const = 0; + virtual QString getTypeName() const = 0; + + virtual QWidget* getInputWidget() = 0; virtual QWidget* getListDisplayWidget() const = 0; - virtual ~LayerStyle() {}; - virtual std::unique_ptr clonePtr() const = 0; + + virtual QJsonObject toJson() const; + virtual std::unique_ptr clone() const = 0; }; -class StrokeElementLayerStyle : public Renderer::ElementStyle, public LayerStyle +class StrokeElementLayerStyle : public LayerStyle { + using PMaterialStyleStroke = std::shared_ptr; + public: - std::vector toBaseStyles() const override; - QString getStyleName() const override; - QWidget* getInputWidget() const override; - QWidget* getListDisplayWidget() const override; - StrokeElementLayerStyle() = default; + STYLE_NAME("", "stroke") + static std::unique_ptr fromJson(const QJsonObject& json); + + StrokeElementLayerStyle(); + StrokeElementLayerStyle(const PMaterialStyleStroke& left, const PMaterialStyleStroke& right = nullptr); StrokeElementLayerStyle(const StrokeElementLayerStyle& other); - ~StrokeElementLayerStyle() = default; - std::vector> materialStyles; - std::unique_ptr clonePtr() const override; + ~StrokeElementLayerStyle() override = default; + + std::vector toBaseStyles() const override; + QWidget* getInputWidget() override; + QWidget* getListDisplayWidget() const override; + QJsonObject toJson() const override; + std::unique_ptr clone() const override; + + std::pair strokePair; + bool enableEachSideIndependent = false; }; -class FillElementLayerStyle : public Renderer::ElementStyle, public LayerStyle +class FillElementLayerStyle : public LayerStyle { + using PMaterialStyleFill = std::shared_ptr; + public: - std::vector toBaseStyles() const override; - QString getStyleName() const override; - QWidget* getInputWidget() const override; - QWidget* getListDisplayWidget() const override; - FillElementLayerStyle() = default; + STYLE_NAME("", "fill") + static std::unique_ptr fromJson(const QJsonObject& json); + + FillElementLayerStyle(const PMaterialStyleFill& fillMaterialStyle = nullptr); FillElementLayerStyle(const FillElementLayerStyle& other); - ~FillElementLayerStyle() = default; - std::vector> materialStyles; - std::unique_ptr clonePtr() const override; + ~FillElementLayerStyle() override = default; + + std::vector toBaseStyles() const override; + QWidget* getInputWidget() override; + QWidget* getListDisplayWidget() const override; + QJsonObject toJson() const override; + std::unique_ptr clone() const override; + + PMaterialStyleFill fillMaterialStyle; +}; + +/** + * LayerStyleÿݱʱҪֶcomputeNewHash()ϣֵ + */ +class LayerStyleContainer : public Renderer::ElementStyle +{ + using DisplayNameWithSupplier = std::map()>>; +private: + + inline const static DisplayNameWithSupplier commonStyles = { { + StrokeElementLayerStyle::displayName(), + [] { return std::make_unique(); } + } }; + inline const static DisplayNameWithSupplier closedOnlyStyles = { { + FillElementLayerStyle::displayName(), + [] { return std::make_unique(); } + } }; + inline const static DisplayNameWithSupplier unclosedOnlyStyles = { }; + + DisplayNameWithSupplier unusedStyles; + DisplayNameWithSupplier usedStyles; + std::map> styles; + size_t hash; +public: + static LayerStyleContainer fromJson(bool isClosedElement, const QJsonArray& jsonArray); + + LayerStyleContainer(bool isClosedElement); + std::vector toBaseStyles() const override; + QJsonArray toJson() const; + + bool empty() const; + bool full() const; + std::map>::iterator begin(); + std::map>::iterator end(); + + QStringList unusedStyleNames() const; + std::unique_ptr makeUnusedStyle(const QString& styleName) const; + bool useStyle(const std::shared_ptr& style); + bool dropStyle(const QString& styleName); + float boundingBoxAffectValue() const; + size_t getHash() const; + + /** + * Ҫÿθĺֶ + */ + void computeNewHash(); }; \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp b/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp index 9e1febe..bf0222b 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp +++ b/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp @@ -46,6 +46,7 @@ LayerWrapper::LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementM property.scale = {transformJson.value("scale").toObject().value("x").toDouble(), transformJson.value("scale").toObject().value("y").toDouble()}; property.rotation = {transformJson.value("rotation").toDouble()}; + selected = false; } FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent) @@ -72,27 +73,35 @@ FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *element } } -LeafLayerWrapper::LeafLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent) - : LayerWrapper(json, parent) +LeafLayerWrapper::LeafLayerWrapper(QJsonObject json, ElementManager* elementManager, FolderLayerWrapper* parent) + : LayerWrapper(json, parent), + wrappedElement(elementManager->getElementById(json.value("element").toInt())), + styles(LayerStyleContainer::fromJson(wrappedElement->isClosed(), json.value("styles").toArray())) { qDebug() << json.value("name").toString() << " " << this; - int elementIndex = json.value("element").toInt(); - wrappedElement = elementManager->getElementById(elementIndex); + if(wrappedElement != nullptr) + wrappedElement->referencedCount++; } -void LayerWrapper::SimpleProperty::apply(PixelPath&cache) const +LeafLayerWrapper::~LeafLayerWrapper() { - QTransform trans; + if (wrappedElement != nullptr) + wrappedElement->referencedCount--; +} + +void LayerWrapper::SimpleProperty::apply(PixelPath&cache) +{ + transform.reset(); double centerX = cache.getBoundingRect().center().x(); double centerY = cache.getBoundingRect().center().y(); //qDebug() << name << " " << cache.boundingRect().center(); //qDebug() << name << " " << cache.boundingRect(); - trans.translate(centerX, centerY); - trans.translate(offset.x(), offset.y()); - trans.rotate(rotation); - trans.scale(scale.x(), scale.y()); - trans.translate(-centerX, -centerY); - cache = cache.trans(trans); + transform.translate(centerX, centerY); + transform.translate(offset.x(), offset.y()); + transform.rotate(rotation); + transform.scale(scale.x(), scale.y()); + transform.translate(-centerX, -centerY); + cache = cache.trans(transform); } QTransform LayerWrapper::getTransform() @@ -129,7 +138,7 @@ void LeafLayerWrapper::refresh(LayerWrapper* layer) cache.clear(); if (wrappedElement != nullptr) { - cache.addPath(wrappedElement->getPaintObject(&(this->styles))); + cache.addPath(wrappedElement->getPaintObject(this->styles)); } LayerWrapper::refresh(); } @@ -189,19 +198,39 @@ void FolderLayerWrapper::delSelf() { QTreeWidgetItem* LayerWrapper::getQTreeItem() { - this->qTreeWidgetItem->setText(0, this->property.name); this->qTreeWidgetItem->setData(0, Qt::UserRole, QVariant::fromValue(this)); return this->qTreeWidgetItem; } +QTreeWidgetItem* LeafLayerWrapper::getQTreeItem() +{ + if (this->qTreeWidgetItem == nullptr) + this->qTreeWidgetItem = new QTreeWidgetItem(); + if (typeid(*wrappedElement) == typeid(GroupElement)) + { + this->qTreeWidgetItem->setText(0, "@ "+this->property.name); + } + else + { + this->qTreeWidgetItem->setText(0, this->property.name); + } + this->qTreeWidgetItem->setText(1,">> "+this->wrappedElement->name); + this->qTreeWidgetItem->setTextColor(1, Qt::blue); + return LayerWrapper::getQTreeItem(); +} + QTreeWidgetItem* FolderLayerWrapper::getQTreeItem() { - while (this->qTreeWidgetItem->childCount() > 0) { - this->qTreeWidgetItem->removeChild(this->qTreeWidgetItem->child(0)); - } - for (auto& child : this->children) { + for (auto& child : this->children) { this->qTreeWidgetItem->addChild(child->getQTreeItem()); } + this->qTreeWidgetItem->setText(0, this->property.name); + auto ele = this->elementManager->getElementById(this->getReferencedBy()); + if (ele != nullptr) + { + this->qTreeWidgetItem->setText(1, "<< " + ele->name); + this->qTreeWidgetItem->setTextColor(1, Qt::darkGreen); + } return LayerWrapper::getQTreeItem(); } @@ -239,6 +268,7 @@ QJsonObject LeafLayerWrapper::toJson() const QJsonObject json = LayerWrapper::toJson(); json.insert("element", wrappedElement->index); json.insert("is-folder", false); + json.insert("styles", styles.toJson()); return json; } @@ -250,31 +280,152 @@ int FolderLayerWrapper::getReferencedBy()const return -1; } -void LayerWrapper::paint(QPainter* painter) +void LayerWrapper::paint(QPainter* painter, QTransform transform, bool ignoreSelected) +{ + if (!ignoreSelected && this->selected) + { + painter->save(); + painter->setTransform(transform, ignoreSelected); + painter->setPen(QPen(Qt::gray, 2)); + painter->setPen(Qt::DashLine); + painter->drawRect(cache.getBoundingRect()); + painter->restore(); + } +} + +void FolderLayerWrapper::paint(QPainter* painter, QTransform transform, bool ignoreSelected) +{ + LayerWrapper::paint(painter, transform, ignoreSelected); + transform = property.transform * transform; + //qDebug() << transform; + for (auto& child : children) + child->paint(painter, transform, ignoreSelected); +} + +void LeafLayerWrapper::paint(QPainter* painter, QTransform transform, bool ignoreSelected) +{ + LayerWrapper::paint(painter, transform, ignoreSelected); + transform = property.transform * transform; + //qDebug() << transform; + if (wrappedElement != nullptr) + { + wrappedElement->paint(painter, transform, styles); + } +} + +void LayerWrapper::collectUpReachable(std::set& reachable) +{ + auto cPos = this; + while (cPos != nullptr) + { + reachable.insert(cPos); + cPos = cPos->parent; + } +} + +void LayerWrapper::collectDownReachable(std::set& reachable) +{ + reachable.insert(this); +} + +void LeafLayerWrapper::collectDownReachable(std::set& reachable) +{ + LayerWrapper::collectDownReachable(reachable); + auto ele = dynamic_cast(wrappedElement); + if (ele != nullptr) + { + ele->collectReachable(reachable); + } +} + +void FolderLayerWrapper::collectDownReachable(std::set& reachable) +{ + LayerWrapper::collectDownReachable(reachable); + for (auto& child : children) + child->collectDownReachable(reachable); +} + +void LayerWrapper::refreshTreeItem() { } -void FolderLayerWrapper::paint(QPainter* painter) +void LeafLayerWrapper::refreshTreeItem() { - for (auto& child : children) - child->paint(painter); + if (typeid(*wrappedElement) == typeid(GroupElement)) + { + this->qTreeWidgetItem->setText(0, "@ " + this->property.name); + } + else + { + this->qTreeWidgetItem->setText(0, this->property.name); + } + this->qTreeWidgetItem->setText(1, ">> " + this->wrappedElement->name); + this->qTreeWidgetItem->setTextColor(1, Qt::blue); } -void LeafLayerWrapper::paint(QPainter* painter) +void FolderLayerWrapper::refreshTreeItem() { - if (wrappedElement != nullptr) + for (auto& child : this->children) { + child->refreshTreeItem(); + } + this->qTreeWidgetItem->setText(0, this->property.name); + auto ele = this->elementManager->getElementById(this->getReferencedBy()); + if (ele != nullptr) + { + this->qTreeWidgetItem->setText(1, "<< " + ele->name); + this->qTreeWidgetItem->setTextColor(1, Qt::darkGreen); + } + if (this->referencedCount() > 0) { - //painter->save(); - QTransform trans; - LayerWrapper* layer = this; - while (layer != nullptr) { - trans *= layer->getTransform(); - layer = layer->getParent(); - } - auto p = wrappedElement->getPaintObject(&this->styles); - p.trans(trans); - painter->drawPixmap(0, 0, p.getPixmap()); - //painter->restore(); + this->qTreeWidgetItem->setToolTip(0,QString::fromLocal8Bit("ü") + QString::number(this->referencedCount(true)) + "\n" + + QString::fromLocal8Bit("ǰڵ㱻ü") + QString::number(this->getReferencedBy() != -1)); } + else + { + this->qTreeWidgetItem->setToolTip(0, ""); + } +} + +size_t LayerWrapper::referencedCount(bool excludeSelf) const +{ + return 0; +} + +size_t FolderLayerWrapper::referencedCount(bool excludeSelf) const +{ + size_t count = 0; + for (auto& child : children) + count += child->referencedCount(); + if (!excludeSelf && this->getReferencedBy() != -1) + count++; + return count; +} + +bool LayerWrapper::deleteable(bool excludeSubTree) const +{ + return true; +} + +bool FolderLayerWrapper::deleteable(bool excludeSubTree) const +{ + if (excludeSubTree) + return this->getReferencedBy() == -1; + else + return this->referencedCount() == 0; +} + +bool LayerWrapper::referencingGroupElement() const +{ + return false; +} + +bool LeafLayerWrapper::referencingGroupElement() const +{ + return typeid(*wrappedElement) == typeid(GroupElement); +} + +bool LayerWrapper::canApplyStyles() const +{ + return typeid(*this) == typeid(LeafLayerWrapper) && !referencingGroupElement(); } \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/LayerWrapper.h b/ArchitectureColoredPainting/src/Editor/LayerWrapper.h index 2186cd6..947d90c 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerWrapper.h +++ b/ArchitectureColoredPainting/src/Editor/LayerWrapper.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ class LayerWrapper public: QTreeWidgetItem* qTreeWidgetItem; + bool selected; struct SimpleProperty { QString name = ""; @@ -43,8 +45,9 @@ class LayerWrapper double rotation = 0; bool flipHorizontally = 0; bool flipVertically = 0; + QTransform transform; // TODO: QPainterPathΪBitmapPath - void apply(PixelPath&cache) const; + void apply(PixelPath&cache); } property; virtual void setParent(FolderLayerWrapper*newParent); virtual void refresh(LayerWrapper* layer = nullptr); @@ -55,7 +58,7 @@ class LayerWrapper FolderLayerWrapper*getParent() const; // invoke by manager, then invoke parent's applyStyles LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementManager* elementManager=nullptr); LayerWrapper() = default; - virtual void paint(QPainter* painter); + virtual void paint(QPainter* painter, QTransform transform=QTransform(), bool ignoreSelected = false); // TODO : export Function // virtual LayerWrapper *addChild() = 0; // Leaf Child Only // virtual LayerWrapper *addParent() = 0; // Folder Parent Only @@ -65,7 +68,13 @@ class LayerWrapper virtual void delSelf(); virtual QJsonObject toJson() const; ~LayerWrapper() = default; - + virtual void collectUpReachable(std::set& reachable); + virtual void collectDownReachable(std::set& reachable); + virtual void refreshTreeItem(); + virtual size_t referencedCount(bool excludeSelf = false) const; + virtual bool deleteable(bool excludeSubTree = false) const; + virtual bool referencingGroupElement() const; + bool canApplyStyles() const; }; class FolderLayerWrapper : public LayerWrapper @@ -88,23 +97,29 @@ class FolderLayerWrapper : public LayerWrapper QTreeWidgetItem* getQTreeItem() override; QJsonObject toJson() const override; int getReferencedBy()const; - void paint(QPainter* painter) override; + void paint(QPainter* painter, QTransform transform = QTransform(), bool ignoreSelected = false) override; + void collectDownReachable(std::set& reachable) override; + void refreshTreeItem() override; + size_t referencedCount(bool excludeSelf = false) const override; + bool deleteable(bool excludeSubTree = false) const override; }; class LeafLayerWrapper : public LayerWrapper { public: GraphicElement *wrappedElement; - //const vector styles; - vector> styles; + LayerStyleContainer styles; public: - ~LeafLayerWrapper() = default; + ~LeafLayerWrapper(); void refresh(LayerWrapper* layer = nullptr) override; - LeafLayerWrapper() = default; LeafLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent); QJsonObject toJson() const override; - void paint(QPainter* painter) override; + void paint(QPainter* painter, QTransform transform = QTransform(), bool ignoreSelected = false) override; + void collectDownReachable(std::set& reachable) override; + QTreeWidgetItem* getQTreeItem() override; + void refreshTreeItem() override; + bool referencingGroupElement() const override; }; Q_DECLARE_METATYPE(LayerWrapper *) diff --git a/ArchitectureColoredPainting/src/Editor/PixelPath.cpp b/ArchitectureColoredPainting/src/Editor/PixelPath.cpp index 89f4bbc..3087f39 100644 --- a/ArchitectureColoredPainting/src/Editor/PixelPath.cpp +++ b/ArchitectureColoredPainting/src/Editor/PixelPath.cpp @@ -66,6 +66,7 @@ void PixelPath::clear() { pixmap.fill(Qt::transparent); boundingRect = QRectF(0, 0, 0, 0); + painterPath.clear(); } PixelPath PixelPath::trans(QTransform& mat)const @@ -76,6 +77,7 @@ PixelPath PixelPath::trans(QTransform& mat)const painter.setRenderHint(QPainter::HighQualityAntialiasing); painter.setTransform(mat); painter.drawPixmap(0, 0, pixmap); + result.painterPath.addPath(this->painterPath); result.boundingRect = mat.mapRect(boundingRect); return result; } diff --git a/ArchitectureColoredPainting/src/Editor/PixelPath.h b/ArchitectureColoredPainting/src/Editor/PixelPath.h index a764ce8..c3dc704 100644 --- a/ArchitectureColoredPainting/src/Editor/PixelPath.h +++ b/ArchitectureColoredPainting/src/Editor/PixelPath.h @@ -7,14 +7,14 @@ class PixelPath { -private: +public: QRectF boundingRect; QPixmap pixmap; QPainterPath painterPath; int w,h; public: - PixelPath(int w=1080, int h= 1080); - PixelPath(QPainterPath painterPath,int w = 1080, int h = 1080); + PixelPath(int w=16, int h= 16); + PixelPath(QPainterPath painterPath,int w = 16, int h = 16); ~PixelPath() = default; QRectF getBoundingRect() const; QPixmap getPixmap() const; diff --git a/ArchitectureColoredPainting/src/Editor/PreviewWindow.cpp b/ArchitectureColoredPainting/src/Editor/PreviewWindow.cpp index 723f0b1..c4a03b5 100644 --- a/ArchitectureColoredPainting/src/Editor/PreviewWindow.cpp +++ b/ArchitectureColoredPainting/src/Editor/PreviewWindow.cpp @@ -2,7 +2,8 @@ PreviewWindow::PreviewWindow(QWidget *parent) : QOpenGLWidget(parent) { - this->setFixedSize(QSize(1080, 1080)); + //this->setFixedSize(QSize(108, 108)); + this->setStyleSheet("border: 1px solid black"); this->renderer = Renderer::ElementRenderer::instance(); QSurfaceFormat surfaceFormat; surfaceFormat.setSamples(16); @@ -12,6 +13,7 @@ PreviewWindow::PreviewWindow(QWidget *parent) : QOpenGLWidget(parent) painter->setRenderHint(QPainter::HighQualityAntialiasing); layerManager = nullptr; currentLayer = nullptr; + backgroundColor = QColor(255, 255, 255, 255); } void PreviewWindow::initialize(LayerManager *layerManager,QSize windowSize) @@ -43,7 +45,7 @@ void PreviewWindow::initializeGL() void PreviewWindow::paintGL() { - glClearColor(1.0, 1.0, 1.0, 1.0); + glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), backgroundColor.alphaF()); glClear(GL_COLOR_BUFFER_BIT); painter->begin(this); painter->setRenderHint(QPainter::Antialiasing); @@ -86,14 +88,21 @@ void PreviewWindow::mouseMoveEvent(QMouseEvent* event) // µôƽͼ currentLayer->property.offset.setX(currentLayer->property.offset.x() + dx); currentLayer->property.offset.setY(currentLayer->property.offset.y() + dy); - qDebug() << dx << "----" << dy; - emit layerInfoChanged(); } - else if (event->buttons() & Qt::RightButton) { + else if (event->buttons() & Qt::RightButton) { // µҼôתͼ - qreal angle = -sqrt(dx * dx + dy * dy) / 1.0; + qreal angle = dx; currentLayer->property.rotation += angle; - emit layerInfoChanged(); + } + auto layer = currentLayer; + while (layer != nullptr) + { + auto index = -1; + if (typeid(*layer) == typeid(FolderLayerWrapper)) + index = dynamic_cast(layer)->getReferencedBy(); + if (index != -1) + emit refreshElementPreviewByIndex(index); + layer = layer->getParent(); } } // һελ @@ -103,5 +112,11 @@ void PreviewWindow::mouseMoveEvent(QMouseEvent* event) void PreviewWindow::mouseReleaseEvent(QMouseEvent* event) { - // ͷʱκβ + emit layerInfoChanged(); +} + +void PreviewWindow::setBackgroundColor(QColor color) +{ + this->backgroundColor = color; + this->repaint(); } \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/PreviewWindow.h b/ArchitectureColoredPainting/src/Editor/PreviewWindow.h index 66f4a52..9f9d07c 100644 --- a/ArchitectureColoredPainting/src/Editor/PreviewWindow.h +++ b/ArchitectureColoredPainting/src/Editor/PreviewWindow.h @@ -23,6 +23,7 @@ class PreviewWindow : public QOpenGLWidget, protected QOpenGLFunctions QRectF viewportRect; LayerWrapper* currentLayer; QPointF m_lastPos; + QColor backgroundColor; void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; @@ -35,6 +36,7 @@ class PreviewWindow : public QOpenGLWidget, protected QOpenGLFunctions void paintGL() override; void resizeGL(int w, int h) override; Renderer::ElementRenderer* const getRenderer()const; + void setBackgroundColor(QColor color); public slots: void currentLayerChanged(LayerWrapper*); @@ -42,4 +44,6 @@ class PreviewWindow : public QOpenGLWidget, protected QOpenGLFunctions signals: void layerInfoChanged(); + void refreshElementPreview(GraphicElement*); + void refreshElementPreviewByIndex(int); }; diff --git a/ArchitectureColoredPainting/src/Editor/RightBar/EditorSettingWidget.cpp b/ArchitectureColoredPainting/src/Editor/RightBar/EditorSettingWidget.cpp new file mode 100644 index 0000000..2f54a22 --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/RightBar/EditorSettingWidget.cpp @@ -0,0 +1,21 @@ +#include "EditorSettingWidget.h" +#include +#include +#include + +EditorSettingWidget::EditorSettingWidget(QWidget* parent) + : QWidget(parent) +{ + ui.setupUi(this); + connect(ui.backgroundColorButton, &QPushButton::clicked, this, [this]() { + QColor color = QColorDialog::getColor(Qt::white, this, QString::fromLocal8Bit("ѡ񱳾ɫ")); + if (color.isValid()) + { + emit backgroundColorChanged(color); + } + }); + connect(ui.renameButton, &QPushButton::clicked, this, [this]() { + QString name = QInputDialog::getText(this, QString::fromLocal8Bit(""), QString::fromLocal8Bit("µĿ")); + emit projectNameChanged(name); + }); +} \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/RightBar/EditorSettingWidget.h b/ArchitectureColoredPainting/src/Editor/RightBar/EditorSettingWidget.h new file mode 100644 index 0000000..ba0166c --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/RightBar/EditorSettingWidget.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include "ui_EditorSettingWidget.h" + +class EditorSettingWidget : + public QWidget +{ + Q_OBJECT; + + private: + Ui::EditorSettingWidget ui; + + public: + EditorSettingWidget(QWidget* parent = nullptr); + + signals: + void backgroundColorChanged(QColor); + void projectNameChanged(QString); + +}; + diff --git a/ArchitectureColoredPainting/src/Editor/RightBar/InfoDisplayWidget.cpp b/ArchitectureColoredPainting/src/Editor/RightBar/InfoDisplayWidget.cpp index d41d374..6869729 100644 --- a/ArchitectureColoredPainting/src/Editor/RightBar/InfoDisplayWidget.cpp +++ b/ArchitectureColoredPainting/src/Editor/RightBar/InfoDisplayWidget.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -46,32 +45,27 @@ void InfoDisplayWidget::generateLayerForm() rotation->setValidator(new QIntValidator(-10000, 10000, this)); connect(rotation, &QLineEdit::textChanged, [=](QString content) { this->displayLayer->property.rotation = content.toDouble(); - emit requireRefreshElementWidget(); - emit requireRefreshPreview(); + emit triggerCentralRefresh(); }); offsetX->setValidator(new QIntValidator(-10000, 10000, this)); connect(offsetX, &QLineEdit::textChanged, [=](QString content) { this->displayLayer->property.offset = {content.toDouble(), this->displayLayer->property.offset.y()}; - emit requireRefreshElementWidget(); - emit requireRefreshPreview(); + emit triggerCentralRefresh(); }); offsetY->setValidator(new QIntValidator(-10000, 10000, this)); connect(offsetY, &QLineEdit::textChanged, [=](QString content) { this->displayLayer->property.offset = {this->displayLayer->property.offset.x(), content.toDouble()}; - emit requireRefreshElementWidget(); - emit requireRefreshPreview(); + emit triggerCentralRefresh(); }); - scaleX->setValidator(new QDoubleValidator(0.001, 1000, 4, this)); + scaleX->setValidator(new QDoubleValidator(-1000, 1000, 4, this)); connect(scaleX, &QLineEdit::textChanged, [=](QString content) { this->displayLayer->property.scale = {content.toDouble(), this->displayLayer->property.scale.y()}; - emit requireRefreshElementWidget(); - emit requireRefreshPreview(); + emit triggerCentralRefresh(); }); - scaleY->setValidator(new QDoubleValidator(0.001, 1000, 4, this)); + scaleY->setValidator(new QDoubleValidator(-1000, 1000, 4, this)); connect(scaleY, &QLineEdit::textChanged, [=](QString content) { this->displayLayer->property.scale = {this->displayLayer->property.scale.x(), content.toDouble()}; - emit requireRefreshElementWidget(); - emit requireRefreshPreview(); + emit triggerCentralRefresh(); }); layout->addRow("layer name:", name); @@ -81,39 +75,36 @@ void InfoDisplayWidget::generateLayerForm() layout->addRow("scale-X:", scaleX); layout->addRow("scale-Y:", scaleY); layout->setRowWrapPolicy(QFormLayout::DontWrapRows); - - LeafLayerWrapper* leafP = dynamic_cast(this->displayLayer); - if (leafP) { - QListWidget* styleList = new QListWidget(this); - QListWidgetItem* header = new QListWidgetItem; - QWidget* headerWidget = new QWidget(styleList); - QHBoxLayout* headerLayout = new QHBoxLayout; + if (auto* leafP = dynamic_cast(this->displayLayer); leafP) { + auto* styleList = new QListWidget(this); - QLabel* headerLabel = new QLabel(headerWidget); + auto* header = new QListWidgetItem; + auto* headerWidget = new QWidget(styleList); + auto* headerLayout = new QHBoxLayout; + + auto* headerLabel = new QLabel(headerWidget); headerLabel->setText("样式列表"); headerLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); //QtMaterialRaisedButton* addStyleButton = new QtMaterialRaisedButton("+", headerWidget); - QPushButton* addStyleButton = new QPushButton("+", headerWidget); + auto* addStyleButton = new QPushButton("+", headerWidget); addStyleButton->setFixedSize(QSize(20, 20)); - if (leafP->styles.size() >= LayerStyle::types.size()) + if (leafP->styles.full()) { addStyleButton->setDisabled(true); } else { - connect(addStyleButton, &QPushButton::clicked, [&, leafP]() { - LayerStyleDialog* dialog = new LayerStyleDialog(nullptr, nullptr, &(leafP->styles)); - dialog->exec(); - if (dialog->layerStyle) - { - leafP->styles.push_back(dialog->layerStyle); - emit requireRefreshPreview(); - emit requireSelfRefresh(); - emit requireRefreshElementWidget(); - } - dialog->deleteLater(); + connect(addStyleButton, &QPushButton::clicked, [&, leafP] { + auto* dialog = new LayerStyleDialog(leafP->styles, nullptr, this); + dialog->exec(); + if (dialog->layerStyle) + { + leafP->styles.useStyle(dialog->layerStyle); + leafP->styles.computeNewHash(); + emit triggerCentralRefresh(); + } }); } @@ -125,103 +116,48 @@ void InfoDisplayWidget::generateLayerForm() header->setFlags(Qt::NoItemFlags); styleList->addItem(header); styleList->setItemWidget(header, headerWidget); - //static vector styleNames = { "样例1", "样例2", "样例3" }; - // auto createStyleItem = [this, styleList](int index) { - // QListWidgetItem* item = new QListWidgetItem; - // QWidget* w = new QWidget; - // item->setSizeHint(QSize(50, 40)); - // QHBoxLayout* layout = new QHBoxLayout; - // QPushButton* deleteButton = new QPushButton(w); - // QPushButton* detailButton = new QPushButton(w); - // QLabel* name = new QLabel(w); - // name->setText(styleNames[index]); - // detailButton->setText("..."); - // detailButton->setFixedSize(QSize(20, 20)); - // deleteButton->setText("×"); - // deleteButton->setFixedSize(QSize(20, 20)); - // connect(detailButton, &QPushButton::clicked, [styleList, item, this, index]() { - // QDialog dlg(this); - // dlg.setWindowTitle("样式详情"); - // dlg.resize(400, 200); - // QGridLayout *contentLayout = new QGridLayout(&dlg); - // QLineEdit* name = new QLineEdit(styleNames[index], &dlg); - // auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel); - // contentLayout->addWidget(buttonBox); - // connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept); - // connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject); - // bool updateStyle = dlg.exec(); - // if (updateStyle) { - // styleNames[index] = name->text(); - // qDebug() << name->text(); - // // 在此处修改新样式信息至内存 - // emit requireRefreshPreview(); - // emit requireSelfRefresh(); - // } - // }); - // connect(deleteButton, &QPushButton::clicked, [styleList,item,this]() { - // styleList->removeItemWidget(item); - // delete item; - // // 删除layer对应样式 - // emit requireRefreshPreview(); - // emit requireSelfRefresh(); - // }); - // layout->addWidget(name); - // layout->addWidget(detailButton); - // layout->addWidget(deleteButton); - // w->setLayout(layout); - // styleList->addItem(item); - // styleList->setItemWidget(item, w); - // }; - // for (int i = 0; i < styleNames.size(); i++) - // createStyleItem(i); - /*if (leafP->styles.empty()) + auto* styles = &leafP->styles; + for (auto styleIterator = styles->begin(); styleIterator != styles->end(); ++styleIterator) { - leafP->styles.push_back(std::shared_ptr(new StrokeElementLayerStyle())); - }*/ - std::vector>* styles = &(leafP->styles); - for (auto styleIterator = styles->begin(); styleIterator != styles->end(); styleIterator++) - { - QListWidgetItem* item = new QListWidgetItem; - QWidget* w = new QWidget; + auto* item = new QListWidgetItem; + auto* w = new QWidget(this); item->setSizeHint(QSize(50, 40)); - QHBoxLayout* layout = new QHBoxLayout; + auto* layout = new QHBoxLayout(w); layout->setAlignment(Qt::AlignmentFlag::AlignRight); //QtMaterialFlatButton* detailButton = new QtMaterialFlatButton(w); //QtMaterialFlatButton* removeButton = new QtMaterialFlatButton(w); - QPushButton* detailButton = new QPushButton(w); - QPushButton* removeButton = new QPushButton(w); + auto* detailButton = new QPushButton(w); + auto* removeButton = new QPushButton(w); detailButton->setText("..."); detailButton->setFixedSize(QSize(20, 20)); removeButton->setText("×"); removeButton->setFixedSize(QSize(20, 20)); connect(detailButton, &QPushButton::clicked, this, - [this, styleIterator]() + [this, styles, styleIterator] { - LayerStyleDialog* dialog = new LayerStyleDialog(nullptr, *styleIterator); + auto* dialog = + new LayerStyleDialog(*styles, styleIterator->second, this); dialog->exec(); if (dialog->layerStyle) { - (*styleIterator) = dialog->layerStyle; - emit requireRefreshPreview(); - emit requireSelfRefresh(); - emit requireRefreshElementWidget(); + styleIterator->second = dialog->layerStyle; + styles->computeNewHash(); + emit triggerCentralRefresh(); } - dialog->deleteLater(); }); connect(removeButton, &QPushButton::clicked, this, - [this, styleIterator, styles]() + [this, styleIterator, styles] { - styles->erase(styleIterator); - emit requireRefreshPreview(); - emit requireSelfRefresh(); - emit requireRefreshElementWidget(); + styles->dropStyle(styleIterator->first); + styles->computeNewHash(); + emit triggerCentralRefresh(); }); - QWidget* styleDisplayWidget = (*styleIterator)->getListDisplayWidget(); + QWidget* styleDisplayWidget = styleIterator->second->getListDisplayWidget(); styleDisplayWidget->setParent(w); styleDisplayWidget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); @@ -240,6 +176,12 @@ void InfoDisplayWidget::generateLayerForm() } void InfoDisplayWidget::triggerSelfRefresh() +{ + if (this->displayLayer != nullptr) + this->generateLayerForm(); +} + +void InfoDisplayWidget::refresh() { if (this->displayLayer != nullptr) this->generateLayerForm(); diff --git a/ArchitectureColoredPainting/src/Editor/RightBar/InfoDisplayWidget.h b/ArchitectureColoredPainting/src/Editor/RightBar/InfoDisplayWidget.h index 37b259c..da314f5 100644 --- a/ArchitectureColoredPainting/src/Editor/RightBar/InfoDisplayWidget.h +++ b/ArchitectureColoredPainting/src/Editor/RightBar/InfoDisplayWidget.h @@ -16,11 +16,13 @@ class InfoDisplayWidget : public QWidget public: void setLayer(LayerWrapper *layer); void generateLayerForm(); + void refresh(); public slots: void triggerSelfRefresh(); - signals: +signals: + void triggerCentralRefresh(); void requireRefreshPreview(); void requireSelfRefresh(); void requireRefreshElementWidget(); diff --git a/ArchitectureColoredPainting/src/Editor/RightBar/LayerTreeWidget.cpp b/ArchitectureColoredPainting/src/Editor/RightBar/LayerTreeWidget.cpp index f6962db..a8ba44e 100644 --- a/ArchitectureColoredPainting/src/Editor/RightBar/LayerTreeWidget.cpp +++ b/ArchitectureColoredPainting/src/Editor/RightBar/LayerTreeWidget.cpp @@ -9,18 +9,22 @@ LayerTreeWidget::LayerTreeWidget(QWidget *parent) this->selectedItem = nullptr; this->copiedItem = nullptr; this->setContextMenuPolicy(Qt::CustomContextMenu); - this->setHeaderLabel("Layer Content"); + this->setColumnWidth(0, 240); connect(this, &QTreeWidget::customContextMenuRequested, this, &LayerTreeWidget::popMenu); connect(this, &QTreeWidget::currentItemChanged, [=](QTreeWidgetItem *currentItem) { + if (this->selectedItem != nullptr) { + this->selectedItem->data(0, Qt::UserRole).value()->selected = false; + } this->selectedItem = currentItem; if (this->selectedItem != nullptr) { auto layer = this->selectedItem->data(0, Qt::UserRole).value(); + layer->selected = true; emit displayLayerChange(layer); } else { emit displayLayerChange(nullptr); } - emit requireRefreshPreview(); + emit triggerCentralRefresh(); }); // connect(this, &QTreeWidget::itemDoubleClicked, this, &LayerTreeWidget::onItemDoubleClicked); } @@ -46,7 +50,7 @@ void LayerTreeWidget::popMenu(const QPoint &pos) if (layer != nullptr) { if (typeid(*layer) == typeid(FolderLayerWrapper)) { menu.addAction(QString::fromLocal8Bit("ӽڵ"), this, [this, layer]() { - auto dialog = new LayerCreateWidget(elementManager, this); + auto dialog = new LayerCreateWidget(elementManager, dynamic_cast(layer), this); connect(dialog, &LayerCreateWidget::LayerInfoReturned, this, [this, layer](QJsonObject jsonObj) { auto folderLayer = dynamic_cast(layer); LayerWrapper* newLayer; @@ -58,29 +62,30 @@ void LayerTreeWidget::popMenu(const QPoint &pos) folderLayer->addChild(std::shared_ptr(newLayer)); folderLayer->qTreeWidgetItem->addChild(newLayer->getQTreeItem()); qDebug() << jsonObj<<"----------------------"; - this->refresh(); - emit requireRefreshPreview(); - emit requireRefreshElementWidget(); + emit triggerCentralRefresh(); }); dialog->exec(); }); - menu.addAction(QString::fromLocal8Bit("ɾӽڵ㣩"), this, [this]() { - auto layer = this->selectedItem->data(0, Qt::UserRole).value(); - layer->delSelf(); - layer->getParent()->removeChild(layer); - this->refresh(); - emit requireRefreshPreview(); - }); } if (layer != root) { menu.addAction(QString::fromLocal8Bit("ɾ"), this, [this]() { auto layer = this->selectedItem->data(0, Qt::UserRole).value(); layer->del(); layer->getParent()->removeChild(layer); - this->refresh(); - emit requireRefreshPreview(); + emit triggerCentralRefresh(); }); + menu.actions().last()->setEnabled(layer->deleteable()); menu.addAction(QString::fromLocal8Bit(""), this, &LayerTreeWidget::onRenameEvent); + if(typeid(*layer) == typeid(FolderLayerWrapper)) + { + menu.addAction(QString::fromLocal8Bit("ɾӽڵ㣩"), this, [this]() { + auto layer = this->selectedItem->data(0, Qt::UserRole).value(); + layer->delSelf(); + layer->getParent()->removeChild(layer); + emit triggerCentralRefresh(); + }); + menu.actions().last()->setEnabled(layer->deleteable(true)); + } } if (typeid(*layer) == typeid(FolderLayerWrapper) && ((FolderLayerWrapper*)layer)->getReferencedBy() == -1) { menu.addAction(QString::fromLocal8Bit("Ԫ"), this, [this]() { @@ -92,7 +97,7 @@ void LayerTreeWidget::popMenu(const QPoint &pos) "", &ok); if (ok && !name.isEmpty()) { elementManager->createGroupElement(name, layer); - emit requireRefreshElementWidget(); + emit triggerCentralRefresh(); } } }); @@ -119,9 +124,5 @@ void LayerTreeWidget::onRenameEvent() } void LayerTreeWidget::refresh() { - // if(this->root!=nullptr) - //{ - // this->clear(); - // this->addTopLevelItem(this->root->getQTreeItem()); - //} + this->root->refreshTreeItem(); } \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/RightBar/LayerTreeWidget.h b/ArchitectureColoredPainting/src/Editor/RightBar/LayerTreeWidget.h index e7472dd..a7f4c93 100644 --- a/ArchitectureColoredPainting/src/Editor/RightBar/LayerTreeWidget.h +++ b/ArchitectureColoredPainting/src/Editor/RightBar/LayerTreeWidget.h @@ -21,6 +21,7 @@ class LayerTreeWidget : public QTreeWidget // void onItemDoubleClicked(QTreeWidgetItem *item, int column = 0); signals: + void triggerCentralRefresh(); void displayLayerChange(LayerWrapper *); void requireRefreshPreview(); void requireRefreshElementWidget(); diff --git a/ArchitectureColoredPainting/src/Editor/util/EncodeUtil.hpp b/ArchitectureColoredPainting/src/Editor/util/EncodeUtil.hpp new file mode 100644 index 0000000..d9a5d01 --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/util/EncodeUtil.hpp @@ -0,0 +1,25 @@ +#pragma once +#include + +namespace EncodeUtil +{ +#include + template + QString toBase64(const std::vector& vec) + { + QByteArray ba; + ba.resize(vec.size() * sizeof(S)); + memcpy_s(ba.data(), ba.size(), vec.data(), vec.size() * sizeof(S)); + return ba.toBase64(); + } + + template + std::vector fromBase64(const QString& str) + { + QByteArray ba = QByteArray::fromBase64(str.toUtf8()); + std::vector vec; + vec.resize(ba.size() / sizeof(T)); + memcpy_s(vec.data(), vec.size() * sizeof(T), ba.data(), ba.size()); + return vec; + } +} diff --git a/ArchitectureColoredPainting/src/Editor/util/JsonUtil.hpp b/ArchitectureColoredPainting/src/Editor/util/JsonUtil.hpp new file mode 100644 index 0000000..e393f38 --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/util/JsonUtil.hpp @@ -0,0 +1,14 @@ +#pragma once +#include + +namespace JsonUtil +{ +#include + template + QJsonArray toQJsonArray(const std::vector& vec) + { + QJsonArray array; + std::copy(vec.begin(), vec.end(), std::back_inserter(array)); + return array; + } +} diff --git a/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.cpp b/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.cpp index f679dc2..104abb6 100644 --- a/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.cpp +++ b/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.cpp @@ -15,7 +15,7 @@ vector> PainterPathUtil::transformToLines(const QPainterPath& pai for (int elementIndex = 0; elementIndex < painterPath.elementCount(); elementIndex++) { auto element = painterPath.elementAt(elementIndex); point = element; - qDebug() << element; + //qDebug() << element; if (element.isMoveTo()) { if (line.size() >= 2) { lines.push_back(line); @@ -43,7 +43,7 @@ vector> PainterPathUtil::transformToLines(const QPainterPath& pai return lines; } -QPainterPath PainterPathUtil::monotonization(QPainterPath& painterPath) { +QPainterPath PainterPathUtil::monotonization(const QPainterPath& painterPath) { QPainterPath resPath; vector > lines = transformToLines(painterPath); vector> linePtrVector; @@ -62,15 +62,15 @@ QPainterPath PainterPathUtil::monotonization(QPainterPath& painterPath) { return resPath; } -std::pair PainterPathUtil::normalized(const QPainterPath& path) +std::pair PainterPathUtil::normalized(const QPainterPath& path, float width) { auto rect = path.boundingRect(); - return { (QTransform::fromTranslate(-rect.center().x(), -rect.center().y()) * QTransform::fromScale(1 / (rect.width() / 1.999999), -1 / (rect.height() / 1.999999))).map(path), + return { (QTransform::fromTranslate(-rect.center().x(), -rect.center().y()) * QTransform::fromScale(1 / (rect.width() / 1.999999), -1 / (rect.height() / 1.999999)) * QTransform::fromScale(1 / (1 + width),1 / (1 + width))).map(path), rect.width() / rect.height() }; } -std::pair>, float> PainterPathUtil::toNormalizedLines(const QPainterPath& path) +std::pair>, float> PainterPathUtil::toNormalizedLines(const QPainterPath& path, float width) { - auto [p, ratio] = normalized(path); + auto [p, ratio] = normalized(path, width); return { transformToLines(p), ratio }; } diff --git a/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.h b/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.h index 0e0d579..196142c 100644 --- a/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.h +++ b/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.h @@ -7,8 +7,8 @@ class PainterPathUtil { public: static std::vector> transformToLines(const QPainterPath& painterPath); - static QPainterPath monotonization(QPainterPath& painterPath); - static std::pair normalized(const QPainterPath& path); - static std::pair>, float> toNormalizedLines(const QPainterPath& path); + static QPainterPath monotonization(const QPainterPath& painterPath); + static std::pair normalized(const QPainterPath& path, float width = 0); + static std::pair>, float> toNormalizedLines(const QPainterPath& path, float width = 0); }; diff --git a/ArchitectureColoredPainting/src/Editor/util/PaintingUtil.cpp b/ArchitectureColoredPainting/src/Editor/util/PaintingUtil.cpp index 3faa60e..39daca5 100644 --- a/ArchitectureColoredPainting/src/Editor/util/PaintingUtil.cpp +++ b/ArchitectureColoredPainting/src/Editor/util/PaintingUtil.cpp @@ -2,6 +2,7 @@ #include #include #include "PainterPathUtil.h" +#include using Renderer::Painting; using Renderer::Element; @@ -9,6 +10,10 @@ using Renderer::ElementTransform; using Renderer::BaseElement; using glm::bvec2; using std::max; +using std::shared_ptr; +using std::make_shared; +using std::min; +using std::queue; const double PaintingUtil::pi = acos(-1); @@ -29,7 +34,6 @@ QJsonObject PaintingUtil::readJsonFile(QString jsonFilePath) { Painting PaintingUtil::transfromToPainting(QString jsonFilePath) { Painting painting; - QTransform transform; glm::bvec2 flip(0, 0); QJsonObject jsonObj = readJsonFile(jsonFilePath); qDebug() << jsonObj; @@ -61,13 +65,8 @@ Painting PaintingUtil::transfromToPainting(QString jsonFilePath) { FolderLayerWrapper* PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, double& maxLineWidth, QTransform& transform, Painting& painting) { LeafLayerWrapper* leafLayer = dynamic_cast(nowLayer); - QRectF transBound = transform.map(pixelPath.getPainterPath()).boundingRect(); + transform = nowLayer->property.transform * transform; - transform.translate(nowLayer->property.offset.x(), nowLayer->property.offset.y()) - .translate(-centerX, -centerY) - .rotate(nowLayer->property.rotation) - .scale(nowLayer->property.scale.x(), nowLayer->property.scale.y()) - .translate(centerX, centerY); if (leafLayer != nullptr) { GroupElement* wrapperElement = dynamic_cast(leafLayer->wrappedElement); @@ -85,6 +84,7 @@ FolderLayerWrapper* PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, dou //ElementTransform elementTrans; //element.ratio = bound.width() / bound.height(); // transform to initial painterPath + // transfrom to -1 1 QTransform trans; trans.translate(-centerX, -centerY) .scale(1 / nowLayer->property.scale.x(), 1 / nowLayer->property.scale.y()) @@ -147,28 +147,37 @@ FolderLayerWrapper* PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, dou (2 * bound.center().x() - screenSize.width()) / screenSize.width(), (2 * bound.center().y() - screenSize.height()) / screenSize.height() ); + qDebug() << elementTrans.center.x << elementTrans.center.y; decomposeTransform(transform, elementTrans.rotation, elementTrans.scale); + elementTrans.scale = glm::vec2( + bound.width() * 2 / screenSize.width(), + bound.height() * 2 / screenSize.height() + ); elementTrans.flip = glm::bvec2( nowLayer->property.flipHorizontally, nowLayer->property.flipVertically ); - return; + qDebug() << elementTrans.scale.x << elementTrans.scale.y; + painting.addElement(element, elementTrans);*/ + return nullptr; } FolderLayerWrapper* folderLayer = dynamic_cast(nowLayer); - for (auto sonLayer : folderLayer->children) { - traverseLayTree(sonLayer.get(), transform, flip, painting); - } - return; + return folderLayer; } void PaintingUtil::decomposeTransform(QTransform trans, float& angle, glm::vec2& scale) { - trans.translate(-trans.dx(), -trans.dy()); + //qDebug() << trans; + trans.setMatrix( + trans.m11(), trans.m12(), trans.m13(), + trans.m21(), trans.m22(), trans.m23(), + 0, 0, 1); + //qDebug() << trans.dx() << trans.dy(); int count = 0; double norm = 0, n = 0; QTransform R = trans, Rit, Rnext; do { ++count; - Rit = R.inverted(); + Rit = R.transposed().inverted(); Rnext.setMatrix( (R.m11() + Rit.m11()) / 2, (R.m12() + Rit.m12()) / 2, @@ -195,9 +204,16 @@ void PaintingUtil::decomposeTransform(QTransform trans, float& angle, glm::vec2& + fabs(R.m33() - Rnext.m33())); R = Rnext; } while (count < 100 && norm > 0.0001); - angle = acos(R.m11()); - R = R.inverted() * trans; - scale = glm::vec2(R.m11(), R.m22()); + double cosValue = max(-1.0, min(R.m11(), 1.0)); + double sinValue = max(-1.0, min(R.m12(), 1.0)); + angle = acos(cosValue) * 180 / pi; + if (sinValue < 0) { + angle = 360 - angle; + } + qDebug() << angle; + //R = R.inverted() * trans; + //scale = glm::vec2(R.m11(), R.m22()); + //qDebug() << scale.x << scale.y; return; } diff --git a/ArchitectureColoredPainting/src/Editor/util/PaintingUtil.h b/ArchitectureColoredPainting/src/Editor/util/PaintingUtil.h index 56977e1..9c2ebc1 100644 --- a/ArchitectureColoredPainting/src/Editor/util/PaintingUtil.h +++ b/ArchitectureColoredPainting/src/Editor/util/PaintingUtil.h @@ -5,11 +5,13 @@ class PaintingUtil { private: + static const double pi; static QJsonObject readJsonFile(QString jsonFilePath); static FolderLayerWrapper* handleLayerWrapper(LayerWrapper* nowLayer, double& maxLineWidth, QTransform& transform, Renderer::Painting& painting); static double getMaxLineWidth(LayerWrapper* root); public: static Renderer::Painting transfromToPainting(QString jsonFilePath); + static void decomposeTransform(QTransform trans, float& angle, glm::vec2& scale); }; diff --git a/ArchitectureColoredPainting/src/Renderer/Mesh.h b/ArchitectureColoredPainting/src/Renderer/Mesh.h index ec452fd..35348bc 100644 --- a/ArchitectureColoredPainting/src/Renderer/Mesh.h +++ b/ArchitectureColoredPainting/src/Renderer/Mesh.h @@ -18,7 +18,7 @@ namespace Renderer glm::vec3 Position; glm::vec3 Normal; glm::vec2 TexCoords; - Vertex(const aiVector3D& position, const aiVector3D& Normal, const aiVector3D& TexCoords); + Vertex(const aiVector3D& position, const aiVector3D& normal, const aiVector3D& texCoords); }; struct Texture diff --git a/ArchitectureColoredPainting/src/Renderer/Model.cpp b/ArchitectureColoredPainting/src/Renderer/Model.cpp index 7cd638b..d2dc3d2 100644 --- a/ArchitectureColoredPainting/src/Renderer/Model.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Model.cpp @@ -14,6 +14,9 @@ #include "../Editor/util/PainterPathUtil.h" #include "../Editor/util/SvgFileLoader.h" #include +#include +#include "Painting/MaterialStyleStroke.h" +#include using namespace Renderer; using std::vector; @@ -30,14 +33,12 @@ Model::Model(QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram, } void Model::draw() { - //shaderProgram->bind(); for (auto& mesh : meshes) { mesh->draw(); } } void Model::drawShadow() { - //shaderProgram->bind(); for (auto& mesh : meshes) { mesh->drawShadow(); } @@ -50,10 +51,10 @@ void Renderer::Model::loadModel(QString path) directory = modelFile.dir(); Assimp::Importer importer; - const aiScene* scene = importer.ReadFile(modelFile.absoluteFilePath().toUtf8(), aiProcess_Triangulate /*| aiProcess_FlipUVs*/); + const aiScene* scene = importer.ReadFile(modelFile.absoluteFilePath().toUtf8(), aiProcess_Triangulate); if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { - qCritical() << "ERROR::ASSIMP::" << importer.GetErrorString() << endl; + qCritical() << "ERROR::ASSIMP::" << importer.GetErrorString(); return; } qDebug() << modelFile.absoluteFilePath() << "Loaded Successfully"; @@ -61,9 +62,11 @@ void Renderer::Model::loadModel(QString path) qDebug() << "NumMaterials: " << scene->mNumMaterials; qDebug() << "NumTextures: " << scene->mNumTextures; + unloadModel(); + + if (QFile paintingConfigFile(directory.filePath(name + ".txt")); paintingConfigFile.open(QFile::ReadOnly | QIODevice::Text)) { - paintingMap.clear(); QTextStream stream(&paintingConfigFile); while (!stream.atEnd()) { @@ -80,23 +83,36 @@ void Renderer::Model::loadModel(QString path) qWarning() << "Painting Config Not Found!"; } + + aiMatrix4x4 transform; + aiMatrix4x4::Scaling(aiVector3D(1 / 0.008), transform); + processNode(scene->mRootNode, scene, transform * scene->mRootNode->mTransformation); + AABB.clear(); + AABB.emplace_back(minX, minY, minZ); + AABB.emplace_back(minX, minY, maxZ); + AABB.emplace_back(minX, maxY, minZ); + AABB.emplace_back(minX, maxY, maxZ); + AABB.emplace_back(maxX, minY, minZ); + AABB.emplace_back(maxX, minY, maxZ); + AABB.emplace_back(maxX, maxY, minZ); + AABB.emplace_back(maxX, maxY, maxZ); +} + +void Renderer::Model::unloadModel() +{ + paintingMap.clear(); + for (auto& i : paintingLoaded) + vtManager->deleteVirtualTexture(i.second); + paintingLoaded.clear(); + texturesLoaded.clear(); + meshes.clear(); + minX = std::numeric_limits::max(); maxX = std::numeric_limits::min(); minY = std::numeric_limits::max(); maxY = std::numeric_limits::min(); minZ = std::numeric_limits::max(); maxZ = std::numeric_limits::min(); - aiMatrix4x4 transform; - aiMatrix4x4::Scaling(aiVector3D(1 / 0.008), transform); - processNode(scene->mRootNode, scene, transform * scene->mRootNode->mTransformation); - AABB.push_back(QVector3D(minX, minY, minZ)); - AABB.push_back(QVector3D(minX, minY, maxZ)); - AABB.push_back(QVector3D(minX, maxY, minZ)); - AABB.push_back(QVector3D(minX, maxY, maxZ)); - AABB.push_back(QVector3D(maxX, minY, minZ)); - AABB.push_back(QVector3D(maxX, minY, maxZ)); - AABB.push_back(QVector3D(maxX, maxY, minZ)); - AABB.push_back(QVector3D(maxX, maxY, maxZ)); } void Model::processNode(aiNode* node, const aiScene* scene, aiMatrix4x4 mat4) @@ -156,14 +172,13 @@ std::unique_ptr Model::processMesh(aiMesh* mesh, const aiScene* scene, for (auto& v : vertices) { - //qDebug() << v.TexCoords.x << v.TexCoords.y; v.TexCoords = (v.TexCoords - leftBottom) / (rightTop - leftBottom); //qDebug() << v.TexCoords.x << v.TexCoords.y; } - + mesh->vertices = vertices; mesh->indices = indices; - + mesh->paintingId = loadPainting(paintingPath); auto& handle = vtManager->getPaintingHandle(mesh->paintingId); mesh->textureBasecolor = handle.baseColor; @@ -229,6 +244,13 @@ GLuint Renderer::Model::loadPainting(std::string path) return iter->second; Painting painting; + if (auto file = QFileInfo(QString(path.c_str())); file.isFile()) + painting = PaintingUtil::transfromToPainting(file.path()); + else + { + qDebug() << path.c_str() << "Not Found, Using Default Painting"; + vector, float>> contours; + Painting painting; //if (auto file = QFileInfo(QString(path.c_str())); file.isFile()) if(auto file = QFileInfo(QString("../test.json")); file.isFile()) painting = PaintingUtil::transfromToPainting("../test.json"); @@ -237,51 +259,122 @@ GLuint Renderer::Model::loadPainting(std::string path) qDebug() << path.c_str() << "Not Found, Using Default Painting"; vector, float>> contours; - QPainterPath painterPaths[3]; - QQuickSvgParser::parsePathDataFast("M100,100C-.5,100,0,100.5,0,0L40,.07C40,59.5,39.5,60,100,60Z", - painterPaths[0]); - if (!SvgFileLoader().loadSvgFile("../svg/2.svg", painterPaths[1])) - qCritical() << "load error"; - /*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", - painterPaths[1]);*/ - QQuickSvgParser::parsePathDataFast("M377,459.61a11.26,11.26,0,0,1,11.27-11.27H696.12a11.27,11.27,0,0,0,11-8.62A359.84,359.84,0,0,0,708,280.56a11.26,11.26,0,0,0-11-8.73H388.27A11.26,11.26,0,0,1,377,260.57h0a11.26,11.26,0,0,1,11.27-11.26H683.71A11.32,11.32,0,0,0,694.28,234C649.8,113.69,542.57,23.85,412.3,4.12a11.22,11.22,0,0,0-12.76,11.17v158.9a11.26,11.26,0,0,0,11.26,11.27H583.12a11.32,11.32,0,0,0,9.26-17.75c-31.67-46.59-78.51-75.2-109.11-90.07a11.25,11.25,0,0,0-16.13,10.17V115.2a11.24,11.24,0,0,0,6.22,10.07l7.51,3.76a11.28,11.28,0,0,1,5,15.12h0a11.27,11.27,0,0,1-15.11,5l-20-10a11.27,11.27,0,0,1-6.22-10.07V54a11.27,11.27,0,0,1,14.62-10.75c5.11,1.59,125.66,40.35,172.24,149A11.27,11.27,0,0,1,621.11,208H388.27A11.26,11.26,0,0,1,377,196.73V11.36A11.32,11.32,0,0,0,365.89.08C363.34,0,360.79,0,358.22,0s-5.11,0-7.66.08a11.32,11.32,0,0,0-11.11,11.28V196.74A11.26,11.26,0,0,1,328.18,208H95.35A11.27,11.27,0,0,1,85,192.3c46.57-108.67,167.12-147.42,172.23-149A11.26,11.26,0,0,1,271.86,54v75.11a11.25,11.25,0,0,1-6.23,10.07l-20,10a11.27,11.27,0,0,1-15.11-5h0a11.26,11.26,0,0,1,5-15.11l7.52-3.76a11.27,11.27,0,0,0,6.22-10.07V87.82a11.25,11.25,0,0,0-16.14-10.16c-30.6,14.87-77.45,43.48-109.1,90.07a11.3,11.3,0,0,0,9.25,17.74H305.66a11.26,11.26,0,0,0,11.27-11.26V15.31A11.22,11.22,0,0,0,304.17,4.14C173.88,23.86,66.66,113.71,22.17,234a11.32,11.32,0,0,0,10.56,15.29H328.18a11.26,11.26,0,0,1,11.27,11.26v0a11.26,11.26,0,0,1-11.27,11.26H19.52a11.26,11.26,0,0,0-11,8.72,359.84,359.84,0,0,0,.83,159.16,11.26,11.26,0,0,0,11,8.61H328.18a11.26,11.26,0,0,1,11.27,11.27h0a11.26,11.26,0,0,1-11.27,11.26h-294a11.32,11.32,0,0,0-10.53,15.4C69,604.65,175.3,692.78,304.16,712.3a11.21,11.21,0,0,0,12.76-11.16V542.22A11.26,11.26,0,0,0,305.66,531h-166c-9.53,0-14.89,11.22-8.69,18.47,34.09,39.77,74.45,65.66,101.77,80.18a11.25,11.25,0,0,0,16.53-10V591a11.26,11.26,0,0,1,11.26-11.26h0A11.26,11.26,0,0,1,271.85,591v63.85A11.27,11.27,0,0,1,256.8,665.5c-4.45-1.59-109.58-40-171-139.9a11.27,11.27,0,0,1,9.59-17.17H328.18a11.26,11.26,0,0,1,11.27,11.26V705.08a11.32,11.32,0,0,0,11.11,11.28q3.82.07,7.66.08c2.57,0,5.12,0,7.67-.08A11.32,11.32,0,0,0,377,705.08V519.69a11.25,11.25,0,0,1,11.27-11.26H621.1a11.26,11.26,0,0,1,9.59,17.16c-61.46,99.87-166.59,138.3-171,139.9a11.27,11.27,0,0,1-15-10.61V591a11.26,11.26,0,0,1,11.26-11.26h0A11.26,11.26,0,0,1,467.14,591v28.6a11.25,11.25,0,0,0,16.53,10c27.33-14.53,67.68-40.42,101.77-80.19,6.2-7.23.85-18.46-8.69-18.46h-166a11.26,11.26,0,0,0-11.26,11.26V701.12a11.21,11.21,0,0,0,12.76,11.17c128.86-19.51,235.14-107.66,280.48-226a11.33,11.33,0,0,0-10.53-15.41h-294A11.25,11.25,0,0,1,377,459.61ZM35.27,399.53V316.9a11.26,11.26,0,0,1,11.27-11.26H669.92a11.25,11.25,0,0,1,11.26,11.26v82.63a11.25,11.25,0,0,1-11.26,11.26H46.54a11.27,11.27,0,0,1-11.27-11.26Z", - painterPaths[2]); + QPainterPath painterPaths[3]; + QQuickSvgParser::parsePathDataFast("M100,100C-.5,100,0,100.5,0,0L40,.07C40,59.5,39.5,60,100,60Z", + painterPaths[0]); + if (!SvgFileLoader().loadSvgFile("../svg/2.svg", painterPaths[1])) + qCritical() << "load error"; + /*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", + painterPaths[1]);*/ + QQuickSvgParser::parsePathDataFast("M377,459.61a11.26,11.26,0,0,1,11.27-11.27H696.12a11.27,11.27,0,0,0,11-8.62A359.84,359.84,0,0,0,708,280.56a11.26,11.26,0,0,0-11-8.73H388.27A11.26,11.26,0,0,1,377,260.57h0a11.26,11.26,0,0,1,11.27-11.26H683.71A11.32,11.32,0,0,0,694.28,234C649.8,113.69,542.57,23.85,412.3,4.12a11.22,11.22,0,0,0-12.76,11.17v158.9a11.26,11.26,0,0,0,11.26,11.27H583.12a11.32,11.32,0,0,0,9.26-17.75c-31.67-46.59-78.51-75.2-109.11-90.07a11.25,11.25,0,0,0-16.13,10.17V115.2a11.24,11.24,0,0,0,6.22,10.07l7.51,3.76a11.28,11.28,0,0,1,5,15.12h0a11.27,11.27,0,0,1-15.11,5l-20-10a11.27,11.27,0,0,1-6.22-10.07V54a11.27,11.27,0,0,1,14.62-10.75c5.11,1.59,125.66,40.35,172.24,149A11.27,11.27,0,0,1,621.11,208H388.27A11.26,11.26,0,0,1,377,196.73V11.36A11.32,11.32,0,0,0,365.89.08C363.34,0,360.79,0,358.22,0s-5.11,0-7.66.08a11.32,11.32,0,0,0-11.11,11.28V196.74A11.26,11.26,0,0,1,328.18,208H95.35A11.27,11.27,0,0,1,85,192.3c46.57-108.67,167.12-147.42,172.23-149A11.26,11.26,0,0,1,271.86,54v75.11a11.25,11.25,0,0,1-6.23,10.07l-20,10a11.27,11.27,0,0,1-15.11-5h0a11.26,11.26,0,0,1,5-15.11l7.52-3.76a11.27,11.27,0,0,0,6.22-10.07V87.82a11.25,11.25,0,0,0-16.14-10.16c-30.6,14.87-77.45,43.48-109.1,90.07a11.3,11.3,0,0,0,9.25,17.74H305.66a11.26,11.26,0,0,0,11.27-11.26V15.31A11.22,11.22,0,0,0,304.17,4.14C173.88,23.86,66.66,113.71,22.17,234a11.32,11.32,0,0,0,10.56,15.29H328.18a11.26,11.26,0,0,1,11.27,11.26v0a11.26,11.26,0,0,1-11.27,11.26H19.52a11.26,11.26,0,0,0-11,8.72,359.84,359.84,0,0,0,.83,159.16,11.26,11.26,0,0,0,11,8.61H328.18a11.26,11.26,0,0,1,11.27,11.27h0a11.26,11.26,0,0,1-11.27,11.26h-294a11.32,11.32,0,0,0-10.53,15.4C69,604.65,175.3,692.78,304.16,712.3a11.21,11.21,0,0,0,12.76-11.16V542.22A11.26,11.26,0,0,0,305.66,531h-166c-9.53,0-14.89,11.22-8.69,18.47,34.09,39.77,74.45,65.66,101.77,80.18a11.25,11.25,0,0,0,16.53-10V591a11.26,11.26,0,0,1,11.26-11.26h0A11.26,11.26,0,0,1,271.85,591v63.85A11.27,11.27,0,0,1,256.8,665.5c-4.45-1.59-109.58-40-171-139.9a11.27,11.27,0,0,1,9.59-17.17H328.18a11.26,11.26,0,0,1,11.27,11.26V705.08a11.32,11.32,0,0,0,11.11,11.28q3.82.07,7.66.08c2.57,0,5.12,0,7.67-.08A11.32,11.32,0,0,0,377,705.08V519.69a11.25,11.25,0,0,1,11.27-11.26H621.1a11.26,11.26,0,0,1,9.59,17.16c-61.46,99.87-166.59,138.3-171,139.9a11.27,11.27,0,0,1-15-10.61V591a11.26,11.26,0,0,1,11.26-11.26h0A11.26,11.26,0,0,1,467.14,591v28.6a11.25,11.25,0,0,0,16.53,10c27.33-14.53,67.68-40.42,101.77-80.19,6.2-7.23.85-18.46-8.69-18.46h-166a11.26,11.26,0,0,0-11.26,11.26V701.12a11.21,11.21,0,0,0,12.76,11.17c128.86-19.51,235.14-107.66,280.48-226a11.33,11.33,0,0,0-10.53-15.41h-294A11.25,11.25,0,0,1,377,459.61ZM35.27,399.53V316.9a11.26,11.26,0,0,1,11.27-11.26H669.92a11.25,11.25,0,0,1,11.26,11.26v82.63a11.25,11.25,0,0,1-11.26,11.26H46.54a11.27,11.27,0,0,1-11.27-11.26Z", + painterPaths[2]); - for (auto& i : painterPaths) - { - auto [contour, ratio] = PainterPathUtil::toNormalizedLines(i); - contours.emplace_back(std::make_shared(contour), ratio); - } - - vector> style = { - std::make_shared(), - std::make_shared(0.06), - std::make_shared(0.06) - }; - - vector> element = { - std::make_shared(Element{ contours[0].first, style[0], contours[0].second}), - std::make_shared(Element{ contours[1].first, style[2], contours[1].second}), - std::make_shared(Element{ contours[2].first, style[1], contours[2].second}), - }; - Painting painting; - - if (path == "0.json") - { - painting.addElement(*element[0], ElementTransform{ glm::vec2(-0.45,-0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 }); - //painting.addElement(*element[1], ElementTransform{ glm::vec2(-0.45, 0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 }); - painting.addElement(*element[2], ElementTransform{ glm::vec2(0.50,-0.45), glm::vec2(0.6,0.7) / 2.f, 0, glm::bvec2(false), 0 }); - } - else - { - for (int i = 0; i < 1000; i++) + for (auto& i : painterPaths) { - float x = (float)rand() / RAND_MAX * 2 - 1; - float y = (float)rand() / RAND_MAX * 2 - 1; - painting.addElement(*element[i % 3], ElementTransform{ glm::vec2(x,y), glm::vec2(0.025), (float)rand() / RAND_MAX * 360, glm::bvec2(false), 0 }); + auto [contour, ratio] = PainterPathUtil::toNormalizedLines(i); + contours.emplace_back(std::make_shared(contour), ratio); + } + + vector> style = { + std::make_shared(), + std::make_shared(0.02), + std::make_shared(0.2) + }; + + vector> element = { + std::make_shared(Element{ contours[0].first, style[0], contours[0].second}), + std::make_shared(Element{ contours[1].first, style[2], contours[1].second}), + std::make_shared(Element{ contours[2].first, style[0], contours[2].second}), + }; + + if (path == "0.json") + { + //painting.addElement(*element[0], ElementTransform{ glm::vec2(-0.45,-0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 }); + //painting.addElement(*element[1], ElementTransform{ glm::vec2(-0.45, 0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 }); + //painting.addElement(*element[2], ElementTransform{ glm::vec2(0.50,-0.45), glm::vec2(0.6,0.7) / 2.f, 0, glm::bvec2(false), 0 }); + } + else if (path == "1.json") + { + //painting.backgroundColor = QColor(196, 81, 35); + float widths[] = { 0.22, 0.22 * 0.25 / 0.15, 0.22 * 0.25 / 0.15 }; + QPainterPath painterPaths[6]; + for (int i = 0; i < 6; i++) + if (!SvgFileLoader().loadSvgFile(QString(std::format("../svg/{}.svg", i + 1).c_str()), painterPaths[i])) + qCritical() << "load error"; + + vector, float>> contours; + for (int i = 0; i < 3; i++) + { + auto [contour, ratio] = PainterPathUtil::toNormalizedLines(painterPaths[i], widths[i]); + contours.emplace_back(std::make_shared(contour), ratio); + } + class StyleStrokeRadialGradient : public Renderer::ElementStyle + { + public: + float width; + StrokeType type; + StyleStrokeRadialGradient(float width, StrokeType type) :width(width), type(type) {}; + virtual std::vector toBaseStyles() const override + { + std::map materialMap = { + {0.09, Material{QColor(255,255,255),0,0.8}}, + {0.63, Material{QColor(165,176,207),0,0.8}}, + {1.00, Material{QColor(58,64,151),0,0.8}} + }; + return { BaseStyle(std::make_shared(), + std::make_shared(width, type, StrokeEndType::kFlat, + std::make_shared(materialMap, false))) }; + } + }; + vector> style = { + std::make_shared(widths[0], StrokeType::kLeftSide), + std::make_shared(widths[1], StrokeType::kRightSide), + std::make_shared(widths[2], StrokeType::kLeftSide), + }; + std::map materialMap = { + {0.09, Material{QColor(255,255,255),0,0.8}}, + {0.63, Material{QColor(165,176,207),0,0.8}}, + {1.00, Material{QColor(58,64,151),0,0.8}} + }; + vector> element = { + std::make_shared(contours[0].first, std::make_shared( + widths[0], StrokeType::kLeftSide, StrokeEndType::kFlat, std::make_shared(materialMap, false))), + std::make_shared(contours[1].first, std::make_shared( + widths[1], StrokeType::kRightSide, StrokeEndType::kFlat, std::make_shared(materialMap, false))), + std::make_shared(contours[2].first, std::make_shared( + widths[2], StrokeType::kLeftSide, StrokeEndType::kFlat, std::make_shared(materialMap, false))), + }; + + QTransform trans = QTransform::fromScale(0.3, 0.3).inverted(); + glm::mat3x2 mat(trans.m11(), trans.m12(), trans.m21(), trans.m22(), trans.m31(), trans.m32()); + + trans = QTransform::fromTranslate(0.5, 0).scale(0.3, 0.3).inverted(); + glm::mat3x2 mat2(trans.m11(), trans.m12(), trans.m21(), trans.m22(), trans.m31(), trans.m32()); + //mat = glm::mat3x2(11, 22, 33, 44, 55, 66); + //mat2 = glm::mat3x2(1, 2, 3, 4, 5, 6); + + qDebug() << std::format("\n{} {} {}\n {} {} {}\n{} {} {}", + trans.m11(), trans.m21(), trans.m31(), + trans.m12(), trans.m22(), trans.m32(), + trans.m13(), trans.m23(), trans.m33()).c_str(); + + //painting.addElement(*element[0], ElementTransform{ glm::vec4(-1,-1,1,1), mat, 0}); + painting.addElement(*element[1], ElementTransform{ glm::vec4(-1,-1,0,1), mat, 0 }); + painting.addElement(*element[2], ElementTransform{ glm::vec4(0,-1,1,1), mat2, 0 }); + //painting.addElement(*element[0], ElementTransform{ glm::vec2(-0.45,0.45), glm::vec2(0.25), 0, glm::bvec2(false), 0 }); + //painting.addElement(*element[1], ElementTransform{ glm::vec2(-0.535,0.33), glm::vec2(0.15), 0, glm::bvec2(false), 0 }); + //painting.addElement(*element[2], ElementTransform{ glm::vec2(-0.535,0.23), glm::vec2(0.15), 0, glm::bvec2(false), 0 }); + } + else + { + for (int i = 0; i < 1000; i++) + { + float x = (float)rand() / RAND_MAX * 2 - 1; + float y = (float)rand() / RAND_MAX * 2 - 1; + //painting.addElement(*element[i % 3], ElementTransform{ glm::vec2(x,y), glm::vec2(0.025), (float)rand() / RAND_MAX * 360, glm::bvec2(false), 0 }); + } } } - painting.generateBuffers(glFunc); auto index = vtManager->createVirtualTexture(painting); diff --git a/ArchitectureColoredPainting/src/Renderer/Model.h b/ArchitectureColoredPainting/src/Renderer/Model.h index d9a6bea..d8112f9 100644 --- a/ArchitectureColoredPainting/src/Renderer/Model.h +++ b/ArchitectureColoredPainting/src/Renderer/Model.h @@ -15,6 +15,7 @@ namespace Renderer void draw(); void drawShadow(); void loadModel(QString path); + void unloadModel(); Model(QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* paintingProgram, QOpenGLShaderProgram* shadowProgram, VirtualTextureManager* vtManager); private: QOpenGLContext* context = nullptr; @@ -25,8 +26,8 @@ namespace Renderer VirtualTextureManager* vtManager = nullptr; /** - * @param key BaseColor· - * @param value json·, + * @brief key BaseColor· \n + * value json·, */ std::unordered_map> paintingMap; std::unordered_map paintingLoaded; diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.cpp b/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.cpp index f1b2088..a22470c 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.cpp @@ -1,6 +1,43 @@ #include "BaseStyle.h" +#include "MaterialStyleFill.h" +#include "MaterialStyleStroke.h" using namespace Renderer; +using namespace glm; + +Renderer::Material::Material(const QColor& color, float metallic, float roughness) + : color(color) +{ + setMetallicF(metallic); + setRoughnessF(roughness); +} + +Renderer::Material::Material(const glm::vec4& color, const glm::vec2& metallicRoughness) + : color(QColor::fromRgbF(color.r, color.g, color.b, color.a)) +{ + setMetallicF(metallicRoughness.r); + setRoughnessF(metallicRoughness.g); +} + +float Renderer::Material::metallicF() const +{ + return metallic / 255.f; +} + +float Renderer::Material::roughnessF() const +{ + return roughness / 255.f; +} + +void Renderer::Material::setMetallicF(float metallic) +{ + this->metallic = metallic * 255; +} + +void Renderer::Material::setRoughnessF(float roughness) +{ + this->roughness = roughness * 255; +} bool Renderer::Material::operator==(const Material& m) const { @@ -9,5 +46,52 @@ bool Renderer::Material::operator==(const Material& m) const std::pair Renderer::Material::toVec() const { - return { glm::vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF()), glm::vec2(metallic, roughness)}; + return { glm::vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF()), glm::vec2(metallicF(), roughnessF())}; +} + +std::unique_ptr Renderer::MaterialStyle::decoded(const std::vector& encoded) +{ + if (encoded[0] <= 0) /// MaterialStyleFill + { + std::unique_ptr materiallFill; + glm::vec4 head = glm::unpackUnorm4x8(glm::floatBitsToUint(encoded[0])); + if (head.z == 0) + { + materiallFill = std::make_unique(Material(glm::unpackUnorm4x8(glm::floatBitsToUint(encoded[1])), glm::vec2(head.r, head.g))); + } + return std::make_unique(std::move(materiallFill)); + } + else /// MaterialStyleStroke + { + std::unique_ptr materialStroke; + uint headUint = floatBitsToUint(encoded[1]); + vec4 head = unpackUnorm4x8(headUint); + StrokeType strokeType = (StrokeType)floor(head.b * 10); + StrokeEndType endType = (StrokeEndType)(int(round(head.b * 100)) % 10); + switch (int(head.a * 100) % 10) + { + /// Plain + case 0: { + materialStroke = std::make_unique(Material(glm::unpackUnorm4x8(glm::floatBitsToUint(encoded[2])), glm::vec2(head.r, head.g))); + break; + } + /// RadialGradient + case 1: { + uint size = headUint % (1 << 15); + bool gradual = (headUint & (1 << 15)) != 0; + std::map materialMap; + for (uint i = 0; i < size; i++) + { + auto data = floatBitsToUint(encoded[2 + i * 2]); + auto level = unpackUnorm2x16(data).y; + auto color = unpackUnorm4x8(floatBitsToUint(encoded[3 + i * 2])); + auto metallicRoughness = unpackUnorm4x8(data); + materialMap.emplace(level, Material(color, glm::vec2(metallicRoughness.r, metallicRoughness.g))); + } + materialStroke = std::make_unique(materialMap, gradual); + break; + } + } + return std::make_unique(encoded[0], strokeType, endType, std::move(materialStroke)); + } } diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.h b/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.h index 6a1e7a8..051221f 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.h +++ b/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.h @@ -25,6 +25,7 @@ namespace Renderer virtual std::vector encoded() const = 0; virtual std::unique_ptr clone() const = 0; virtual bool operator==(const MaterialStyle&) const = 0; + static std::unique_ptr decoded(const std::vector& encoded); }; struct BaseStyle @@ -36,8 +37,16 @@ namespace Renderer struct Material { QColor color; - float metallic; - float roughness; + std::uint8_t metallic; + std::uint8_t roughness; + + Material() = default; + Material(const QColor& color, float metallic = 0, float roughness = 0.5); + Material(const glm::vec4& color, const glm::vec2& metallicRoughness); + float metallicF() const; + float roughnessF() const; + void setMetallicF(float metallic); + void setRoughnessF(float roughness); bool operator==(const Material&) const; std::pair toVec() const; }; diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/Element.cpp b/ArchitectureColoredPainting/src/Renderer/Painting/Element.cpp index 4f2dab0..ce01a11 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/Element.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Painting/Element.cpp @@ -1,16 +1,16 @@ #include "Element.h" -void Renderer::ElementTransform::applyTransformStyle(const TransformStyle& t) -{ - center += t.translation; - scale *= t.scale; - rotation += t.rotation; - flip ^= t.flip; -} - -Renderer::ElementTransform Renderer::ElementTransform::appliedTransformStyle(const TransformStyle& t) const -{ - ElementTransform result = *this; - result.applyTransformStyle(t); - return result; -} +//void Renderer::ElementTransform::applyTransformStyle(const TransformStyle& t) +//{ +// /*center += t.translation; +// scale *= t.scale; +// rotation += t.rotation; +// flip ^= t.flip;*/ +//} +// +//Renderer::ElementTransform Renderer::ElementTransform::appliedTransformStyle(const TransformStyle& t) const +//{ +// ElementTransform result = *this; +// result.applyTransformStyle(t); +// return result; +//} diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/Element.h b/ArchitectureColoredPainting/src/Renderer/Painting/Element.h index 2e5c892..c7655ed 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/Element.h +++ b/ArchitectureColoredPainting/src/Renderer/Painting/Element.h @@ -5,7 +5,7 @@ #include "Line.h" #include "ElementStyle.h" -namespace Renderer +namespace Renderer { using Contour = std::vector>; @@ -18,14 +18,15 @@ namespace Renderer struct ElementTransform { - glm::vec2 center; - //glm::vec2 size; - glm::vec2 scale; /// ڻ - float rotation; /// Ƕ - glm::bvec2 flip; + //glm::vec2 center; + //glm::vec2 scale; /// ڻ + //float rotation; /// Ƕ + //glm::bvec2 flip; + glm::vec4 bound; /// ΧУӰ任 + glm::mat3x2 transform; /// 任 GLuint zIndex; - - void applyTransformStyle(const TransformStyle& t); - ElementTransform appliedTransformStyle(const TransformStyle& t) const; + + //void applyTransformStyle(const TransformStyle& t); + //ElementTransform appliedTransformStyle(const TransformStyle& t) const; }; } \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleFill.cpp b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleFill.cpp index 318895c..ec29f9b 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleFill.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleFill.cpp @@ -3,7 +3,12 @@ using namespace Renderer; Renderer::FillPlain::FillPlain(QColor color, float metallic, float roughness) - : color(color), metallic(metallic), roughness(roughness) + : material(color, metallic, roughness) +{ +} + +Renderer::FillPlain::FillPlain(const Material& material) + : material(material) { } @@ -14,8 +19,9 @@ MaterialFillType Renderer::FillPlain::type() const std::vector Renderer::FillPlain::encoded() const { - return { glm::uintBitsToFloat(glm::packUnorm4x8(glm::vec4(metallic, roughness, 0, 1))), - glm::uintBitsToFloat(glm::packUnorm4x8(glm::vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF())))}; + auto pair = material.toVec(); + return { glm::uintBitsToFloat(glm::packUnorm4x8(glm::vec4(pair.second, 0, 1))), + glm::uintBitsToFloat(glm::packUnorm4x8(pair.first))}; } std::unique_ptr Renderer::FillPlain::clone() const @@ -26,9 +32,7 @@ std::unique_ptr Renderer::FillPlain::clone() const bool Renderer::FillPlain::operator==(const MaterialFill& m) const { return type() == m.type() - && color == static_cast(m).color - && metallic == static_cast(m).metallic - && roughness == static_cast(m).roughness; + && material == static_cast(m).material; } diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleFill.h b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleFill.h index e3c1001..1910c65 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleFill.h +++ b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleFill.h @@ -18,14 +18,13 @@ namespace Renderer { public: FillPlain(QColor color, float metallic, float roughness); + FillPlain(const Material& material); virtual MaterialFillType type() const override; virtual std::vector encoded() const override; virtual std::unique_ptr clone() const override; virtual bool operator==(const MaterialFill&) const override; - QColor color; - float metallic; - float roughness; + Material material; }; class MaterialStyleFill : public MaterialStyle diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.cpp b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.cpp index 340b767..0d72542 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.cpp @@ -25,6 +25,7 @@ std::unique_ptr Renderer::StrokePlain::clone() const std::vector Renderer::StrokePlain::encoded() const { + qDebug() << material.color; return { glm::uintBitsToFloat(glm::packUnorm4x8(glm::vec4(material.toVec().second, 0.f, 0.f))), glm::uintBitsToFloat(glm::packUnorm4x8(material.toVec().first)) }; } @@ -58,7 +59,7 @@ std::vector Renderer::StrokeRadialGradient::encoded() const v.y = pair.first; result.push_back(glm::uintBitsToFloat(glm::packUnorm2x16(v))); result.push_back(glm::uintBitsToFloat(glm::packUnorm4x8(color))); - qDebug() << pair.first; + //qDebug() << pair.first; } return result; } @@ -75,8 +76,8 @@ bool Renderer::StrokeRadialGradient::operator==(const MaterialStroke& m) const && materialMap == static_cast(m).materialMap; } -Renderer::MaterialStyleStroke::MaterialStyleStroke(float width, StrokeType strokeType, StrokeEndType endType, std::shared_ptr materialStroke) - : halfWidth(width/2), strokeType(strokeType), endType(endType), materialStroke(materialStroke) +Renderer::MaterialStyleStroke::MaterialStyleStroke(float halfWidth, StrokeType strokeType, StrokeEndType endType, std::shared_ptr materialStroke) + : halfWidth(halfWidth), strokeType(strokeType), endType(endType), materialStroke(materialStroke) { } @@ -99,12 +100,16 @@ std::vector Renderer::MaterialStyleStroke::encoded() const std::unique_ptr Renderer::MaterialStyleStroke::clone() const { - return std::make_unique(halfWidth*2, strokeType, endType, materialStroke->clone()); + return std::make_unique(halfWidth, strokeType, endType, materialStroke->clone()); } bool Renderer::MaterialStyleStroke::operator==(const MaterialStyle& m) const { - return type() == m.type() && *materialStroke == *static_cast(m).materialStroke; + return type() == m.type() + && halfWidth == static_cast(m).halfWidth + && strokeType == static_cast(m).strokeType + && endType == static_cast(m).endType + && *materialStroke == *static_cast(m).materialStroke; } float Renderer::MaterialStyleStroke::getHalfWidth() const diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.h b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.h index db5ddbf..29fd55b 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.h +++ b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.h @@ -43,7 +43,7 @@ namespace Renderer }; enum class StrokeType { kBothSides = 2, kLeftSide = 1, kRightSide = 0 }; - enum class StrokeEndType { kRound = 0, kFlat = 1 }; + enum class StrokeEndType { kRound = 0b00, kFlat = 0b11, kRoundFlat = 0b10, kFlatRound = 0b01 }; class MaterialStyleStroke : public MaterialStyle { @@ -54,7 +54,7 @@ namespace Renderer virtual std::unique_ptr clone() const override; virtual bool operator==(const MaterialStyle&) const override; float getHalfWidth() const; - //protected: + float halfWidth; StrokeType strokeType; StrokeEndType endType; diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/Painting.cpp b/ArchitectureColoredPainting/src/Renderer/Painting/Painting.cpp index 3d1fb2a..2fd6d73 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/Painting.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Painting/Painting.cpp @@ -9,7 +9,7 @@ using namespace Renderer; constexpr int kMaxLineCount = 20; -Painting::Painting() +Painting::Painting(QColor backgroundColor) : backgroundColor(backgroundColor) { } @@ -53,87 +53,40 @@ void Renderer::Painting::addElement(ElementWithTransform elementWithTransform) elementPool.insert({ element, 0 }); } elements.push_back(elementWithTransform); + elementTransformPool.emplace(elementWithTransform.transform.transform, 0); } -void Renderer::Painting::addElement(const Element& element, const ElementTransform& transform) +void Renderer::Painting::addElement(const BaseElement& element, const ElementTransform& transform) { - auto contour = element.contour; - auto it = elementStyleMap.find(element.style); - if (it == elementStyleMap.end()) - { - std::vector baseStyles; - for (auto& style : element.style->toBaseStyles()) - { - auto [iter, _] = styleSet.insert(style.material); - baseStyles.push_back(BaseStyle{ style.transform, *iter }); - } - it = elementStyleMap.insert({ element.style, baseStyles }).first; - } - for (int i = 0; i < it->second.size(); i++) - { - auto& style = it->second[i]; - ElementTransform trans = transform; - trans.applyTransformStyle(*style.transform); - trans.zIndex = trans.zIndex * 10 + i; - - addElement(ElementWithTransform{ BaseElement{element.contour, style.material, element.ratio}, trans }); - } + addElement({ element , transform }); } -Renderer::BaseTransform::BaseTransform(ElementTransform t) - : bound(glm::vec4(t.center - t.scale, t.center + t.scale)) - , rotation(t.rotation) - , flip(t.flip) - , zIndex(t.zIndex) +BvhTreeData Painting::encodeElementLeaf(const ElementWithTransform& e) { -} - -void Renderer::Painting::addElement(std::shared_ptr element, QVector4D bound, float rotation, int zIndex) -{ - -} - -BvhTreeData Painting::encodeElementLeaf(ElementWithTransform e) -{ - glm::vec4 bound; - switch (e.element.style->type()) - { - case MaterialStyleType::kStroke: - { - auto w = std::static_pointer_cast(e.element.style)->getHalfWidth(); - glm::vec2 size = e.element.ratio < 1 ? glm::vec2(e.element.ratio, 1) : glm::vec2(1, 1 / e.element.ratio); - glm::vec2 scale = size * e.transform.scale; - bound = glm::vec4(e.transform.center - scale, e.transform.center + scale); - break; - } - case MaterialStyleType::kFill: - { - bound = glm::vec4(e.transform.center - e.transform.scale, e.transform.center + e.transform.scale); - break; - } - } - //bound = glm::vec4(e.transform.center - e.transform.scale, e.transform.center + e.transform.scale); - GLuint rightSon = (GLuint)(glm::mod(e.transform.rotation, 360.f) / 360.f * (1 << 16)) - + (e.transform.flip.x << 16) + (e.transform.flip.y << 17) + (e.transform.zIndex << 18); - return BvhTreeData(QVector4D(bound.x, bound.y, bound.z, bound.w), elementPool[e.element], rightSon); + QVector4D bound(e.transform.bound.x, e.transform.bound.y, e.transform.bound.z, e.transform.bound.w); + GLuint rightSon = elementPool[e.element] + (e.transform.zIndex << 18); + return { bound, elementTransformPool[e.transform.transform], rightSon }; } void Painting::generateBuffers(QOpenGLFunctions_4_5_Core* glFunc) { qDebug() << "Element Count: " << elementPool.size(); - qDebug() << "Coutour Count: " << contourPool.size(); + qDebug() << "Contour Count: " << contourPool.size(); qDebug() << " Style Count: " << stylePool.size(); bvhChildren.clear(); bvhBounds.clear(); + elementTransform.clear(); elementOffsets.clear(); elementIndex.clear(); elementData.clear(); for (int index = 0; auto & i : elementPool) - { i.second = index++; - } + for (int index = 0; auto & i : elementTransformPool) + i.second = index++; + + std::vector rootBvhTreeData; for (auto& i : elements) @@ -162,27 +115,35 @@ void Painting::generateBuffers(QOpenGLFunctions_4_5_Core* glFunc) elementData.insert(elementData.end(), encodedStyle.begin(), encodedStyle.end()); } + for (auto & i : elementTransformPool) + elementTransform.emplace_back(i.first); + for (auto& i : elementPool) { //qDebug() <<"element:" << i.second; //std::shared_ptr contourBuffer = i.first.style->type() == MaterialStyleType::kStroke ? contourPool[i.first.contour].second : contourPool[i.first.contour].first; auto& contourBuffer = contourPool[{i.first.contour, i.first.style->type()}]; - elementOffsets.push_back({ contourBuffer.bvhOffset, stylePool[i.first.style], contourBuffer.pointsOffset, contourBuffer.linesOffset, glm::floatBitsToUint(i.first.ratio) }); + elementOffsets.push_back({ contourBuffer.bvhOffset, stylePool[i.first.style], contourBuffer.pointsOffset, contourBuffer.linesOffset }); //std::cout << std::format("{} {} {} {}\n", contourBuffer->bvhOffset, stylePool[i.first->style], contourBuffer->pointsOffset, contourBuffer->linesOffset); } - glFunc->glCreateBuffers(5, buffers.data()); + glFunc->glCreateBuffers(7, buffers.data()); GLuint& bvhSSBO = buffers[0]; GLuint& bvhBoundSSBO = buffers[1]; - GLuint& elementOffsetSSBO = buffers[2]; - GLuint& elementIndexSSBO = buffers[3]; - GLuint& elementDataSSBO = buffers[4]; + GLuint& elementTransformSSBO = buffers[2]; + GLuint& elementOffsetSSBO = buffers[3]; + GLuint& elementIndexSSBO = buffers[4]; + GLuint& elementDataSSBO = buffers[5]; + GLuint& backgroundColorUBO = buffers[6]; glFunc->glNamedBufferData(buffers[0], bvhChildren.size() * sizeof(bvhChildren[0]), bvhChildren.data(), GL_STATIC_READ); glFunc->glNamedBufferData(buffers[1], bvhBounds.size() * sizeof(bvhBounds[0]), bvhBounds.data(), GL_STATIC_READ); - glFunc->glNamedBufferData(buffers[2], elementOffsets.size() * sizeof(elementOffsets[0]), elementOffsets.data(), GL_STATIC_READ); - glFunc->glNamedBufferData(buffers[3], elementIndex.size() * sizeof(elementIndex[0]), elementIndex.data(), GL_STATIC_READ); - glFunc->glNamedBufferData(buffers[4], elementData.size() * sizeof(elementData[0]), elementData.data(), GL_STATIC_READ); + glFunc->glNamedBufferData(buffers[2], elementTransform.size() * sizeof(elementTransform[0]), elementTransform.data(), GL_STATIC_READ); + glFunc->glNamedBufferData(buffers[3], elementOffsets.size() * sizeof(elementOffsets[0]), elementOffsets.data(), GL_STATIC_READ); + glFunc->glNamedBufferData(buffers[4], elementIndex.size() * sizeof(elementIndex[0]), elementIndex.data(), GL_STATIC_READ); + glFunc->glNamedBufferData(buffers[5], elementData.size() * sizeof(elementData[0]), elementData.data(), GL_STATIC_READ); + glm::vec3 color(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF()); + glFunc->glNamedBufferData(buffers[6], sizeof(glm::vec3), &color, GL_STATIC_READ); } GLuint Renderer::Painting::getElementCount() @@ -220,3 +181,17 @@ bool Renderer::Painting::CompareMaterialStyle::operator()(const std::shared_ptr< else return left < right; } + +std::size_t Renderer::Painting::HashMat3x2::operator()(const glm::mat3x2& mat) const +{ + std::size_t result = 0; + constexpr long long P = 998244353; + long long base = 1; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 2; j++) + { + result += base * mat[i][j]; + base *= P; + } + return result; +} diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/Painting.h b/ArchitectureColoredPainting/src/Renderer/Painting/Painting.h index ab8f31a..cf18500 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/Painting.h +++ b/ArchitectureColoredPainting/src/Renderer/Painting/Painting.h @@ -15,24 +15,13 @@ namespace Renderer { std::shared_ptr contour; std::shared_ptr style; - float ratio; /// ߱ bool operator<(const BaseElement& e) const; }; - struct BaseTransform - { - glm::vec4 bound; - float rotation; - glm::bvec2 flip; - GLuint zIndex; - BaseTransform(ElementTransform t); - }; - struct ElementWithTransform { BaseElement element; ElementTransform transform; - //BaseTransform transform; }; struct ContourBuffer @@ -52,7 +41,6 @@ namespace Renderer GLuint styleOffset; GLuint pointsOffset; GLuint linesOffset; - GLuint ratio; }; class Painting @@ -60,15 +48,16 @@ namespace Renderer public: std::vector bvhChildren; std::vector bvhBounds; + std::vector elementTransform; std::vector elementOffsets; std::vector elementIndex; std::vector elementData; int paintingId = 0; - std::array buffers; + std::array buffers; + QColor backgroundColor; - Painting(); - void addElement(const Element& element, const ElementTransform& transform); - void addElement(std::shared_ptr element, QVector4D bound, float rotation, int zIndex); + Painting(QColor backgroundColor = Qt::white); + void addElement(const BaseElement& element, const ElementTransform& transform); void generateBuffers(QOpenGLFunctions_4_5_Core* glFunc); GLuint getElementCount(); private: @@ -76,14 +65,15 @@ namespace Renderer std::unordered_map, MaterialStyleType>, ContourBuffer, ContourHash> contourPool; struct CompareMaterialStyle { bool operator()(const std::shared_ptr&, const std::shared_ptr&) const; }; std::set, CompareMaterialStyle> styleSet; - std::unordered_map, std::vector> elementStyleMap; std::unordered_map, GLuint> stylePool; + struct HashMat3x2 { std::size_t operator()(const glm::mat3x2&) const; }; + std::unordered_map elementTransformPool; std::map elementPool; std::vector elements; void addElement(ElementWithTransform element); void insertContourBuffer(ContourBuffer& buffer); - BvhTreeData encodeElementLeaf(ElementWithTransform e); + BvhTreeData encodeElementLeaf(const ElementWithTransform& e); }; } diff --git a/ArchitectureColoredPainting/src/Renderer/Preview/ElementRenderer.cpp b/ArchitectureColoredPainting/src/Renderer/Preview/ElementRenderer.cpp index 756222b..259cc20 100644 --- a/ArchitectureColoredPainting/src/Renderer/Preview/ElementRenderer.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Preview/ElementRenderer.cpp @@ -10,6 +10,7 @@ #include "../Painting/Element.h" #include "../Painting/Painting.h" #include "../Painting/MaterialStyleStroke.h" +#include using namespace Renderer; std::vector generatePathBuffer(const QPainterPath& path) @@ -75,8 +76,10 @@ std::vector generateStyleBuffer(const std::vector& styles) styleBuffer.push_back(style.transform->scale.y); styleBuffer.push_back(style.transform->rotation); styleBuffer.push_back(glm::uintBitsToFloat(glm::packUnorm2x16(style.transform->flip))); + auto encoded = style.material->encoded(); styleBuffer.insert(styleBuffer.end(), encoded.begin(), encoded.end()); + //qDebug() << "style size" << styleBuffer.size(); } return styleBuffer; } @@ -85,13 +88,23 @@ QRectF calcBoundingRect(const QPainterPath& path, const std::vector& { QRectF bound = path.boundingRect(); - ElementTransform originTransform{ glm::vec2(bound.center().x(), bound.center().y()), glm::vec2(bound.width(), bound.height()), 0, glm::bvec2(false), 0 }; + //ElementTransform originTransform{ glm::vec2(bound.center().x(), bound.center().y()), glm::vec2(bound.width(), bound.height()), 0, glm::bvec2(false), 0 }; glm::vec2 leftTop(std::numeric_limits::max()), rightBottom(std::numeric_limits::min()); for (auto& baseStyle : styles) { - BaseTransform transform(originTransform.appliedTransformStyle(*baseStyle.transform)); + struct BaseTransform + { + glm::vec4 bound; + float rotation = 0; + glm::bvec2 flip = glm::bvec2(false); + GLuint zIndex = 0; + }; + //BaseTransform transform(originTransform.appliedTransformStyle(*baseStyle.transform)); + glm::vec2 center(bound.center().x(), bound.center().y()); + glm::vec2 scale(bound.width(), bound.height()); + BaseTransform transform{ glm::vec4(center - scale, center + scale) }; if (baseStyle.material->type() == MaterialStyleType::kStroke) { float halfWidth = std::static_pointer_cast(baseStyle.material)->getHalfWidth(); @@ -197,7 +210,8 @@ std::pair Renderer::ElementRenderer::drawElement(const QPainter std::unique_lock lock(drawMutex); draw.wait(lock, [&] {return drawFinished; }); drawFinished = false; - this->path = &path; + QPainterPath monotonized = PainterPathUtil::monotonization(path); + this->path = &monotonized; this->style = &style; this->pixelRatio = pixelRatio; needDraw = true; diff --git a/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp b/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp index 5e777a4..d0df771 100644 --- a/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp +++ b/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp @@ -12,9 +12,11 @@ Renderer::RendererWidget::RendererWidget(QWidget* parent) auto openAction = new QAction(QStringLiteral(""), menu); auto saveAction = new QAction(QStringLiteral(""), menu); auto testAction = new QAction(QStringLiteral(""), menu); + auto test2Action = new QAction(QStringLiteral("2"), menu); menu->addAction(openAction); menu->addAction(saveAction); menu->addAction(testAction); + menu->addAction(test2Action); ui.openButton->setHaloVisible(false); ui.openButton->setOverlayStyle(::Material::TintedOverlay); @@ -47,7 +49,9 @@ Renderer::RendererWidget::RendererWidget(QWidget* parent) QObject::connect(testAction, &QAction::triggered, [&] { ui.openGLWidget->setModel("Models/Sponza/Sponza.gltf"); }); - + QObject::connect(test2Action, &QAction::triggered, [&] { + ui.openGLWidget->setModel("E:\\3D Objects\\Gate\\Gate.gltf"); + }); ui.horizontalSlider->setValue(105); ui.horizontalSlider_2->setValue(80); ui.exposureSlider->setValue(60); diff --git a/ArchitectureColoredPainting/src/Renderer/RendererWidget.h b/ArchitectureColoredPainting/src/Renderer/RendererWidget.h index 258a173..d5a3725 100644 --- a/ArchitectureColoredPainting/src/Renderer/RendererWidget.h +++ b/ArchitectureColoredPainting/src/Renderer/RendererWidget.h @@ -13,6 +13,7 @@ namespace Renderer ~RendererWidget(); public slots: void currentTabChanged(int index); + private: Ui::RendererWidgetClass ui; }; diff --git a/ArchitectureColoredPainting/src/Renderer/VirtualTextureManager.cpp b/ArchitectureColoredPainting/src/Renderer/VirtualTextureManager.cpp index eeba398..55a2c01 100644 --- a/ArchitectureColoredPainting/src/Renderer/VirtualTextureManager.cpp +++ b/ArchitectureColoredPainting/src/Renderer/VirtualTextureManager.cpp @@ -76,7 +76,7 @@ Renderer::VirtualTextureManager::VirtualTextureManager(GladGLContext* glMain) pageIdBufferMutex.lock(); currentBuffer = 1 - currentBuffer; pageIdBufferMutex.unlock(); - + gl->BeginQuery(GL_TIME_ELAPSED, pageLoadTimeQuery); updatePages(pageIds); gl->EndQuery(GL_TIME_ELAPSED); @@ -124,8 +124,9 @@ std::uint16_t Renderer::VirtualTextureManager::createVirtualTexture(Painting pai glMain->TextureParameteri(metallicRoughness, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glMain->TextureStorage2D(metallicRoughness, levels, GL_RG8, GLsizei(textureSize), GLsizei(textureSize)); - for (int i = 0; i < 5; i++) + for (int i = 0; i < 6; i++) glMain->BindBufferBase(GL_SHADER_STORAGE_BUFFER, i, painting.buffers[i]); + glMain->BindBufferBase(GL_UNIFORM_BUFFER, 1, painting.buffers[6]); for (auto level = levels - 1; level < levels; ++level) { @@ -143,7 +144,7 @@ std::uint16_t Renderer::VirtualTextureManager::createVirtualTexture(Painting pai GL_TRUE); program.bind(); - glMain->Uniform2i(gl->GetUniformLocation(program.programId(), "pixelOffset"), static_cast(pageSize) * i, static_cast(pageSize) * j); + glMain->Uniform2i(0 /*pixelOffset*/, static_cast(pageSize) * i, static_cast(pageSize) * j); glMain->BindImageTexture(0, baseColor, level, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8); glMain->BindImageTexture(1, metallicRoughness, level, GL_FALSE, 0, GL_READ_WRITE, GL_RG8); glMain->DispatchCompute(pageSize / 8, pageSize / 8, 1); @@ -151,8 +152,28 @@ std::uint16_t Renderer::VirtualTextureManager::createVirtualTexture(Painting pai } } - paintings.emplace_back(baseColor, metallicRoughness, painting.buffers); - return paintings.size(); + if (const auto it = std::find_if(paintings.begin(), paintings.end(), [](const PaintingHandle& i) { return i.baseColor == 0; }); + it != paintings.end()) + { + *it = { baseColor, metallicRoughness, painting.buffers }; + return it - paintings.begin() + 1; + } + else + { + paintings.emplace_back(baseColor, metallicRoughness, painting.buffers); + return paintings.size(); + } + +} + +void Renderer::VirtualTextureManager::deleteVirtualTexture(std::uint16_t id) +{ + auto& painting = getPaintingHandle(id); + glMain->DeleteTextures(2, (GLuint*)&painting); + glMain->DeleteBuffers(7, painting.buffers.data()); + painting.baseColor = 0; + painting.metallicRoughness = 0; + painting.buffers.fill(0); } Renderer::PaintingHandle& Renderer::VirtualTextureManager::getPaintingHandle(std::uint16_t id) @@ -188,8 +209,9 @@ void Renderer::VirtualTextureManager::pageCommitmentById(const glm::u16vec2& pag if (commit) { program.bind(); - for (int i = 0; i < 5; i++) + for (int i = 0; i < 6; i++) gl->BindBufferBase(GL_SHADER_STORAGE_BUFFER, i, painting.buffers[i]); + glMain->BindBufferBase(GL_UNIFORM_BUFFER, 1, painting.buffers[6]); gl->Uniform2i(gl->GetUniformLocation(program.programId(), "pixelOffset"), static_cast(pageSize) * page.x, static_cast(pageSize) * page.y); gl->BindImageTexture(0, painting.baseColor, level, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8); gl->BindImageTexture(1, painting.metallicRoughness, level, GL_FALSE, 0, GL_READ_WRITE, GL_RG8); diff --git a/ArchitectureColoredPainting/src/Renderer/VirtualTextureManager.h b/ArchitectureColoredPainting/src/Renderer/VirtualTextureManager.h index e1d3a03..e5bbd24 100644 --- a/ArchitectureColoredPainting/src/Renderer/VirtualTextureManager.h +++ b/ArchitectureColoredPainting/src/Renderer/VirtualTextureManager.h @@ -15,7 +15,7 @@ namespace Renderer { GLuint baseColor; GLuint metallicRoughness; - std::array buffers; + std::array buffers; }; class VirtualTextureManager @@ -24,11 +24,12 @@ namespace Renderer VirtualTextureManager(GladGLContext* glMain); /** - * @brief - * @param painting + * @brief ʻ + * @param painting ʻ * @return id */ std::uint16_t createVirtualTexture(Painting painting); + void deleteVirtualTexture(std::uint16_t id); PaintingHandle& getPaintingHandle(std::uint16_t id); void tryUpdatePages(const std::vector& pageIds); diff --git a/ArchitectureColoredPainting/src/main.cpp b/ArchitectureColoredPainting/src/main.cpp index e43c971..5bf6bbe 100644 --- a/ArchitectureColoredPainting/src/main.cpp +++ b/ArchitectureColoredPainting/src/main.cpp @@ -5,6 +5,7 @@ #include #include #include "consoleapi2.h" +#include extern "C" { @@ -49,6 +50,10 @@ int main(int argc, char* argv[]) //QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); QApplication a(argc, argv); + Q_INIT_RESOURCE(resources); + QtMaterialTheme theme; + theme.setColor("primary1", QColor(0, 90, 158)); + QtMaterialStyle::instance().setTheme(&theme); //FramelessHelper::Core::setApplicationOSThemeAware(); FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur); //FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); diff --git a/UnitTest/ElementRendererTest.cpp b/UnitTest/ElementRendererTest.cpp index 2ff2e88..aba0474 100644 --- a/UnitTest/ElementRendererTest.cpp +++ b/UnitTest/ElementRendererTest.cpp @@ -3,23 +3,76 @@ #include #include "ElementRendererTest.h" #include "Renderer/Painting/ElementStyle.h" - +#include "Renderer/Painting/MaterialStyleFill.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace Renderer; namespace UnitTest { + void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); + + TEST_CLASS(ElementRendererFillTest) + { + private: + char* argv[1]; + int argc; + QPainterPath path; + public: + ElementRendererFillTest() :argv{ (char*)"" }, argc(1) + { + QQuickSvgParser::parsePathDataFast("M100,100C-.5,100,0,100.5,0,0L40,.07C40,59.5,39.5,60,100,60Z", + path); + QTransform transform; + //transform.scale(10, 10); + path = transform.map(path); + } + + TEST_METHOD_INITIALIZE(initialize) + { + qInstallMessageHandler(messageHandler); + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); + } + TEST_METHOD(TestFillPlain) + { + QApplication a(argc, argv); + class Style : public Renderer::ElementStyle + { + virtual std::vector toBaseStyles() const override + { + + return { BaseStyle(std::make_shared(), + std::make_shared( + std::make_shared(Material(QColor(255,255,0))))) }; + } + } style; + TestGLWidget w(style, path); + w.show(); + a.exec(); + } + + }; + TEST_CLASS(ElementRendererStokeTypeTest) { private: char* argv[1]; int argc; + QPainterPath path; public: - ElementRendererStokeTypeTest() :argv{ (char*)"" }, argc(1) {} + ElementRendererStokeTypeTest() :argv{ (char*)"" }, argc(1) + { + 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); + QTransform transform; + transform.scale(10, 10); + path = transform.map(path); + } TEST_METHOD_INITIALIZE(initialize) { + qInstallMessageHandler(messageHandler); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); @@ -37,11 +90,11 @@ namespace UnitTest {1.00, Material{QColor(58,64,151)}} }; return { BaseStyle(std::make_shared(), - std::make_shared(60, StrokeType::kBothSides, StrokeEndType::kRound, + std::make_shared(30, StrokeType::kBothSides, StrokeEndType::kRound, std::make_shared(materialMap, false))) }; } } style; - TestGLWidget w(style); + TestGLWidget w(style, path); w.show(); a.exec(); } @@ -58,11 +111,11 @@ namespace UnitTest {1.00, Material{QColor(58,64,151)}} }; return { BaseStyle(std::make_shared(), - std::make_shared(60, StrokeType::kBothSides, StrokeEndType::kFlat, + std::make_shared(30, StrokeType::kBothSides, StrokeEndType::kFlat, std::make_shared(materialMap, false))) }; } } style; - TestGLWidget w(style); + TestGLWidget w(style, path); w.show(); a.exec(); } @@ -79,11 +132,11 @@ namespace UnitTest {1.00, Material{QColor(58,64,151)}} }; return { BaseStyle(std::make_shared(), - std::make_shared(60, StrokeType::kLeftSide, StrokeEndType::kRound, + std::make_shared(30, StrokeType::kLeftSide, StrokeEndType::kRound, std::make_shared(materialMap, false))) }; } } style; - TestGLWidget w(style); + TestGLWidget w(style, path); w.show(); a.exec(); } @@ -100,11 +153,32 @@ namespace UnitTest {1.00, Material{QColor(58,64,151)}} }; return { BaseStyle(std::make_shared(), - std::make_shared(60, StrokeType::kLeftSide, StrokeEndType::kFlat, + std::make_shared(30, StrokeType::kLeftSide, StrokeEndType::kFlat, std::make_shared(materialMap, false))) }; } } style; - TestGLWidget w(style); + TestGLWidget w(style, path); + w.show(); + a.exec(); + } + TEST_METHOD(TestRightSideFlatRound) + { + QApplication a(argc, argv); + class StyleStrokeRadialGradient : public Renderer::ElementStyle + { + virtual std::vector toBaseStyles() const override + { + std::map 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(), + std::make_shared(80, StrokeType::kRightSide, StrokeEndType::kFlatRound, + std::make_shared(materialMap, false))) }; + } + } style; + TestGLWidget w(style, path); w.show(); a.exec(); } @@ -115,8 +189,15 @@ namespace UnitTest private: char* argv[1]; int argc; + QPainterPath path; public: - ElementRendererStokeMaterialTest() :argv{ (char*)"" }, argc(1) {} + ElementRendererStokeMaterialTest() :argv{ (char*)"" }, argc(1) + { + 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); + QTransform transform; + transform.scale(10, 10); + path = transform.map(path); + } TEST_METHOD(TestStrokePlain) { QApplication a(argc, argv); @@ -125,11 +206,11 @@ namespace UnitTest virtual std::vector toBaseStyles() const override { return { BaseStyle(std::make_shared(), - std::make_shared(60, StrokeType::kBothSides, StrokeEndType::kRound, + std::make_shared(30, StrokeType::kBothSides, StrokeEndType::kRound, std::make_shared(QColor(255,255,255),1,1))) }; } } style; - TestGLWidget w(style); + TestGLWidget w(style, path); w.show(); a.exec(); } @@ -146,11 +227,11 @@ namespace UnitTest {1.00, Material{QColor(58,64,151)}} }; return { BaseStyle(std::make_shared(), - std::make_shared(60, StrokeType::kBothSides, StrokeEndType::kRound, + std::make_shared(30, StrokeType::kBothSides, StrokeEndType::kRound, std::make_shared(materialMap, false))) }; } } style; - TestGLWidget w(style); + TestGLWidget w(style, path); w.show(); a.exec(); } @@ -171,7 +252,7 @@ namespace UnitTest std::make_shared(materialMap, true))) }; } } style; - TestGLWidget w(style); + TestGLWidget w(style, path); w.show(); a.exec(); } diff --git a/UnitTest/ElementRendererTest.h b/UnitTest/ElementRendererTest.h index 9271821..d709bfe 100644 --- a/UnitTest/ElementRendererTest.h +++ b/UnitTest/ElementRendererTest.h @@ -16,29 +16,25 @@ namespace UnitTest private: Renderer::ElementRenderer& renderer; Renderer::ElementStyle& style; + QPainterPath& path; public: - TestGLWidget(Renderer::ElementStyle& style, QWidget* parent = nullptr) - : QOpenGLWidget(parent), renderer(*Renderer::ElementRenderer::instance()), style(style) {}; + TestGLWidget(Renderer::ElementStyle& style, QPainterPath& path, QWidget* parent = nullptr) + : QOpenGLWidget(parent), renderer(*Renderer::ElementRenderer::instance()), style(style), path(path) {}; void initializeGL() override { initializeOpenGLFunctions(); }; void paintGL() override { - glClearColor(219/255., 78/255., 32/255., 1.0); + glClearColor(219 / 255., 78 / 255., 32 / 255., 1.0); glClear(GL_COLOR_BUFFER_BIT); - QPainterPath 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(10, 10); - path = transform.map(path); + float pixelRatio = devicePixelRatioF(); auto [img, pos] = renderer.drawElement(path, style, pixelRatio); QPainter painter(this); painter.setRenderHint(QPainter::SmoothPixmapTransform); painter.setRenderHint(QPainter::HighQualityAntialiasing); - painter.drawImage(QRectF(QPointF(0, 0), img.size()/pixelRatio), img); + painter.drawImage(QRectF(QPointF(0, 0), img.size() / pixelRatio), img); }; void resizeGL(int w, int h) override {}; }; diff --git a/UnitTest/StyleTest.cpp b/UnitTest/StyleTest.cpp new file mode 100644 index 0000000..f3bf969 --- /dev/null +++ b/UnitTest/StyleTest.cpp @@ -0,0 +1,54 @@ +#include "CppUnitTest.h" +#include +#include +#include "Renderer/Painting/ElementStyle.h" +#include + + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; +using namespace Renderer; + +namespace UnitTest +{ + TEST_CLASS(StyleTest) + { + public: + TEST_METHOD(TestEncodeDecode) + { + std::map materialMap = { + {0.20, Material{QColor(255,255,255)}}, + {0.60, Material{QColor(165,176,207)}}, + {1.00, Material{QColor(58,64,151)}} + }; + auto style = std::make_unique( + 30, StrokeType::kBothSides, StrokeEndType::kRound, + std::make_shared(materialMap, false) + ); + /* auto style = std::make_shared( + 30, StrokeType::kBothSides, StrokeEndType::kRound, + std::make_shared(Material(QColor(255, 255, 255))) + );*/ + std::shared_ptr decoded = MaterialStyle::decoded(style->encoded()); + /* Assert::IsTrue(decoded->type() == MaterialStyleType::kStroke); + Assert::IsTrue(std::static_pointer_cast(decoded)->halfWidth == 30); + Assert::IsTrue(std::static_pointer_cast(decoded)->strokeType == StrokeType::kBothSides); + Assert::IsTrue(std::static_pointer_cast(decoded)->endType == StrokeEndType::kRound); + std::shared_ptr materialStroke = std::static_pointer_cast(decoded)->materialStroke; + Assert::IsTrue(materialStroke->type() == MaterialStrokeType::kPlain); + Material material = std::static_pointer_cast(materialStroke)->material; + Assert::IsTrue(material == Material(QColor(255, 255, 255))); + Logger::WriteMessage(std::format("({} {} {} {}), ({} {})\n", + material.color.red(), + material.color.green(), + material.color.blue(), + material.color.alpha(), + material.metallic, + material.roughness + ).c_str());*/ + + + Assert::IsTrue(*style == *decoded); + + } + }; +} \ No newline at end of file diff --git a/UnitTest/UnitTest.cpp b/UnitTest/UnitTest.cpp index 2d7d95a..25bcf67 100644 --- a/UnitTest/UnitTest.cpp +++ b/UnitTest/UnitTest.cpp @@ -7,6 +7,7 @@ #include "Renderer/Painting/CubicBezier.h" #include #include +#include using namespace Microsoft::VisualStudio::CppUnitTestFramework; @@ -119,4 +120,22 @@ namespace UnitTest a.exec(); } }; + TEST_CLASS(PaintingUtilTest) + { + TEST_METHOD(TransfromTest) + { + qInstallMessageHandler(messageHandler); + QPainterPath path; + path.addRect(0, 0, 20, 20); + QTransform trans; + qDebug() << path.boundingRect(); + //qDebug() << trans; + //qDebug() << acos(-0.707107); + glm::vec2 scale; + float rotate; + PaintingUtil::decomposeTransform(trans, rotate, scale); + qDebug() << rotate; + qDebug() << scale.x << scale.y; + } + }; } diff --git a/UnitTest/UnitTest.vcxproj b/UnitTest/UnitTest.vcxproj index 402f061..3c5c76d 100644 --- a/UnitTest/UnitTest.vcxproj +++ b/UnitTest/UnitTest.vcxproj @@ -117,6 +117,7 @@ input %(Filename).moc +
diff --git a/UnitTest/UnitTest.vcxproj.filters b/UnitTest/UnitTest.vcxproj.filters index 2a74cbc..2265b74 100644 --- a/UnitTest/UnitTest.vcxproj.filters +++ b/UnitTest/UnitTest.vcxproj.filters @@ -48,5 +48,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/data.json b/data.json index 150cb6c..93398a4 100644 --- a/data.json +++ b/data.json @@ -1,4 +1,6 @@ { + "project-name": "样例1", + "background-color": "#ffffff", "height": 1080, "width": 1080, "elements": [ @@ -6,7 +8,7 @@ "name": "ababa", "type": "svg-file", "data": { - "include": "./svg/2.svg" + "include": "../svg/2.svg" } }, { @@ -15,6 +17,13 @@ "data": { "reference-layer": "0.0" } + }, + { + "name": "ababa2", + "type": "svg-file", + "data": { + "include": "../svg/0.svg" + } } ], "root-layer": { diff --git a/svg/4_L0.svg b/svg/4_L0.svg new file mode 100644 index 0000000..58facaf --- /dev/null +++ b/svg/4_L0.svg @@ -0,0 +1,5 @@ + + 4_L0 + + + diff --git a/svg/ex.json b/svg/ex.json new file mode 100644 index 0000000..9b3f948 --- /dev/null +++ b/svg/ex.json @@ -0,0 +1,31 @@ +{ + "height": 1080, + "width": 1080, + "elements": [ + { + "name": "ababa", + "type": "svg-file", + "data": { + "include": "./svg/0.svg" + } + } + ], + "root-layer": { + "name": "root", + "transform": { + "offset": { + "x": 0, + "y": 0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "rotation": 0.0 + }, + "effects": [], + "is-folder": true, + "referenced-by": null, + "children":[] + } +} \ No newline at end of file